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.
Files changed (103) hide show
  1. package/dist/{canonical-BbnqavJm.mjs → canonical-BXUguqfo.mjs} +23 -23
  2. package/dist/codegen.d.mts +25 -33
  3. package/dist/codegen.mjs +38 -45
  4. package/dist/column-hqKr7-1I.mjs +620 -0
  5. package/dist/{complete-D5Djh-zo.mjs → complete-WYyVozgK.mjs} +50 -50
  6. package/dist/{complete-types-BavBtv8J.d.mts → complete-types-IjEn5VPN.d.mts} +56 -56
  7. package/dist/core.d.mts +2 -2
  8. package/dist/core.mjs +4 -4
  9. package/dist/ddl.d.mts +12 -12
  10. package/dist/ddl.mjs +13 -14
  11. package/dist/{dialect-b2-Z6uBF.mjs → dialect-wUKrnPMB.mjs} +2 -2
  12. package/dist/diff.d.mts +1 -1
  13. package/dist/diff.mjs +6 -7
  14. package/dist/{index-DH0qV6aS.d.mts → index-1DpA3mUh.d.mts} +20 -20
  15. package/dist/{index-D3ZOwPT-.d.mts → index-CPvfEheG.d.mts} +13 -15
  16. package/dist/index.d.mts +1 -1
  17. package/dist/index.mjs +94 -95
  18. package/dist/introspection.d.mts +8 -8
  19. package/dist/introspection.mjs +29 -30
  20. package/dist/{json-CUZlv4HT.mjs → json-Db7XRD91.mjs} +2 -2
  21. package/dist/migration.d.mts +20 -21
  22. package/dist/migration.mjs +5 -6
  23. package/dist/{mysql-CKEKGnj4.mjs → mysql-B_cYzzX2.mjs} +237 -7
  24. package/dist/mysql.d.mts +2 -2
  25. package/dist/mysql.mjs +3 -3
  26. package/dist/{on-conflict-4MOBl51J.mjs → on-conflict-hfPW0KmQ.mjs} +3 -4
  27. package/dist/postgres.d.mts +5 -5
  28. package/dist/postgres.mjs +6 -6
  29. package/dist/{registry-CXV8u7Pt.mjs → registry-BRMLYwDp.mjs} +26 -29
  30. package/dist/{relational-DuQ9IHSb.mjs → relational-BZ3WDPzC.mjs} +2 -2
  31. package/dist/schema.d.mts +1 -1
  32. package/dist/schema.mjs +5 -6
  33. package/dist/{snapshot-DJpfmxhQ.mjs → snapshot-C-W65HEd.mjs} +5 -5
  34. package/dist/snapshot.d.mts +3 -3
  35. package/dist/snapshot.mjs +5 -7
  36. package/dist/{source-DGO3DRgg.mjs → source-BcS2AsIg.mjs} +6 -8
  37. package/dist/{serialize-CFtYYAdk.mjs → sqlite-Cg0nwYEH.mjs} +326 -12
  38. package/dist/sqlite.d.mts +1 -1
  39. package/dist/sqlite.mjs +2 -2
  40. package/dist/{standard-BTVYKh_F.mjs → standard-DfcZEVOj.mjs} +1 -1
  41. package/dist/{table-D6rcs8SB.mjs → table-Bp5irMSj.mjs} +2 -3
  42. package/dist/{types-Deo_q43Y.d.mts → types-BK1COGZe.d.mts} +1113 -1128
  43. package/dist/{types-g-vVvj1B.mjs → types-JM3FcAnX.mjs} +7 -8
  44. package/dist/{types-CSNJTYaM.d.mts → types-JSZHpUEj.d.mts} +83 -84
  45. package/dist/{value-b6OFZXVS.mjs → value-Bi71Agyf.mjs} +1 -1
  46. package/dist/vite/ambient.d.ts +309 -340
  47. package/dist/vite.d.mts +5 -6
  48. package/dist/vite.mjs +3 -4
  49. package/docs/dialects-and-execution.md +45 -55
  50. package/docs/getting-started.md +4 -4
  51. package/docs/guides/better-auth.md +57 -0
  52. package/docs/guides/compose-queries.md +24 -67
  53. package/docs/guides/drizzle.md +23 -23
  54. package/docs/guides/extensions/dialects.md +4 -4
  55. package/docs/guides/extensions/sources-and-clauses.md +15 -15
  56. package/docs/guides/extensions/typed-expressions.md +13 -17
  57. package/docs/guides/extensions/unsafe-syntax.md +3 -3
  58. package/docs/guides/json.md +7 -7
  59. package/docs/guides/mutations.md +14 -21
  60. package/docs/guides/select/conditions.md +6 -8
  61. package/docs/guides/select/grouping-and-windows.md +4 -15
  62. package/docs/guides/select/ordering-and-pagination.md +6 -19
  63. package/docs/guides/select/overview.md +12 -26
  64. package/docs/guides/sql-templates.md +20 -24
  65. package/docs/guides/vite-plugin.md +9 -13
  66. package/docs/index.md +5 -7
  67. package/docs/query-model/fragments.md +4 -15
  68. package/docs/query-model/result-shapes.md +11 -14
  69. package/docs/query-model/source-scope.md +31 -49
  70. package/docs/reference/mysql-snapshot.md +2 -5
  71. package/docs/reference/postgres-snapshot.md +3 -3
  72. package/docs/reference/sqlite-snapshot.md +2 -5
  73. package/docs/reference/supported-surface.md +26 -26
  74. package/docs/schema/catalog-model.md +2 -5
  75. package/docs/schema/code-generation.md +18 -18
  76. package/docs/schema/columns-and-writes.md +17 -25
  77. package/docs/schema/constraints-and-indexes.md +36 -52
  78. package/docs/schema/ddl-emission.md +6 -6
  79. package/docs/schema/diff.md +5 -5
  80. package/docs/schema/introspection.md +7 -7
  81. package/docs/schema/migration-plans.md +9 -9
  82. package/docs/schema/snapshots.md +3 -3
  83. package/docs/schema/storage-and-schema-sql.md +11 -11
  84. package/docs/schema/tables-and-names.md +11 -14
  85. package/docs/sql-semantic-types.md +10 -10
  86. package/docs/troubleshooting.md +10 -10
  87. package/package.json +21 -33
  88. package/skills/qubu/agents/openai.yaml +3 -3
  89. package/dist/column-D-8OqGuV.mjs +0 -328
  90. package/dist/column-LV7oGQde.mjs +0 -296
  91. package/dist/drizzle-mysql.d.mts +0 -24
  92. package/dist/drizzle-mysql.mjs +0 -72
  93. package/dist/drizzle-postgres.d.mts +0 -24
  94. package/dist/drizzle-postgres.mjs +0 -73
  95. package/dist/drizzle-sqlite.d.mts +0 -57
  96. package/dist/drizzle-sqlite.mjs +0 -108
  97. package/dist/drizzle.d.mts +0 -13
  98. package/dist/drizzle.mjs +0 -2
  99. package/dist/errors-BGCoLe_r.mjs +0 -14
  100. package/dist/postgres-BK2APkKP.mjs +0 -236
  101. package/dist/runtime-Cn_Xgzta.mjs +0 -193
  102. package/dist/sqlite-CrsK0Fza.mjs +0 -320
  103. package/dist/types-DZsueVoI.d.mts +0 -45
