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
@@ -1,8 +1,121 @@
1
- # Read JSON scalars
1
+ # Query nested JSON
2
2
 
3
- > Extract a string, number, or boolean from a JSON column without writing a raw SQL path.
3
+ > Build inferred nested results, or read scalar values from stored JSON documents.
4
4
 
5
- Use a structured jsonPath() when a query needs a scalar or an existence check
5
+ ## Nest query results
6
+
7
+ Use `jsonArrayFrom()` to nest a query's rows and `jsonObjectFrom()` for a query
8
+ proven to return at most one row. Both preserve filtering, correlation,
9
+ ordering, and pagination:
10
+
11
+ ```ts
12
+ import {
13
+ correlate,
14
+ desc,
15
+ eq,
16
+ fetchFirst,
17
+ from,
18
+ integer,
19
+ jsonArrayFrom,
20
+ jsonObjectFrom,
21
+ orderBy,
22
+ select,
23
+ table,
24
+ text,
25
+ where,
26
+ } from "qubu"
27
+
28
+ const users = table("users", { id: integer(), name: text() })
29
+ const posts = table("posts", {
30
+ id: integer(),
31
+ authorId: integer(),
32
+ title: text(),
33
+ })
34
+ const latestPosts = select(
35
+ { id: posts.id, title: posts.title },
36
+ from(posts),
37
+ correlate(users),
38
+ where(eq(posts.authorId, users.id)),
39
+ orderBy(desc(posts.id)),
40
+ fetchFirst(3),
41
+ )
42
+ const latestPost = select(
43
+ { title: posts.title },
44
+ from(posts),
45
+ correlate(users),
46
+ where(eq(posts.authorId, users.id)),
47
+ orderBy(desc(posts.id)),
48
+ fetchFirst(1),
49
+ )
50
+ const query = select(
51
+ {
52
+ name: users.name,
53
+ posts: jsonArrayFrom(latestPosts),
54
+ latestPost: jsonObjectFrom(latestPost),
55
+ },
56
+ from(users),
57
+ )
58
+ // Row: { name: string; posts: { id: number; title: string }[];
59
+ // latestPost: { title: string } | null }
60
+ ```
61
+
62
+ ### Empty results and row limits
63
+
64
+ Execute the query through a Qubu adapter to decode nested results:
65
+
66
+ - An empty array query returns `[]`.
67
+ - An empty object query returns `null`.
68
+ - A source-free query proven to return exactly one row produces a non-null object.
69
+
70
+ An object query must be known to return at most one row. An unconditional
71
+ `fetchFirst(1)` or `fetchFirst(0)` proves that limit; a conditional limit does not.
72
+
73
+ The helpers compose inside further `select()` projections, so nesting can
74
+ continue without result-type assertions. `correlate()` and the outer query's
75
+ `FROM/JOIN` scope remain checked at every level.
76
+
77
+ ### Preserve ordering
78
+
79
+ Nested arrays retain explicit `ORDER BY` and pagination. Tied sort keys retain
80
+ SQL's unspecified tie order; add a unique tie-breaker when order matters.
81
+ `DISTINCT` ordering must use the same expressions as the selection. Without an
82
+ `ORDER BY`, array order is unspecified.
83
+
84
+ ### Supported databases
85
+
86
+ Nested results support PostgreSQL, MySQL 8.0.21+, and SQLite 3.45+. Other
87
+ dialects fail during rendering. SQLite's minimum includes the JSON aggregate
88
+ ordering fix needed to retain object values.
89
+
90
+ ### Decode nested values
91
+
92
+ Built-in column domains decode to their declared types, including bigint,
93
+ `Uint8Array`, `Date`, boolean, and nested JSON. Qubu transports precision-sensitive
94
+ values as text and rejects numbers that lose significant decimal digits or
95
+ exceed JavaScript's safe integer range. Use bigint columns for exact large
96
+ integers.
97
+
98
+ Unknown or custom SQL domains need a supported explicit cast, for
99
+ example `cast(value(7), integer())`; declaring a TypeScript result alone does
100
+ not provide runtime decoding information.
101
+
102
+ Custom `mapResult()` and column decoders receive the JSON transport value as
103
+ `unknown`. The value may be:
104
+
105
+ - A bigint or decimal string.
106
+ - A hexadecimal binary string.
107
+ - A serialized JSON string.
108
+ - An ordinary JSON scalar.
109
+
110
+ The custom decoder converts it to the declared application type. Adapter-wide
111
+ decoders do not run inside nested objects.
112
+
113
+ Keep arbitrary stored JSON within JavaScript’s numeric precision;
114
+ unsupported numeric representations fail instead of silently rounding.
115
+
116
+ ## Read stored JSON scalars
117
+
118
+ Use a structured `jsonPath()` when a query needs a scalar or an existence check
6
119
  inside a JSON document:
