qubu 0.3.5 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{canonical-BbnqavJm.mjs → canonical-BXUguqfo.mjs} +23 -23
- package/dist/codegen.d.mts +25 -33
- package/dist/codegen.mjs +38 -45
- package/dist/column-hqKr7-1I.mjs +620 -0
- package/dist/{complete-D5Djh-zo.mjs → complete-WYyVozgK.mjs} +50 -50
- package/dist/{complete-types-B2PO6wQD.d.mts → complete-types-IjEn5VPN.d.mts} +56 -56
- package/dist/core.d.mts +2 -2
- package/dist/core.mjs +4 -4
- package/dist/ddl.d.mts +12 -12
- package/dist/ddl.mjs +13 -14
- package/dist/{dialect-b2-Z6uBF.mjs → dialect-wUKrnPMB.mjs} +2 -2
- package/dist/diff.d.mts +1 -1
- package/dist/diff.mjs +6 -7
- package/dist/{index-7qc6OcIC.d.mts → index-1DpA3mUh.d.mts} +20 -20
- package/dist/{index-CqWnouTK.d.mts → index-CPvfEheG.d.mts} +13 -15
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +148 -120
- package/dist/introspection.d.mts +8 -8
- package/dist/introspection.mjs +29 -30
- package/dist/{json-CUZlv4HT.mjs → json-Db7XRD91.mjs} +2 -2
- package/dist/migration.d.mts +20 -21
- package/dist/migration.mjs +5 -6
- package/dist/{mysql-DqkqXB6A.mjs → mysql-B_cYzzX2.mjs} +237 -7
- package/dist/mysql.d.mts +1 -1
- package/dist/mysql.mjs +3 -3
- package/dist/{on-conflict-BxnxubMb.mjs → on-conflict-hfPW0KmQ.mjs} +4 -4
- package/dist/postgres.d.mts +5 -5
- package/dist/postgres.mjs +6 -6
- package/dist/{registry-BufIskVN.mjs → registry-BRMLYwDp.mjs} +27 -135
- package/dist/{relational-DCZrrNia.mjs → relational-BZ3WDPzC.mjs} +4 -4
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +6 -6
- package/dist/{snapshot-CWPgzxNx.mjs → snapshot-C-W65HEd.mjs} +5 -5
- package/dist/snapshot.d.mts +3 -3
- package/dist/snapshot.mjs +5 -7
- package/dist/{source-DUoJVXmL.mjs → source-BcS2AsIg.mjs} +7 -9
- package/dist/{serialize-PF1cfH2P.mjs → sqlite-Cg0nwYEH.mjs} +326 -12
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +2 -2
- package/dist/{standard-BTVYKh_F.mjs → standard-DfcZEVOj.mjs} +1 -1
- package/dist/{table-CCUJ60rB.mjs → table-Bp5irMSj.mjs} +5 -7
- package/dist/{types-D8M1yZF4.d.mts → types-BK1COGZe.d.mts} +2661 -2150
- package/dist/{types-Cec0xzo4.mjs → types-JM3FcAnX.mjs} +8 -8
- package/dist/{types-LBt5rclR.d.mts → types-JSZHpUEj.d.mts} +83 -84
- package/dist/{value-BvilP0oz.mjs → value-Bi71Agyf.mjs} +1 -1
- package/dist/vite/ambient.d.ts +311 -390
- package/dist/vite.d.mts +5 -6
- package/dist/vite.mjs +3 -4
- package/docs/dialects-and-execution.md +121 -81
- package/docs/getting-started.md +4 -4
- package/docs/guides/better-auth.md +57 -0
- package/docs/guides/compose-queries.md +24 -67
- package/docs/guides/drizzle.md +23 -23
- package/docs/guides/extensions/dialects.md +4 -4
- package/docs/guides/extensions/sources-and-clauses.md +15 -15
- package/docs/guides/extensions/typed-expressions.md +13 -17
- package/docs/guides/extensions/unsafe-syntax.md +3 -3
- package/docs/guides/json.md +7 -7
- package/docs/guides/mutations.md +14 -21
- package/docs/guides/select/conditions.md +6 -8
- package/docs/guides/select/grouping-and-windows.md +4 -15
- package/docs/guides/select/ordering-and-pagination.md +6 -19
- package/docs/guides/select/overview.md +12 -26
- package/docs/guides/sql-templates.md +20 -24
- package/docs/guides/vite-plugin.md +9 -13
- package/docs/index.md +5 -7
- package/docs/query-model/fragments.md +4 -15
- package/docs/query-model/result-shapes.md +11 -14
- package/docs/query-model/source-scope.md +31 -49
- package/docs/reference/mysql-snapshot.md +2 -5
- package/docs/reference/postgres-snapshot.md +3 -3
- package/docs/reference/sqlite-snapshot.md +2 -5
- package/docs/reference/supported-surface.md +33 -33
- package/docs/schema/catalog-model.md +2 -5
- package/docs/schema/code-generation.md +18 -18
- package/docs/schema/columns-and-writes.md +17 -25
- package/docs/schema/constraints-and-indexes.md +36 -52
- package/docs/schema/ddl-emission.md +6 -6
- package/docs/schema/diff.md +5 -5
- package/docs/schema/introspection.md +7 -7
- package/docs/schema/migration-plans.md +9 -9
- package/docs/schema/snapshots.md +3 -3
- package/docs/schema/storage-and-schema-sql.md +11 -11
- package/docs/schema/tables-and-names.md +11 -14
- package/docs/sql-semantic-types.md +10 -10
- package/docs/troubleshooting.md +10 -10
- package/package.json +21 -33
- package/skills/qubu/agents/openai.yaml +3 -3
- package/dist/column-CXMxx8Hq.mjs +0 -118
- package/dist/column-CYMbKbOy.mjs +0 -290
- package/dist/drizzle-mysql.d.mts +0 -24
- package/dist/drizzle-mysql.mjs +0 -72
- package/dist/drizzle-postgres.d.mts +0 -24
- package/dist/drizzle-postgres.mjs +0 -73
- package/dist/drizzle-sqlite.d.mts +0 -50
- package/dist/drizzle-sqlite.mjs +0 -108
- package/dist/drizzle.d.mts +0 -13
- package/dist/drizzle.mjs +0 -2
- package/dist/errors-BGCoLe_r.mjs +0 -14
- package/dist/naming-QVCOnSj2.mjs +0 -20
- package/dist/postgres-DEBBeh52.mjs +0 -235
- package/dist/runtime-Cn_Xgzta.mjs +0 -193
- package/dist/sqlite-BU6DBxef.mjs +0 -320
- package/dist/types-Ctlxz1ip.d.mts +0 -45
|
@@ -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
|
|
12
|
+
import { integer, render, sql, table, text } from "qubu"
|
|
13
13
|
|
|
14
|
-
const users = table(
|
|
15
|
-
const posts = table(
|
|
16
|
-
const search =
|
|
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
|
|
37
|
-
import { postgresDialect } from
|
|
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} <> ${
|
|
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
|
|
66
|
-
import type { SqlText } from
|
|
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
|
|
102
|
-
import { identifier, unsafeExpression } from
|
|
97
|
+
import { sql } from "qubu"
|
|
98
|
+
import { identifier, unsafeExpression } from "qubu/core"
|
|
103
99
|
|
|
104
|
-
const sortColumn =
|
|
105
|
-
const direction =
|
|
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
|
|
124
|
-
import type { SqlInteger } from
|
|
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
|
|
144
|
-
import { withDialectCapability } from
|
|
145
|
-
import type { SqlBoolean } from
|
|
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
|
-
|
|
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
|
|
15
|
-
import { qubu } from
|
|
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
|
-
|
|
39
|
+
"use qubu"
|
|
40
40
|
|
|
41
|
-
const users = table(
|
|
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
|
|
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:
|
|
67
|
-
include: id => id.includes(
|
|
62
|
+
module: "qubu",
|
|
63
|
+
include: (id) => id.includes("/src/"),
|
|
68
64
|
exclude: /\.stories\./,
|
|
69
|
-
globals: [
|
|
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
|
|
87
|
+
import { eq, from, integer, render, select, table, text, where } from "qubu"
|
|
86
88
|
|
|
87
|
-
const users = table(
|
|
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
|
// {
|
|
@@ -54,20 +54,9 @@ Parameter values are not fragment metadata. A renderer calls
|
|
|
54
54
|
`context.parameter(value)`, and `render()` collects values in placeholder order:
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
from,
|
|
61
|
-
integer,
|
|
62
|
-
like,
|
|
63
|
-
render,
|
|
64
|
-
select,
|
|
65
|
-
table,
|
|
66
|
-
text,
|
|
67
|
-
where,
|
|
68
|
-
} from 'qubu'
|
|
69
|
-
|
|
70
|
-
const users = table('users', {
|
|
57
|
+
import { and, eq, from, integer, like, render, select, table, text, where } from "qubu"
|
|
58
|
+
|
|
59
|
+
const users = table("users", {
|
|
71
60
|
id: integer(),
|
|
72
61
|
name: text(),
|
|
73
62
|
})
|
|
@@ -75,7 +64,7 @@ const users = table('users', {
|
|
|
75
64
|
const query = select(
|
|
76
65
|
{ id: users.id },
|
|
77
66
|
from(users),
|
|
78
|
-
where(and(eq(users.id, 7), like(users.name,
|
|
67
|
+
where(and(eq(users.id, 7), like(users.name, "%Ada%"))),
|
|
79
68
|
)
|
|
80
69
|
|
|
81
70
|
render(query)
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
An object projection uses its keys as result names:
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import { from, integer, select, table, text, upper } from
|
|
10
|
+
import { from, integer, select, table, text, upper } from "qubu"
|
|
11
11
|
|
|
12
|
-
const users = table(
|
|
12
|
+
const users = table("users", {
|
|
13
13
|
id: integer(),
|
|
14
14
|
name: text(),
|
|
15
15
|
})
|
|
@@ -19,7 +19,7 @@ const query = select(
|
|
|
19
19
|
id: users.id,
|
|
20
20
|
displayName: upper(users.name),
|
|
21
21
|
},
|
|
22
|
-
from(users)
|
|
22
|
+
from(users),
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
type Row = typeof query.row
|
|
@@ -31,12 +31,9 @@ shaped result. Reserve `all(source)` for a whole-source result contract. It
|
|
|
31
31
|
expands to named columns, so the SQL columns and inferred row keys stay aligned:
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
|
-
import { all, from, select, upper } from
|
|
34
|
+
import { all, from, select, upper } from "qubu"
|
|
35
35
|
|
|
36
|
-
const query = select(
|
|
37
|
-
{ ...all(users), normalizedName: upper(users.name) },
|
|
38
|
-
from(users)
|
|
39
|
-
)
|
|
36
|
+
const query = select({ ...all(users), normalizedName: upper(users.name) }, from(users))
|
|
40
37
|
```
|
|
41
38
|
|
|
42
39
|
When a query becomes a CTE or derived table, its row shape becomes the columns
|
|
@@ -53,13 +50,13 @@ source widens with `null`, while an expression with its own non-null result
|
|
|
53
50
|
contract can stay non-null:
|
|
54
51
|
|
|
55
52
|
```ts
|
|
56
|
-
import { count, eq, from, integer, leftJoin, select, table, text } from
|
|
53
|
+
import { count, eq, from, integer, leftJoin, select, table, text } from "qubu"
|
|
57
54
|
|
|
58
|
-
const users = table(
|
|
55
|
+
const users = table("users", {
|
|
59
56
|
id: integer(),
|
|
60
57
|
name: text(),
|
|
61
58
|
})
|
|
62
|
-
const posts = table(
|
|
59
|
+
const posts = table("posts", {
|
|
63
60
|
id: integer(),
|
|
64
61
|
authorId: integer(),
|
|
65
62
|
title: text(),
|
|
@@ -72,7 +69,7 @@ const query = select(
|
|
|
72
69
|
postCount: count(posts.id),
|
|
73
70
|
},
|
|
74
71
|
from(users),
|
|
75
|
-
leftJoin(posts, eq(users.id, posts.authorId))
|
|
72
|
+
leftJoin(posts, eq(users.id, posts.authorId)),
|
|
76
73
|
)
|
|
77
74
|
|
|
78
75
|
type Row = typeof query.row
|
|
@@ -89,9 +86,9 @@ expression with non-null branches can return a non-null result.
|
|
|
89
86
|
result includes `null` when the query may return no rows:
|
|
90
87
|
|
|
91
88
|
```ts
|
|
92
|
-
import { fetchFirst, from, scalar, select, table, value } from
|
|
89
|
+
import { fetchFirst, from, scalar, select, table, value } from "qubu"
|
|
93
90
|
|
|
94
|
-
const users = table(
|
|
91
|
+
const users = table("users", { id: integer() })
|
|
95
92
|
const firstUser = select({ id: users.id }, from(users), fetchFirst(1))
|
|
96
93
|
|
|
97
94
|
const firstId = scalar(firstUser)
|
|
@@ -13,12 +13,12 @@ Qubu reports a missing source when a query selects a column from a table that
|
|
|
13
13
|
does not appear in the query:
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import { from, integer, select, table, text } from
|
|
16
|
+
import { from, integer, select, table, text } from "qubu"
|
|
17
17
|
|
|
18
|
-
const users = table(
|
|
18
|
+
const users = table("users", {
|
|
19
19
|
id: integer(),
|
|
20
20
|
})
|
|
21
|
-
const posts = table(
|
|
21
|
+
const posts = table("posts", {
|
|
22
22
|
id: integer(),
|
|
23
23
|
title: text(),
|
|
24
24
|
})
|
|
@@ -31,12 +31,12 @@ Add the source that owns the column, or join it with a condition that refers to
|
|
|
31
31
|
both sources:
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
|
-
import { eq, from, innerJoin, integer, select, table } from
|
|
34
|
+
import { eq, from, innerJoin, integer, select, table } from "qubu"
|
|
35
35
|
|
|
36
|
-
const users = table(
|
|
36
|
+
const users = table("users", {
|
|
37
37
|
id: integer(),
|
|
38
38
|
})
|
|
39
|
-
const posts = table(
|
|
39
|
+
const posts = table("posts", {
|
|
40
40
|
id: integer(),
|
|
41
41
|
authorId: integer(),
|
|
42
42
|
})
|
|
@@ -44,7 +44,7 @@ const posts = table('posts', {
|
|
|
44
44
|
const query = select(
|
|
45
45
|
{ userId: users.id, postId: posts.id },
|
|
46
46
|
from(users),
|
|
47
|
-
innerJoin(posts, eq(users.id, posts.authorId))
|
|
47
|
+
innerJoin(posts, eq(users.id, posts.authorId)),
|
|
48
48
|
)
|
|
49
49
|
```
|
|
50
50
|
|
|
@@ -54,14 +54,14 @@ Aliases, CTEs, derived queries, and custom sources expose new source identities.
|
|
|
54
54
|
Use their columns after wrapping the original source:
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import { alias, from, integer, select, table, text } from
|
|
57
|
+
import { alias, from, integer, select, table, text } from "qubu"
|
|
58
58
|
|
|
59
|
-
const users = table(
|
|
59
|
+
const users = table("users", {
|
|
60
60
|
id: integer(),
|
|
61
61
|
name: text(),
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
const author = alias(users,
|
|
64
|
+
const author = alias(users, "author")
|
|
65
65
|
const query = select({ name: author.name }, from(author))
|
|
66
66
|
```
|
|
67
67
|
|
|
@@ -72,10 +72,10 @@ The same rule applies to a CTE or derived query. A query's selected row becomes
|
|
|
72
72
|
the set of columns exposed by its new source:
|
|
73
73
|
|
|
74
74
|
```ts
|
|
75
|
-
import { alias, from, lower, select } from
|
|
75
|
+
import { alias, from, lower, select } from "qubu"
|
|
76
76
|
|
|
77
77
|
const names = select({ name: lower(users.name) }, from(users))
|
|
78
|
-
const namesSource = alias(names,
|
|
78
|
+
const namesSource = alias(names, "names")
|
|
79
79
|
|
|
80
80
|
const query = select({ name: namesSource.name }, from(namesSource))
|
|
81
81
|
```
|
|
@@ -90,35 +90,31 @@ Use `customSource()` for a table-valued function or another relation that
|
|
|
90
90
|
definitions, and complete relation renderer:
|
|
91
91
|
|
|
92
92
|
```ts
|
|
93
|
-
import { eq, from, integer, select, text, where } from
|
|
94
|
-
import { identifier } from
|
|
95
|
-
import { customSource } from
|
|
93
|
+
import { eq, from, integer, select, text, where } from "qubu"
|
|
94
|
+
import { identifier } from "qubu/core"
|
|
95
|
+
import { customSource } from "qubu/schema"
|
|
96
96
|
|
|
97
97
|
const entries = customSource({
|
|
98
98
|
identity: {
|
|
99
|
-
sourceKind:
|
|
100
|
-
name:
|
|
101
|
-
alias:
|
|
99
|
+
sourceKind: "table-function",
|
|
100
|
+
name: "json_each",
|
|
101
|
+
alias: "entry",
|
|
102
102
|
},
|
|
103
|
-
sourceKind:
|
|
104
|
-
reference: identifier(
|
|
103
|
+
sourceKind: "table-function",
|
|
104
|
+
reference: identifier("entry"),
|
|
105
105
|
columns: {
|
|
106
106
|
key: integer(),
|
|
107
107
|
value: text({ nullable: true }),
|
|
108
108
|
},
|
|
109
109
|
render(context) {
|
|
110
|
-
context.append(
|
|
110
|
+
context.append("json_each(")
|
|
111
111
|
context.parameter('{"a":1}')
|
|
112
|
-
context.append(
|
|
113
|
-
context.render(identifier(
|
|
112
|
+
context.append(") AS ")
|
|
113
|
+
context.render(identifier("entry"))
|
|
114
114
|
},
|
|
115
115
|
})
|
|
116
116
|
|
|
117
|
-
const query = select(
|
|
118
|
-
{ value: entries.value },
|
|
119
|
-
from(entries),
|
|
120
|
-
where(eq(entries.key, 7))
|
|
121
|
-
)
|
|
117
|
+
const query = select({ value: entries.value }, from(entries), where(eq(entries.key, 7)))
|
|
122
118
|
```
|
|
123
119
|
|
|
124
120
|
`identity` is the type-level source key. `reference` is the SQL qualifier used
|
|
@@ -135,20 +131,10 @@ Use `correlate()` when an inner query intentionally reads a source from its
|
|
|
135
131
|
enclosing query. The provision changes type checking but emits no SQL:
|
|
136
132
|
|
|
137
133
|
```ts
|
|
138
|
-
import {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
from,
|
|
143
|
-
integer,
|
|
144
|
-
lateral,
|
|
145
|
-
select,
|
|
146
|
-
table,
|
|
147
|
-
where,
|
|
148
|
-
} from 'qubu'
|
|
149
|
-
|
|
150
|
-
const users = table('users', { id: integer() })
|
|
151
|
-
const posts = table('posts', {
|
|
134
|
+
import { correlate, crossJoin, eq, from, integer, lateral, select, table, where } from "qubu"
|
|
135
|
+
|
|
136
|
+
const users = table("users", { id: integer() })
|
|
137
|
+
const posts = table("posts", {
|
|
152
138
|
id: integer(),
|
|
153
139
|
authorId: integer(),
|
|
154
140
|
})
|
|
@@ -157,15 +143,11 @@ const recentPost = select(
|
|
|
157
143
|
{ id: posts.id },
|
|
158
144
|
from(posts),
|
|
159
145
|
correlate(users),
|
|
160
|
-
where(eq(posts.authorId, users.id))
|
|
146
|
+
where(eq(posts.authorId, users.id)),
|
|
161
147
|
)
|
|
162
148
|
|
|
163
|
-
const recent = lateral(recentPost,
|
|
164
|
-
const query = select(
|
|
165
|
-
{ userId: users.id, postId: recent.id },
|
|
166
|
-
from(users),
|
|
167
|
-
crossJoin(recent)
|
|
168
|
-
)
|
|
149
|
+
const recent = lateral(recentPost, "recent_post")
|
|
150
|
+
const query = select({ userId: users.id, postId: recent.id }, from(users), crossJoin(recent))
|
|
169
151
|
```
|
|
170
152
|
|
|
171
153
|
The inner query consumes `posts` locally. The enclosing `users` source satisfies
|
|
@@ -7,10 +7,7 @@
|
|
|
7
7
|
Import the adapter from the optional snapshot entrypoint:
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import {
|
|
11
|
-
createMysqlSchemaSnapshot,
|
|
12
|
-
tryCreateMysqlSchemaSnapshot,
|
|
13
|
-
} from 'qubu/snapshot'
|
|
10
|
+
import { createMysqlSchemaSnapshot, tryCreateMysqlSchemaSnapshot } from "qubu/snapshot"
|
|
14
11
|
|
|
15
12
|
const snapshot = createMysqlSchemaSnapshot(appSchema)
|
|
16
13
|
const result = tryCreateMysqlSchemaSnapshot(appSchema)
|
|
@@ -44,7 +41,7 @@ a schema may include a MySQL engine or version-specific feature:
|
|
|
44
41
|
const result = tryCreateMysqlSchemaSnapshot(appSchema)
|
|
45
42
|
if (!result.ok) {
|
|
46
43
|
for (const issue of result.diagnostics) {
|
|
47
|
-
console.error(issue.path.join(
|
|
44
|
+
console.error(issue.path.join("."), issue.code, issue.message)
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
47
|
```
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createSchemaSnapshot,
|
|
12
12
|
createPostgresSchemaSnapshot,
|
|
13
13
|
postgresSnapshotAdapter,
|
|
14
|
-
} from
|
|
14
|
+
} from "qubu/snapshot"
|
|
15
15
|
|
|
16
16
|
const snapshot = createPostgresSchemaSnapshot(appSchema)
|
|
17
17
|
// Equivalent: createSchemaSnapshot(appSchema, { adapter: postgresSnapshotAdapter })
|
|
@@ -46,12 +46,12 @@ schema and application boundaries.
|
|
|
46
46
|
Use the non-throwing form when a schema may contain a server-specific feature:
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
|
-
import { tryCreatePostgresSchemaSnapshot } from
|
|
49
|
+
import { tryCreatePostgresSchemaSnapshot } from "qubu/snapshot"
|
|
50
50
|
|
|
51
51
|
const result = tryCreatePostgresSchemaSnapshot(appSchema)
|
|
52
52
|
if (!result.ok) {
|
|
53
53
|
for (const issue of result.diagnostics) {
|
|
54
|
-
console.error(issue.path.join(
|
|
54
|
+
console.error(issue.path.join("."), issue.code, issue.message)
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
```
|
|
@@ -5,10 +5,7 @@
|
|
|
5
5
|
Import the adapter from the optional snapshot entrypoint:
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
import {
|
|
9
|
-
createSqliteSchemaSnapshot,
|
|
10
|
-
tryCreateSqliteSchemaSnapshot,
|
|
11
|
-
} from 'qubu/snapshot'
|
|
8
|
+
import { createSqliteSchemaSnapshot, tryCreateSqliteSchemaSnapshot } from "qubu/snapshot"
|
|
12
9
|
|
|
13
10
|
const snapshot = createSqliteSchemaSnapshot(appSchema)
|
|
14
11
|
const result = tryCreateSqliteSchemaSnapshot(appSchema)
|
|
@@ -42,7 +39,7 @@ schema may include a feature that depends on a SQLite version or table shape:
|
|
|
42
39
|
const result = tryCreateSqliteSchemaSnapshot(appSchema)
|
|
43
40
|
if (!result.ok) {
|
|
44
41
|
for (const issue of result.diagnostics) {
|
|
45
|
-
console.error(issue.path.join(
|
|
42
|
+
console.error(issue.path.join("."), issue.code, issue.message)
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
```
|
|
@@ -4,32 +4,32 @@
|
|
|
4
4
|
|
|
5
5
|
## Package entrypoints
|
|
6
6
|
|
|
7
|
-
| Import
|
|
8
|
-
|
|
|
9
|
-
| `qubu`
|
|
10
|
-
| `qubu/core`
|
|
11
|
-
| `qubu/codegen`
|
|
12
|
-
| `qubu/ddl`
|
|
13
|
-
| `qubu/diff`
|
|
14
|
-
| `qubu/
|
|
15
|
-
| `qubu/
|
|
16
|
-
| `qubu/
|
|
17
|
-
| `qubu/
|
|
18
|
-
| `qubu/
|
|
19
|
-
| `qubu/
|
|
20
|
-
| `qubu/
|
|
21
|
-
| `qubu/
|
|
22
|
-
| `qubu/
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
constructors live on their database subpaths. The
|
|
32
|
-
standard SQL policy by default.
|
|
7
|
+
| Import | Kind | Use it for |
|
|
8
|
+
| ------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `qubu` | Runtime | Ordinary query and schema definitions, reads, writes, SQL templates, rendering, EXPLAIN, and execution contracts |
|
|
10
|
+
| `qubu/core` | Runtime | Fragment and rendering primitives, dialect construction, SQL types, and extension constructors |
|
|
11
|
+
| `qubu/codegen` | Runtime | Deterministic machine-owned TypeScript schemas from complete, non-lossy introspection |
|
|
12
|
+
| `qubu/ddl` | Runtime | DDL preflight and deterministic PostgreSQL, SQLite, or MySQL emission from a migration plan |
|
|
13
|
+
| `qubu/diff` | Runtime | Canonical Snapshot v1 or v2 comparison, rename hints, suggestions, and safety diagnostics |
|
|
14
|
+
| `qubu/introspection` | Runtime | Catalog readers, normalized catalogs, and mapping to Snapshot v1 or v2 |
|
|
15
|
+
| `qubu/migration` | Runtime | Pure migration planning with dependencies, decisions, preconditions, and explicit custom SQL |
|
|
16
|
+
| `qubu/mysql` | Runtime | The MySQL query dialect policy |
|
|
17
|
+
| `qubu/postgres` | Runtime | PostgreSQL query dialect helpers such as `postgresDialect()` and `ilike()` |
|
|
18
|
+
| `qubu/schema` | Runtime | Advanced schema metadata, storage and constraint models, source models, and schema-expression extensions |
|
|
19
|
+
| `qubu/snapshot` | Runtime | Canonical Snapshot v1 and v2 traversal, encoding, decoding, diagnostics, and digests |
|
|
20
|
+
| `qubu/sqlite` | Runtime | The SQLite query dialect policy |
|
|
21
|
+
| `qubu/vite` | Runtime | The optional `qubu()` Vite compiler hint |
|
|
22
|
+
| `qubu/package.json` | JSON | The published package manifest |
|
|
23
|
+
| `@qubu/drizzle` | Runtime | Shared Drizzle conversion errors and dialect types |
|
|
24
|
+
| `@qubu/drizzle/mysql` | Runtime | Runtime conversion from Qubu schemas to MySQL Drizzle tables |
|
|
25
|
+
| `@qubu/drizzle/postgres` | Runtime | Runtime conversion from Qubu schemas to PostgreSQL Drizzle tables |
|
|
26
|
+
| `@qubu/drizzle/sqlite` | Runtime | Runtime conversion from Qubu schemas to SQLite Drizzle tables |
|
|
27
|
+
| `@qubu/better-auth` | Runtime | Better Auth schema derivation and native PostgreSQL, MySQL, and SQLite adapter behavior |
|
|
28
|
+
| `qubu/globals` | TypeScript types | Opt-in ambient declarations for directive-bearing modules |
|
|
29
|
+
|
|
30
|
+
The package validator checks every declared entrypoint in each packed workspace
|
|
31
|
+
package. Concrete dialect constructors live on their database subpaths. The
|
|
32
|
+
root renderer uses Qubu's standard SQL policy by default.
|
|
33
33
|
|
|
34
34
|
Snapshot dialect behavior is documented in the [PostgreSQL](postgres-snapshot.md),
|
|
35
35
|
[SQLite](sqlite-snapshot.md), and [MySQL](mysql-snapshot.md) support matrices.
|
|
@@ -70,13 +70,13 @@ Snapshot creation, diffing, migration planning, and DDL emission are pure.
|
|
|
70
70
|
interfaces the application provides. Qubu can emit DDL, but it never applies
|
|
71
71
|
that DDL to a database.
|
|
72
72
|
|
|
73
|
-
| Boundary | Qubu side
|
|
74
|
-
| ------------------------ |
|
|
75
|
-
| Query rendering | Builds a typed query and renders SQL text with ordered raw parameter values
|
|
76
|
-
| Query execution | Binds an adapter with `qubu()` when requested;
|
|
77
|
-
| Catalog introspection | Selects fixed parameterized catalog queries, normalizes rows, and maps catalog data to snapshots
|
|
78
|
-
| Schema source generation | Prints deterministic TypeScript from complete, non-lossy Snapshot v1 introspection without writing files
|
|
79
|
-
| Schema changes | Creates snapshots, compares them, builds deterministic migration plans, and emits DDL from approved plans with preflight diagnostics
|
|
73
|
+
| Boundary | Qubu side | Application side |
|
|
74
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| Query rendering | Builds a typed query and renders SQL text with ordered raw parameter values | Keeps the runtime database schema aligned with query definitions and validates any dynamic syntax passed to an unsafe helper |
|
|
76
|
+
| Query execution | Binds an adapter with `qubu()` when requested; passes rendered statements and result shapes to execution adapters; applies registered logical field decoders to buffered or streamed object rows; scopes transaction callbacks; returns typed results, plans, rows, or streams | Owns the adapter, driver, connections, pools, cursors, stream cleanup, transactions, savepoints, retries, parameter encoding, proprietary row normalization, decoder policy, plan-row decoding, backpressure, cancellation, driver error translation, and database lifecycle |
|
|
77
|
+
| Catalog introspection | Selects fixed parameterized catalog queries, normalizes rows, and maps catalog data to snapshots | Supplies `CatalogConnection`, credentials, already-decoded catalog rows, logging, and connection lifecycle |
|
|
78
|
+
| Schema source generation | Prints deterministic TypeScript from complete, non-lossy Snapshot v1 introspection without writing files | Owns generated-file writes, replacement policy, hand-edit merging, and CLI integration |
|
|
79
|
+
| Schema changes | Creates snapshots, compares them, builds deterministic migration plans, and emits DDL from approved plans with preflight diagnostics | Reviews decisions, executes or rolls back statements, acquires locks, manages transactions and migration journals, and owns database lifecycle |
|
|
80
80
|
|
|
81
81
|
Start with [Dialects and execution](../dialects-and-execution.md) for the query
|
|
82
82
|
adapter contract. The schema path is documented in [Canonical schema
|
|
@@ -12,7 +12,7 @@ families and an immutable materializer:
|
|
|
12
12
|
import {
|
|
13
13
|
createCompleteIntrospectionCatalog,
|
|
14
14
|
mapCatalogToCompleteSnapshot,
|
|
15
|
-
} from
|
|
15
|
+
} from "qubu/introspection"
|
|
16
16
|
|
|
17
17
|
const completeCatalog = createCompleteIntrospectionCatalog(catalog)
|
|
18
18
|
const result = mapCatalogToCompleteSnapshot(completeCatalog)
|
|
@@ -36,10 +36,7 @@ references and are not used as logical IDs.
|
|
|
36
36
|
`qubu/snapshot` provides the strict complete format as a separate API:
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
|
-
import {
|
|
40
|
-
decodeCompleteSchemaSnapshot,
|
|
41
|
-
encodeCompleteSchemaSnapshot,
|
|
42
|
-
} from 'qubu/snapshot'
|
|
39
|
+
import { decodeCompleteSchemaSnapshot, encodeCompleteSchemaSnapshot } from "qubu/snapshot"
|
|
43
40
|
|
|
44
41
|
const encoded = encodeCompleteSchemaSnapshot(snapshotV2)
|
|
45
42
|
const decoded = decodeCompleteSchemaSnapshot(encoded)
|