@@ -8,18 +8,18 @@ Qubu extensions are ordinary fragments. A custom clause supplies a placement,
8
8
  render order, and renderer:
9
9
 
10
10
  ```ts
11
- import { from, render, select, table, text } from 'qubu'
12
- import { customClause } from 'qubu/core'
11
+ import { from, render, select, table, text } from "qubu"
12
+ import { customClause } from "qubu/core"
13
13
 
14
- const users = table('users', { name: text() })
14
+ const users = table("users", { name: text() })
15
15
 
16
16
  const fetchWithTies = customClause({
17
- name: 'fetch-with-ties',
17
+ name: "fetch-with-ties",
18
18
  order: 100,
19
19
  render(context) {
20
- context.append('FETCH FIRST ')
20
+ context.append("FETCH FIRST ")
21
21
  context.parameter(10)
22
- context.append(' ROWS WITH TIES')
22
+ context.append(" ROWS WITH TIES")
23
23
  },
24
24
  })
25
25
 
@@ -41,23 +41,23 @@ shape is known to the application but cannot be declared with `table()`. Its
41
41
  table, while `from()` or a join supplies the source to the query scope:
42
42
 
43
43
  ```ts
44
- import { from, integer, render, select, text } from 'qubu'
45
- import { identifier } from 'qubu/core'
46
- import { customSource } from 'qubu/schema'
44
+ import { from, integer, render, select, text } from "qubu"
45
+ import { identifier } from "qubu/core"
46
+ import { customSource } from "qubu/schema"
47
47
 