7
120
 
8
121
  ```ts
@@ -42,7 +155,7 @@ interpolating caller-provided SQL.
42
155
  ## Understand missing values
43
156
 
44
157
  Scalar reads return SQL NULL when the path is missing, contains JSON null, or
45
- resolves to another JSON scalar type. jsonExists() returns true for a present
158
+ resolves to another JSON scalar type. `jsonExists()` returns true for a present
46
159
  JSON null, false for a missing path, and false when the document itself is SQL
47
160
  NULL.
48
161
 
@@ -50,16 +163,21 @@ These rules keep path existence separate from extraction nullability.
50
163
 
51
164
  ## Check dialect support
52
165
 
53
- The standard dialect emits SQL/JSON JSON_VALUE and JSON_EXISTS syntax.
166
+ The standard dialect emits SQL/JSON `JSON_VALUE` and `JSON_EXISTS` syntax.
54
167
  PostgreSQL, MySQL, and SQLite use their native JSON policies. The current
55
168
  policies require PostgreSQL 12 or newer, MySQL 8.0.21 or newer, and SQLite JSON
56
169
  functions. An application-created dialect must provide a JSON renderer.
57
170
 
58
171
  ## Know the current limits
59
172
 
60
- JSON paths cover deterministic key and index traversal. Wildcards, filters,
61
- recursive descent, JSON-returning extraction, document mutation, and row
62
- expansion remain dialect-specific extensions.
173
+ JSON paths follow explicit keys and indexes. These features require
174
+ dialect-specific extensions:
175
+
176
+ - Wildcards and filters.
177
+ - Recursive descent.
178
+ - Extraction that returns JSON.
179
+ - Document mutation.
180
+ - Row expansion.
63
181
 
64
182
  For the SQL domain and nullability rules behind JSON columns, read
65
183
  [SQL semantic types](../sql-semantic-types.md).
@@ -1,6 +1,6 @@
1
1
  # Write mutations
2
2
 
3
- > Build typed `INSERT`, `UPDATE`, and `DELETE` statements from the same table metadata while keeping destructive operations explicit.
3
+ > Insert, update, and delete rows with typed inputs and explicit safeguards.
4
4
 
5
5
  ## Define write-time rules
6
6
 
@@ -121,6 +121,8 @@ const query = update(
121
121
  The assignment expression is source-aware, so a column from an unrelated table
122
122
  cannot silently enter the update.
123
123
 
124
+ ### Update from another source
125
+
124
126
  PostgreSQL updates can introduce one or more typed sources with `updateFrom()`.
125
127
  Those sources are available to assignments, the predicate, and `RETURNING`:
126
128
 
@@ -153,6 +155,8 @@ with the default, SQLite, or MySQL dialect is rejected. Qubu still requires a
153
155
  predicate or an explicit `allowAll()` marker; introducing a source does not
154
156
  authorize an unrestricted update.
155
157
 
158
+ ### Omit an assignment at runtime
159
+
156
160
  Use `omit` for a runtime-conditional assignment. Qubu removes omitted fields
157
161
  before validating and rendering the effective assignment set:
158
162
 
@@ -171,11 +175,49 @@ const query = update(
171
175
 
172
176
  `omit` means that the column is absent from `SET`. It is distinct from `null`
173
177
  and explicit `undefined`, which remain bound assignment values, and it does not
174
- emit SQL `DEFAULT`. At least one assignment must remain; `update()` throws
178
+ emit SQL `DEFAULT`.
179
+
180
+ At least one assignment must remain; `update()` throws
175
181
  before rendering when every field is omitted. Possible expression branches
176
182
  remain source- and capability-aware even when their runtime alternative is
177
183
  `omit`.
178
184
 
185
+ ## Update duplicate keys in MySQL
186
+
187
+ Use `onDuplicateKeyUpdate()` from `qubu/mysql` to update a row when an insert
188
+ conflicts with any primary or unique key. MySQL chooses the conflicting key;
189
+ this clause has no conflict-target argument or `RETURNING` support.
190
+
191
+ ```ts
192
+ import { insertInto, values } from "qubu"
193
+ import { incoming, onDuplicateKeyUpdate } from "qubu/mysql"
194
+
195
+ const proposed = incoming(users)
196
+ const query = insertInto(
197
+ users,
198
+ values({ name: "Ada", email: "ada@example.com" }),
199
+ onDuplicateKeyUpdate(users, { name: proposed.name }),
200
+ )
201
+ await db.execute(query)
202
+ ```
203
+
204
+ Declare a unique key on `email` in the database for this example. A duplicate
205
+ email replaces the existing name with the proposed name. Assignments accept
206
+ writable target columns, target-table expressions, `incoming(table)` columns,
207
+ or `omit`. Raw values retain the target column's parameter encoder. At least
208
+ one assignment must remain after omissions.
209
+
210
+ Incoming references are available only inside the matching table's duplicate-key
211
+ assignments. Qubu renders MySQL row aliases for `values()` and `defaultValues()`.
212
+ For `insertSelect()`, it wraps the query in a derived projection and maps its
213
+ fields positionally to the target-column list. Incoming references then cover
214
+ only that list; referencing an omitted target column fails during rendering.
215
+ This syntax requires MySQL 8.0.19 or later.
216
+
217
+ The result retains mysql2's `affectedRows`, `changedRows`, and `insertId` metadata
218
+ when supplied by the driver. These facts do not identify which branch ran for
219
+ every row of a batch. Read rows with a separate query when needed.
220
+
179
221
  ## Delete with a predicate
180
222
 
181
223
  ```ts