48
48
  const rows = customSource({
49
- identity: { sourceKind: 'table-function', name: 'json_each', alias: 'row' },
50
- sourceKind: 'table-function',
51
- reference: identifier('row'),
49
+ identity: { sourceKind: "table-function", name: "json_each", alias: "row" },
50
+ sourceKind: "table-function",
51
+ reference: identifier("row"),
52
52
  columns: {
53
53
  key: integer(),
54
54
  value: text({ nullable: true }),
55
55
  },
56
56
  render(context) {
57
- context.append('json_each(')
57
+ context.append("json_each(")
58
58
  context.parameter('{"a":1}')
59
- context.append(') AS ')
60
- context.render(identifier('row'))
59
+ context.append(") AS ")
60
+ context.render(identifier("row"))
61
61
  },
62
62
  })
63
63
 
@@ -29,22 +29,18 @@ actually supports. Compatibility groups allow a dialect-specific domain to
29
29
  interoperate with a built-in family:
30
30
 
31
31
  ```ts
32
- import { cast, column } from 'qubu'
33
- import type {
34
- SqlEqualityComparable,
35
- SqlOrderable,
36
- SqlSemanticType,
37
- SqlTextLike,
38
- } from 'qubu'
32
+ import { cast, column } from "qubu"
33
+ import type { SqlEqualityComparable, SqlOrderable, SqlSemanticType, SqlTextLike } from "qubu"
39
34
 
40
35
  interface SqlCitext
41
- extends SqlSemanticType<'postgres.citext'>,
36
+ extends
37
+ SqlSemanticType<"postgres.citext">,
42
38
  SqlTextLike,
43
- SqlOrderable<'text'>,
44
- SqlEqualityComparable<'text'> {}
39
+ SqlOrderable<"text">,
40
+ SqlEqualityComparable<"text"> {}
45
41
 
46
42
  const citext = column<string, string, string, SqlCitext>({
47
- castType: 'CITEXT',
43
+ castType: "CITEXT",
48
44
  })
49
45
 
50
46
  const nameAsCitext = cast(users.name, citext)
@@ -61,13 +57,13 @@ nullability comes from the operand and write flags have no cast meaning.
61
57
  Declare result domains at other extension boundaries too:
62
58
 
63
59
  ```ts
64
- import { typedCall, typedCast, typedValue, unsafeExpression } from 'qubu/core'
65
- import type { SqlText, SqlUuid } from 'qubu'
60
+ import { typedCall, typedCast, typedValue, unsafeExpression } from "qubu/core"
61
+ import type { SqlText, SqlUuid } from "qubu"
66
62
 
67
- const id = typedValue<SqlUuid, string>('108cb836-20d2-41b2-8c23-f0c94700aa7e')
68
- const normalized = typedCall<SqlText, string>()('custom_text', users.name)
69
- const rawNameAsText = typedCast<string, SqlText>()(users.name, 'TEXT')
70
- const generated = unsafeExpression<string, SqlText>('custom_text()')
63
+ const id = typedValue<SqlUuid, string>("108cb836-20d2-41b2-8c23-f0c94700aa7e")
64
+ const normalized = typedCall<SqlText, string>()("custom_text", users.name)
65
+ const rawNameAsText = typedCast<string, SqlText>()(users.name, "TEXT")
66
+ const generated = unsafeExpression<string, SqlText>("custom_text()")
71
67
  ```
72
68
 
73
69
  `typedCall()` preserves source requirements from its arguments. `typedCast()`
@@ -6,11 +6,11 @@
6
6
  modeled yet. They do not quote identifiers or bind values for you:
7
7
 
8
8
  ```ts
9
- import { select } from 'qubu'
10
- import { unsafeExpression } from 'qubu/core'
9
+ import { select } from "qubu"
10
+ import { unsafeExpression } from "qubu/core"
11
11
 
12
12
  const query = select({
13
- today: unsafeExpression('CURRENT_DATE'),
13
+ today: unsafeExpression("CURRENT_DATE"),
14
14
  })