@@ -201,11 +243,14 @@ unrestricted operation is intended.
201
243
 
202
244
  ## Return typed rows
203
245
 
204
- `returning()` uses the same named object projection as `SELECT`. Reserve
205
- `{ ...all(table) }` for the intentional contract of returning every table
206
- column. When present, the mutation's `row` type is inferred from that projection, so
246
+ `returning()` uses the same named object projection as `SELECT`. Use
247
+ `{ ...all(table) }` when you want every table column.
248
+
249
+ The mutation’s `row` type is inferred from that projection, so
207
250
  `(await db.execute(query)).rows` has the same shape as a read query when `db`
208
- comes from `qubu(adapter)`. The projection's SQL semantic domains are
251
+ comes from `qubu(adapter)`.
252
+
253
+ The projection’s SQL semantic domains are
209
254
  retained too, so a returned query used by typed composition does not collapse
210
255
  UUID, text, numeric, or other known fields to their JavaScript types alone.
211
256
 
@@ -1,7 +1,6 @@
1
1
  # Add optional conditions
2
2
 
3
- > Keep optional predicates, null checks, and empty-list behavior explicit while
4
- > the query keeps the same structural shape.
3
+ > Add or omit conditions at runtime, and handle NULL values and empty lists.
5
4
 
6
5
  The examples use the `users` table from [Build a `SELECT`](overview.md).
7
6
 
@@ -65,15 +64,23 @@ Here `omit` affects only whether `email` belongs to the projection. It does
65
64
  not make the expression nullable: a non-nullable expression would produce
66
65
  `email?: string`, while this nullable column produces `email?: string | null`.
67
66
 
68
- This support is specific to boolean operand lists, query-level ordering terms,
69
- and the complete clauses named above. Generic `sequence()` and
70
- `commaSeparated()` collections do not discard `omit`. Clauses that provide
71
- sources or change structural guarantees cannot be conditional. Pagination is
72
- the exception: `offset()`, `fetchFirst()`, and `fetchNext()` can be paired
73
- with `omit`, but a conditional row bound keeps the query's inferred
74
- cardinality at `many`. Qubu still rejects `omit` branches paired with
75
- `from()`, joins, `groupBy()`, correlation, CTEs, or custom clauses. Build
76
- separate queries when those structural parts differ at runtime.
67
+ ### Where omission is supported
68
+
69
+ Use `omit` in the clauses and expression lists shown above. Generic
70
+ `sequence()` and `commaSeparated()` collections do not discard it.
71
+
72
+ Pagination also supports `omit`: pair it with `offset()`, `fetchFirst()`, or
73
+ `fetchNext()`. A conditional limit keeps the inferred cardinality at `many`
74
+ because the limit may be absent.
75
+
76
+ Build separate queries when any of these parts differ at runtime. They cannot
77
+ be paired with `omit`:
78
+
79
+ - `from()` or joins.
80
+ - `groupBy()`.
81
+ - Correlation.
82
+ - CTEs.
83
+ - Custom clauses.
77
84
 
78
85
  ## Handle `NULL` and empty lists deliberately
79
86
 
@@ -1,6 +1,6 @@
1
1
  # Group and rank rows
2
2
 
3
- > Use aggregates, grouping, and window expressions while preserving the dependencies and result types that make each expression valid.
3
+ > Summarize rows with aggregates and rank them with window functions.
4
4
 
5
5
  The examples use the `users` and `posts` tables from [Build a
6
6
  `SELECT`](overview.md).