15
15
  ```
16
16
 
@@ -16,9 +16,9 @@ import {
16
16
  jsonText,
17
17
  select,
18
18
  table,
19
- } from 'qubu'
19
+ } from "qubu"
20
20
 
21
- const events = table('events', {
21
+ const events = table("events", {
22
22
  payload: json<{
23
23
  user?: { name?: string; active?: boolean; score?: number }
24
24
  }>(),
@@ -26,12 +26,12 @@ const events = table('events', {
26
26
 
27
27
  const query = select(
28
28
  {
29
- name: jsonText(events.payload, jsonPath('user', 'name')),
30
- active: jsonBoolean(events.payload, jsonPath('user', 'active')),
31
- score: jsonNumber(events.payload, jsonPath('user', 'score')),
32
- hasUser: jsonExists(events.payload, jsonPath('user')),
29
+ name: jsonText(events.payload, jsonPath("user", "name")),
30
+ active: jsonBoolean(events.payload, jsonPath("user", "active")),
31
+ score: jsonNumber(events.payload, jsonPath("user", "score")),
32
+ hasUser: jsonExists(events.payload, jsonPath("user")),
33
33
  },
34
- from(events)
34
+ from(events),
35
35
  )
36
36
  ```
37
37
 
@@ -9,9 +9,9 @@ columns accept `null` as a value, which is distinct from omitting a defaulted
9
9
  field:
10
10
 
11
11
  ```ts
12
- import { integer, table, text } from 'qubu'
12
+ import { integer, table, text } from "qubu"
13
13
 
14
- const users = table('users', {
14
+ const users = table("users", {
15
15
  id: integer({ generated: true }),
16
16
  name: text(),
17
17
  email: text({ nullable: true, hasDefault: true }),
@@ -27,15 +27,12 @@ Pass one or more rows to `values()`. Qubu checks that every row uses the same
27
27
  columns and that required, non-generated fields are present:
28
28
 
29
29
  ```ts
30
- import { insertInto, render, returning, values } from 'qubu'
30
+ import { insertInto, render, returning, values } from "qubu"
31
31
 
32
32
  const query = insertInto(
33
33
  users,
34
- values(
35
- { name: 'Ada', email: null },
36
- { name: 'Grace', email: 'grace@example.com' }
37
- ),
38
- returning({ id: users.id, name: users.name })
34
+ values({ name: "Ada", email: null }, { name: "Grace", email: "grace@example.com" }),
35
+ returning({ id: users.id, name: users.name }),
39
36
  )
40
37
 
41
38
  render(query)
@@ -54,13 +51,13 @@ default. Use `insertSelect(query, columns)` for an `INSERT ... SELECT` source.
54
51
  the target table:
55
52
 
56
53
  ```ts
57
- import { eq, returning, update, upper, where } from 'qubu'
54
+ import { eq, returning, update, upper, where } from "qubu"
58
55
 
59
56
  const query = update(
60
57
  users,
61
58
  { name: upper(users.name) },
62
59
  where(eq(users.id, 7)),
63
- returning({ id: users.id, name: users.name })
60
+ returning({ id: users.id, name: users.name }),
64
61
  )
65
62
  ```
66
63
 
@@ -71,15 +68,15 @@ Use `omit` for a runtime-conditional assignment. Qubu removes omitted fields
71
68
  before validating and rendering the effective assignment set:
72
69
 
73
70
  ```ts
74
- import { eq, omit, update, where } from 'qubu'
71
+ import { eq, omit, update, where } from "qubu"
75
72
 
76
73
  const query = update(
77
74
  users,
78
75
  {
79
- name: rename ? 'Archived' : omit,
76
+ name: rename ? "Archived" : omit,
80
77
  email: clearEmail ? null : omit,
81
78
  },
82
- where(eq(users.id, 7))
79
+ where(eq(users.id, 7)),
83
80
  )
84
81
  ```
85
82
 
@@ -93,13 +90,9 @@ remain source- and capability-aware even when their runtime alternative is
93
90
  ## Delete with a predicate
94
91
 
95
92
  ```ts
96
- import { deleteFrom, eq, returning, where } from 'qubu'
93
+ import { deleteFrom, eq, returning, where } from "qubu"
97
94
 
98
- const query = deleteFrom(
99
- users,
100
- where(eq(users.id, 8)),
101
- returning({ id: users.id })
102
- )
95
+ const query = deleteFrom(users, where(eq(users.id, 8)), returning({ id: users.id }))
103
96
  ```
104
97
 
105
98
  ## Keep unrestricted writes explicit
@@ -108,9 +101,9 @@ Both `UPDATE` and `DELETE` require a `WHERE` clause by default. If an operation
108
101
  really must affect every row, opt in at the call site:
109
102
 