@@ -28,6 +28,7 @@ const counts = select(
28
28
 
29
29
  `users.name` is grouped, while `posts.id` is consumed by `COUNT()`. The
30
30
  same dependency rule applies to `HAVING` and grouped `ORDER BY` expressions.
31
+
31
32
  A projection such as `{ email: users.email, postCount: count(posts.id) }` is
32
33
  rejected unless `users.email` is grouped or is functionally determined by a
33
34
  grouped primary or unique key declared in the table schema. Qubu uses only
@@ -58,7 +59,9 @@ const rankedUsers = select(
58
59
  Window expressions remain ordinary expressions. They can be projected, aliased,
59
60
  and passed to `orderBy()`. Their source requirements and result types are
60
61
  retained through `over()`, and values rendered inside the window
61
- specification remain parameters. Named windows and frame clauses are outside
62
+ specification remain parameters.
63
+
64
+ Named windows and frame clauses are outside
62
65
  the initial inline scope.
63
66
 
64
67
  ## Read next
@@ -1,6 +1,6 @@
1
1
  # Order and paginate
2
2
 
3
- > Choose a stable order and apply optional row limits while keeping SQL clause order and query cardinality visible.
3
+ > Sort results and limit the rows a query returns.
4
4
 
5
5
  The examples use the `users` table from [Build a `SELECT`](overview.md).
6
6
 
@@ -34,10 +34,12 @@ const page = select(
34
34
  )
35
35
  ```
36
36
 
37
- The rendered clause order is still `FROM`, `WHERE`, `ORDER BY`, and
38
- pagination. The active dialect decides whether pagination uses standard
37
+ The rendered clauses follow SQL order: `FROM` comes before `WHERE`, followed
38
+ by `ORDER BY` and pagination. The active dialect decides whether pagination uses standard
39
39
  `FETCH` syntax or a driver-specific `LIMIT` form.
40
40
 
41
+ ### Make the limit optional
42
+
41
43
  Pair a pagination clause with `omit` when the row bound is optional at runtime:
42
44
 
43
45
  ```ts
@@ -1,6 +1,6 @@
1
1
  # Build a `SELECT`
2
2
 
3
- > Build a `SELECT` from typed tables, then inspect its sources, projection, joins, and result row.
3
+ > Choose fields, add a source, and join tables to build a typed SELECT query.
4
4
 
5
5
  ## Start with a projection and a source
6
6
 
@@ -24,8 +24,7 @@ render(query).text
24
24
  ```
25
25
 
26
26
  An object projection uses its keys as result names. Name the fields you intend
27
- to return in the usual case. Reserve `all(source)` for a result contract that
28
- intentionally returns every source column. It returns the source's columns as a
27
+ to return in the usual case. Use `all(source)` when you want every source column. It returns the source's columns as a
29
28
  named projection object, so it can still be spread alongside computed
30
29
  expressions:
31
30
 
@@ -79,6 +78,8 @@ Use `innerJoin`, `leftJoin`, `rightJoin`, or `fullJoin` with an `ON`
79
78
  condition. `crossJoin` and `naturalJoin` add a source without a condition;
80
79
  use them only when that SQL behavior is intentional.
81
80
 
81
+ ### Handle missing joined rows
82
+
82
83
  `leftJoin()` also carries nullability into the selected row. A column from the
83
84
  joined source is nullable because the row may be missing, while an expression
84
85
  with a deliberately non-nullable result such as `count()` remains non-null:
@@ -98,6 +99,8 @@ const summary = select(
98
99
  // { userName: string; postTitle: string | null; postCount: number }
99
100
  ```
100
101
 
102
+ ### Add conditions
103
+
101
104
  Compose boolean expressions explicitly:
102
105
 
103
106
  ```ts
@@ -1,6 +1,6 @@
1
1
  # Compose SQL templates
2
2
 
3
- > Use trusted SQL syntax with bound runtime values while retaining the Qubu metadata carried by interpolated expressions, fragments, and queries.
3
+ > Write SQL templates that bind values safely and preserve type information from Qubu expressions.
4
4
 
5
5
  ## Bind every runtime value
6
6
 
@@ -108,10 +108,16 @@ reaches `unsafeExpression()`.
108
108
 
109
109
  ## Preserve metadata through interpolated fragments
110
110
 
111
- The tag inherits source dependencies, conservative outer-join nullability,
112
- grouping facts, aggregate and window state, subquery state, and dialect
113
- capability requirements from Qubu fragment substitutions. It does not infer
114
- those facts from unchecked template text.
111
+ Qubu fragment substitutions pass these facts to the template:
112
+
113
+ - Required sources.
114
+ - Possible nulls from outer joins.
115
+ - Grouping dependencies.
116
+ - Aggregate and window state.
117
+ - Subquery state.
118
+ - Required dialect capabilities.
119
+
120
+ Qubu does not infer these facts from template text.
115
121
 
116
122
  Use a built-in expression as the substitution when its semantics matter:
117
123
 
@@ -102,11 +102,17 @@ const handlers = applyOpsWithQubu<SyncContext>({
102
102
  export const sync = valtioSync({ schema: { todos }, handlers })
103
103
  ```
104
104
 
105
- Authorization, conflict checks, the application mutation, and
106
- `syncEvents.write()` run in that order inside one Qubu transaction. If any step
107
- fails, the adapter rolls the transaction back. The event sequence becomes
108
- `serverVersion` unless the mutation handler returns an explicit version. Read
109
- handlers pass through unchanged.
105
+ Each mutation runs these steps in one Qubu transaction:
106
+
107
+ 1. Check authorization.
108
+ 2. Check conflicts.
109
+ 3. Apply the application mutation.
110
+ 4. Write the sync event with `syncEvents.write()`.
111
+
112
+ If any step fails, the adapter rolls the transaction back.
113
+
114
+ The event sequence becomes `serverVersion` unless the mutation handler returns
115
+ an explicit version. Read handlers pass through unchanged.
110
116
 
111
117
  The integration does not define persistence tables, import Drizzle, or execute
112
118
  driver APIs. The application owns table design, authorization, conflict policy,
@@ -1,6 +1,6 @@
1
1
  # Vite compiler hint
2
2
 
3
- > Opt a JavaScript or TypeScript module into Qubu's ambient query API while keeping the transform limited to explicit directive-bearing files.
3
+ > Use a "use qubu" directive to add the Qubu imports a module needs.
4
4
 
5
5
  The optional Vite plugin recognizes the `"use qubu"` directive and injects only
6
6
  the referenced named imports from the configured module.
@@ -33,7 +33,7 @@ ambient value and type declarations for the TypeScript compiler.
33
33
 
34
34
  ## Mark a module explicitly
35
35
 
36
- Put the directive in the module's initial directive prologue:
36
+ Put the directive at the start of the module, alongside any other directives:
37
37
 
38
38
  ```ts
39
39
  "use qubu"
package/docs/index.md CHANGED
@@ -1,11 +1,17 @@
1
1
  # Qubu
2
2
 
3
- > Build parameterized SQL from typed tables, expressions, and clauses.
3
+ > Build SQL queries from typed tables and reusable values.
4
4
 
5
- Qubu builds SQL from values. Tables, expressions, clauses, and complete queries
6
- compose without a mutable query builder. TypeScript tracks selected row shapes,
7
- source scope, and nullability, while rendering returns SQL text and ordered
8
- parameters.
5
+ Qubu builds SQL from reusable values. You can combine query parts without
6
+ changing a shared query-builder object.
7
+
8
+ TypeScript checks:
9
+
10
+ - Which fields the query returns and their types.
11
+ - Whether each column belongs to a source in the query.
12
+ - Whether a result can be `null`.
13
+
14
+ Render a query to inspect its SQL text and ordered parameters.
9
15
 
10
16
  The preferred source style names each projected field and writes the final
11
17
  `select()` clauses in SQL order. Clause values remain order-independent at
@@ -17,7 +23,7 @@ and placed in that final call where it reads best.
17
23
  If this is your first query, follow [Getting started](getting-started.md) to
18
24
  define a table, build a `SELECT`, and inspect its SQL and parameters.
19
25
 
20
- ## Choose a task
26
+ ## Build and run queries
21
27
 
22
28
  - [Build a `SELECT`](guides/select/overview.md) with projections, joins,
23
29
  predicates, ordering, and grouping.
@@ -33,21 +39,21 @@ define a table, build a `SELECT`, and inspect its SQL and parameters.
33
39
  derivation and a native transactional database adapter.
34
40
  - [Extend Qubu](guides/extensions/overview.md) with a custom source, clause,
35
41
  dialect policy, or typed expression.
36
- - [Read JSON scalars](guides/json.md) from structured JSON paths.
42
+ - [Query nested JSON](guides/json.md) or read scalars from structured paths.
37
43
  - [Enable the Vite compiler hint](guides/vite-plugin.md) when query modules
38
44
  should opt into named imports through a directive.
39
- - [Inspect an existing database](schema/introspection.md) through the optional
40
- user-owned catalog boundary.
41
- - [Generate a schema module](schema/code-generation.md) from one complete,
42
- non-lossy Snapshot v1 introspection result.
45
+
46
+ ## Work with schemas and migrations
47
+
48
+ - [Inspect an existing database](schema/introspection.md) using a connection you provide.
49
+ - [Generate a schema module](schema/code-generation.md) from an introspection result without losing schema facts.
43
50
  - [Compare snapshots](schema/diff.md) with explicit rename hints and reviewable
44
51
  safety diagnostics.
45
52
  - [Build migration plans](schema/migration-plans.md) as reviewed, deterministic
46
53
  data before DDL emission.
47
54
  - [Emit DDL](schema/ddl-emission.md) from an approved migration plan without
48
55
  handing Qubu a database connection.
49
- - [Operate migrations](migrations/index.md) with versioned artifacts, verified
50
- adapter profiles, baselines, a portable executor, and explicit recovery.
56
+ - [Operate migrations](migrations/index.md) with reviewed migration files and a verified adapter.
51
57
 
52
58
  ## The query pipeline
53
59
 
@@ -106,8 +112,9 @@ render(query)
106
112
  The inferred row is `{ id: number; name: string }`. The value `7` stays out
107
113
  of the SQL text and appears in the `parameters` array in placeholder order.
108
114
 
109
- The [supported features](reference/supported-surface.md) page is the canonical
110
- package-entrypoint and ownership map. Applications may use Qubu's portable
111
- migration executor while retaining credentials, approval policy, custom SQL,
112
- and deployment lifecycle ownership. [Troubleshooting](troubleshooting.md)
113
- starts from common errors and points to the concept page behind each one.
115
+ ## Find support details
116
+
117
+ - [Supported features](reference/supported-surface.md) lists package imports and
118
+ explains which responsibilities stay with your application.
119
+ - [Troubleshooting](troubleshooting.md) starts from common errors and explains
120
+ how to fix them.