110
103
  ```ts
111
- import { allowAll, update } from 'qubu'
104
+ import { allowAll, update } from "qubu"
112
105
 
113
- const query = update(users, { name: 'Archived' }, allowAll())
106
+ const query = update(users, { name: "Archived" }, allowAll())
114
107
  ```
115
108
 
116
109
  `allowAll()` is a safety boundary, not a replacement for authorization or
@@ -11,14 +11,14 @@ Use `omit` as the other branch of a JavaScript conditional when a `WHERE`,
11
11
  `HAVING`, `ORDER BY`, or `DISTINCT` clause is optional:
12
12
 
13
13
  ```ts
14
- import { eq, from, omit, select, where } from 'qubu'
14
+ import { eq, from, omit, select, where } from "qubu"
15
15
 
16
16
  declare const userId: number | undefined
17
17
 
18
18
  const query = select(
19
19
  { id: users.id, name: users.name },
20
20
  from(users),
21
- userId === undefined ? omit : where(eq(users.id, userId))
21
+ userId === undefined ? omit : where(eq(users.id, userId)),
22
22
  )
23
23
  ```
24
24
 
@@ -29,14 +29,12 @@ Use the same token inside `and()`, `or()`, and `orderBy()` when individual
29
29
  predicates or ordering terms are conditional:
30
30
 
31
31
  ```ts
32
- import { and, desc, eq, omit, orderBy, where } from 'qubu'
32
+ import { and, desc, eq, omit, orderBy, where } from "qubu"
33
33
 
34
34
  declare const includeName: boolean
35
35
  declare const newestFirst: boolean
36
36
 
37
- const filter = where(
38
- and(eq(users.id, 7), includeName ? eq(users.name, 'Ada') : omit)
39
- )
37
+ const filter = where(and(eq(users.id, 7), includeName ? eq(users.name, "Ada") : omit))
40
38
  const ordering = orderBy(newestFirst ? desc(users.name) : omit)
41
39
  ```
42
40
 
@@ -56,7 +54,7 @@ const query = select(
56
54
  id: users.id,
57
55
  email: includeEmail ? users.email : omit,
58
56
  },
59
- from(users)
57
+ from(users),
60
58
  )
61
59
 
62
60
  // typeof query.row:
@@ -82,7 +80,7 @@ separate queries when those structural parts differ at runtime.
82
80
  Equality with `null` is translated to the SQL null predicate:
83
81
 
84
82
  ```ts
85
- import { eq, isDistinctFrom, ne } from 'qubu'
83
+ import { eq, isDistinctFrom, ne } from "qubu"
86
84
 
87
85
  eq(users.name, null) // ... IS NULL
88
86
  ne(users.name, null) // ... IS NOT NULL
@@ -11,18 +11,7 @@ Object projection keys provide stable names for aggregates. Group every
11
11
  non-aggregate column dependency:
12
12
 
13
13
  ```ts
14
- import {
15
- count,
16
- desc,
17
- eq,
18
- from,
19
- groupBy,
20
- gt,
21
- having,
22
- leftJoin,
23
- orderBy,
24
- select,
25
- } from 'qubu'
14
+ import { count, desc, eq, from, groupBy, gt, having, leftJoin, orderBy, select } from "qubu"
26
15
 
27
16
  const counts = select(
28
17
  {
@@ -33,7 +22,7 @@ const counts = select(
33
22
  leftJoin(posts, eq(users.id, posts.authorId)),
34
23
  groupBy(users.name),
35
24
  having(gt(count(posts.id), 0)),
36
- orderBy(desc(count(posts.id)))
25
+ orderBy(desc(count(posts.id))),
37
26
  )
38
27
  ```
39
28
 
@@ -52,7 +41,7 @@ The initial window scope supports `PARTITION BY` and `ORDER BY`; the same
52
41
  `asc()` and `desc()` terms used by a query-level `orderBy()` can be reused:
53
42
 
54
43
  ```ts
55
- import { desc, from, over, rowNumber, select } from 'qubu'
44
+ import { desc, from, over, rowNumber, select } from "qubu"
56
45
 
57
46
  const rankedUsers = select(
58
47
  {
@@ -62,7 +51,7 @@ const rankedUsers = select(
62
51
  orderBy: [desc(users.id)],
63
52
  }),
64
53
  },
65
- from(users)
54
+ from(users),
66
55
  )
67
56
  ```
68
57
 
@@ -9,13 +9,9 @@ The examples use the `users` table from [Build a `SELECT`](overview.md).
9
9
  Wrap ordering terms in `orderBy()`:
10
10
 
11
11
  ```ts
12
- import { desc, from, orderBy, select } from 'qubu'
12
+ import { desc, from, orderBy, select } from "qubu"
13
13
 
14
- const ordered = select(
15
- { id: users.id, name: users.name },
16
- from(users),
17
- orderBy(desc(users.name))
18
- )
14
+ const ordered = select({ id: users.id, name: users.name }, from(users), orderBy(desc(users.name)))
19
15
  ```
20
16
 
21
17
  Use `asc()` or `desc()` for each term. The selected dialect controls the
@@ -26,16 +22,7 @@ identifier quoting and any dialect-specific ordering syntax.
26
22
  Choose `fetchFirst()` or `offset()` for pagination:
27
23
 
28
24
  ```ts
29
- import {
30
- desc,
31
- eq,
32
- fetchFirst,
33
- from,
34
- offset,
35
- orderBy,
36
- select,
37
- where,
38
- } from 'qubu'
25
+ import { desc, eq, fetchFirst, from, offset, orderBy, select, where } from "qubu"
39
26
 
40
27
  const page = select(
41
28
  { id: users.id, name: users.name },
@@ -43,7 +30,7 @@ const page = select(
43
30
  where(eq(users.id, 7)),
44
31
  orderBy(desc(users.name)),
45
32
  offset(20),
46
- fetchFirst(20)
33
+ fetchFirst(20),
47
34
  )
48
35
  ```
49
36
 
@@ -54,14 +41,14 @@ pagination. The active dialect decides whether pagination uses standard
54
41
  Pair a pagination clause with `omit` when the row bound is optional at runtime:
55
42
 
56
43
  ```ts
57
- import { fetchFirst, omit } from 'qubu'
44
+ import { fetchFirst, omit } from "qubu"
58
45
 
59
46
  declare const pageSize: number | undefined
60
47
 
61
48
  const page = select(
62
49
  { id: users.id, name: users.name },
63
50
  from(users),
64
- pageSize === undefined ? omit : fetchFirst(pageSize)
51
+ pageSize === undefined ? omit : fetchFirst(pageSize),
65
52
  )
66
53
  ```
67
54
 
@@ -9,19 +9,15 @@ independent clauses. A source-aware column must be provided by `from()` or a
9
9
  join:
10
10
 
11
11
  ```ts
12
- import { eq, from, integer, render, select, table, text, where } from 'qubu'
12
+ import { eq, from, integer, render, select, table, text, where } from "qubu"
13
13
 
14
- const users = table('users', {
14
+ const users = table("users", {
15
15
  id: integer(),
16
16
  name: text(),
17
17
  email: text({ nullable: true }),
18
18
  })
19
19
 
20
- const query = select(
21
- { id: users.id, name: users.name },
22
- from(users),
23
- where(eq(users.id, 7))
24
- )
20
+ const query = select({ id: users.id, name: users.name }, from(users), where(eq(users.id, 7)))
25
21
 
26
22
  render(query).text
27
23
  // SELECT "users"."id" AS "id", "users"."name" AS "name" FROM "users" WHERE ("users"."id" = ?)
@@ -34,12 +30,9 @@ named projection object, so it can still be spread alongside computed
34
30
  expressions:
35
31
 
36
32
  ```ts
37
- import { all, from, select, upper } from 'qubu'
33
+ import { all, from, select, upper } from "qubu"
38
34
 
39
- const query = select(
40
- { ...all(users), normalizedName: upper(users.name) },
41
- from(users)
42
- )
35
+ const query = select({ ...all(users), normalizedName: upper(users.name) }, from(users))
43
36
  ```
44
37
 
45
38
  `all(source)` expands to explicit named columns rather than emitting
@@ -66,9 +59,9 @@ import {
66
59
  text,
67
60
  integer,
68
61
  where,
69
- } from 'qubu'
62
+ } from "qubu"
70
63
 
71
- const posts = table('posts', {
64
+ const posts = table("posts", {
72
65
  id: integer(),
73
66
  authorId: integer(),
74
67
  title: text(),
@@ -78,7 +71,7 @@ const query = select(
78
71
  { userId: users.id, title: posts.title },
79
72
  from(users),
80
73
  innerJoin(posts, eq(users.id, posts.authorId)),
81
- where(isNotNull(users.email))
74
+ where(isNotNull(users.email)),
82
75
  )
83
76
  ```
84
77
 
@@ -98,7 +91,7 @@ const summary = select(
98
91
  postCount: count(posts.id),
99
92
  },
100
93
  from(users),
101
- leftJoin(posts, eq(users.id, posts.authorId))
94
+ leftJoin(posts, eq(users.id, posts.authorId)),
102
95
  )
103
96
 
104
97
  // typeof summary.row:
@@ -108,18 +101,11 @@ const summary = select(
108
101
  Compose boolean expressions explicitly:
109
102
 
110
103
  ```ts
111
- import { and, gt, inList, or } from 'qubu'
104
+ import { and, gt, inList, or } from "qubu"
112
105
 
113
- const filter = and(
114
- gt(users.id, 0),
115
- or(inList(users.id, [7, 8]), eq(users.name, 'Ada'))
116
- )
106
+ const filter = and(gt(users.id, 0), or(inList(users.id, [7, 8]), eq(users.name, "Ada")))
117
107
 
118
- const filtered = select(
119
- { id: users.id, name: users.name },
120
- from(users),
121
- where(filter)
122
- )
108
+ const filtered = select({ id: users.id, name: users.name }, from(users), where(filter))
123
109
  ```
124
110
 
125
111
  Values such as `7`, `'Ada'`, and list members become parameters. They are not
@@ -9,11 +9,11 @@ substitution as a parameter. This includes strings, numbers, objects, arrays,
9
9
  and `null`:
10
10
 
11
11
  ```ts
12
- import { integer, render, sql, table, text } from 'qubu'
12
+ import { integer, render, sql, table, text } from "qubu"
13
13
 
14
- const users = table('users', { name: text() })
15
- const posts = table('posts', { id: integer() })
16
- const search = 'Ada%'
14
+ const users = table("users", { name: text() })
15
+ const posts = table("posts", { id: integer() })
16
+ const search = "Ada%"
17
17
  const predicate = sql`${users.name} LIKE ${search}`
18
18
 
19
19
  render(predicate)
@@ -33,16 +33,12 @@ statement. Parameters keep one placeholder sequence across nested templates
33
33
  and queries:
34
34
 
35
35
  ```ts
36
- import { eq, from, render, select, sql, where } from 'qubu'
37
- import { postgresDialect } from 'qubu/postgres'
36
+ import { eq, from, render, select, sql, where } from "qubu"
37
+ import { postgresDialect } from "qubu/postgres"
38
38
 
39
- const selectedNames = select(
40
- { displayName: users.name },
41
- from(users),
42
- where(eq(users.name, 'Ada'))
43
- )
39
+ const selectedNames = select({ displayName: users.name }, from(users), where(eq(users.name, "Ada")))
44
40
 
45
- const exists = sql`EXISTS (${selectedNames}) AND ${users.name} <> ${'root'}`
41
+ const exists = sql`EXISTS (${selectedNames}) AND ${users.name} <> ${"root"}`
46
42
 
47
43
  render(exists, postgresDialect())
48
44
  // {
@@ -62,8 +58,8 @@ An unannotated template has application output `unknown` and SQL domain
62
58
  named projection:
63
59
 
64
60
  ```ts
65
- import { from, select, sql } from 'qubu'
66
- import type { SqlText } from 'qubu'
61
+ import { from, select, sql } from "qubu"
62
+ import type { SqlText } from "qubu"
67
63
 
68
64
  const normalizedName = sql.type<string, SqlText>()`LOWER(${users.name})`
69
65
 
@@ -98,11 +94,11 @@ Do not use a dotted string as an identifier. Pass each part to
98
94
  syntax that cannot use a fixed template segment:
99
95
 
100
96
  ```ts
101
- import { sql } from 'qubu'
102
- import { identifier, unsafeExpression } from 'qubu/core'
97
+ import { sql } from "qubu"
98
+ import { identifier, unsafeExpression } from "qubu/core"
103
99
 
104
- const sortColumn = 'display_name'
105
- const direction = 'DESC' as const
100
+ const sortColumn = "display_name"
101
+ const direction = "DESC" as const
106
102
 
107
103
  const ordering = sql`ORDER BY ${identifier(sortColumn)} ${unsafeExpression(direction)}`
108
104
  ```
@@ -120,8 +116,8 @@ those facts from unchecked template text.
120
116
  Use a built-in expression as the substitution when its semantics matter:
121
117
 
122
118
  ```ts
123
- import { count, sql } from 'qubu'
124
- import type { SqlInteger } from 'qubu'
119
+ import { count, sql } from "qubu"
120
+ import type { SqlInteger } from "qubu"
125
121
 
126
122
  const postCount = sql.type<number, SqlInteger>()`${count(posts.id)}`
127
123
  ```
@@ -140,13 +136,13 @@ Declare a capability when the template text itself uses dialect-specific
140
136
  syntax:
141
137
 
142
138
  ```ts
143
- import { sql } from 'qubu'
144
- import { withDialectCapability } from 'qubu/core'
145
- import type { SqlBoolean } from 'qubu'
139
+ import { sql } from "qubu"
140
+ import { withDialectCapability } from "qubu/core"
141
+ import type { SqlBoolean } from "qubu"
146
142
 
147
143
  const postgresMatch = withDialectCapability(
148
144
  sql.type<boolean, SqlBoolean>()`${users.name} ILIKE ${search}`,
149
- 'ilike'
145
+ "ilike",
150
146
  )
151
147
  ```
152
148
 
@@ -11,8 +11,8 @@ Add the plugin to Vite and add the matching ambient declarations to TypeScript:
11
11
 
12
12
  ```ts
13
13
  // vite.config.ts
14
- import { defineConfig } from 'vite'
15
- import { qubu } from 'qubu/vite'
14
+ import { defineConfig } from "vite"
15
+ import { qubu } from "qubu/vite"
16
16
 
17
17
  export default defineConfig({
18
18
  plugins: [qubu()],
@@ -36,24 +36,20 @@ ambient value and type declarations for the TypeScript compiler.
36
36
  Put the directive in the module's initial directive prologue:
37
37
 
38
38
  ```ts
39
- 'use qubu'
39
+ "use qubu"
40
40
 
41
- const users = table('users', {
41
+ const users = table("users", {
42
42
  id: integer(),
43
43
  name: text(),
44
44
  })
45
45
 
46
- const query = select(
47
- { id: users.id, name: users.name },
48
- from(users),
49
- where(eq(users.id, 42))
50
- )
46
+ const query = select({ id: users.id, name: users.name }, from(users), where(eq(users.id, 42)))
51
47
  ```
52
48
 
53
49
  Conceptually, the transform adds the imports that this module references:
54
50
 
55
51
  ```ts
56
- import { eq, from, integer, select, table, text, where } from 'qubu'
52
+ import { eq, from, integer, select, table, text, where } from "qubu"
57
53
  ```
58
54
 
59
55
  Existing imports remain valid. The transform does not rewrite member properties,
@@ -63,10 +59,10 @@ strings, comments, or names outside the public Qubu global catalog.
63
59
 
64
60
  ```ts
65
61
  qubu({
66
- module: 'qubu',
67
- include: id => id.includes('/src/'),
62
+ module: "qubu",
63
+ include: (id) => id.includes("/src/"),
68
64
  exclude: /\.stories\./,
69
- globals: ['select', 'from', 'where', 'eq', 'table'],
65
+ globals: ["select", "from", "where", "eq", "table"],
70
66
  })
71
67
  ```
72
68
 
package/docs/index.md CHANGED
@@ -29,6 +29,8 @@ define a table, build a `SELECT`, and inspect its SQL and parameters.
29
29
  `DELETE` statements.
30
30
  - [Use Qubu tables with Drizzle](guides/drizzle.md) while moving query call
31
31
  sites without duplicating schema declarations.
32
+ - [Use Qubu with Better Auth](guides/better-auth.md) with plugin-aware schema
33
+ derivation and a native transactional database adapter.
32
34
  - [Extend Qubu](guides/extensions/overview.md) with a custom source, clause,
33
35
  dialect policy, or typed expression.
34
36
  - [Read JSON scalars](guides/json.md) from structured JSON paths.
@@ -82,18 +84,14 @@ needs to preserve a fact across composition:
82
84
  ## A small example
83
85
 
84
86
  ```ts
85
- import { eq, from, integer, render, select, table, text, where } from 'qubu'
87
+ import { eq, from, integer, render, select, table, text, where } from "qubu"
86
88
 
87
- const users = table('users', {
89
+ const users = table("users", {
88
90
  id: integer(),
89
91
  name: text(),
90
92
  })
91
93
 
92
- const query = select(
93
- { id: users.id, name: users.name },
94
- from(users),
95
- where(eq(users.id, 7))
96
- )
94
+ const query = select({ id: users.id, name: users.name }, from(users), where(eq(users.id, 7)))
97
95
 
98
96
  render(query)
99
97
  // {