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.
Files changed (104) 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-B2PO6wQD.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-7qc6OcIC.d.mts → index-1DpA3mUh.d.mts} +20 -20
  15. package/dist/{index-CqWnouTK.d.mts → index-CPvfEheG.d.mts} +13 -15
  16. package/dist/index.d.mts +2 -2
  17. package/dist/index.mjs +148 -120
  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-DqkqXB6A.mjs → mysql-B_cYzzX2.mjs} +237 -7
  24. package/dist/mysql.d.mts +1 -1
  25. package/dist/mysql.mjs +3 -3
  26. package/dist/{on-conflict-BxnxubMb.mjs → on-conflict-hfPW0KmQ.mjs} +4 -4
  27. package/dist/postgres.d.mts +5 -5
  28. package/dist/postgres.mjs +6 -6
  29. package/dist/{registry-BufIskVN.mjs → registry-BRMLYwDp.mjs} +27 -135
  30. package/dist/{relational-DCZrrNia.mjs → relational-BZ3WDPzC.mjs} +4 -4
  31. package/dist/schema.d.mts +2 -2
  32. package/dist/schema.mjs +6 -6
  33. package/dist/{snapshot-CWPgzxNx.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-DUoJVXmL.mjs → source-BcS2AsIg.mjs} +7 -9
  37. package/dist/{serialize-PF1cfH2P.mjs → sqlite-Cg0nwYEH.mjs} +326 -12
  38. package/dist/sqlite.d.mts +2 -2
  39. package/dist/sqlite.mjs +2 -2
  40. package/dist/{standard-BTVYKh_F.mjs → standard-DfcZEVOj.mjs} +1 -1
  41. package/dist/{table-CCUJ60rB.mjs → table-Bp5irMSj.mjs} +5 -7
  42. package/dist/{types-D8M1yZF4.d.mts → types-BK1COGZe.d.mts} +2661 -2150
  43. package/dist/{types-Cec0xzo4.mjs → types-JM3FcAnX.mjs} +8 -8
  44. package/dist/{types-LBt5rclR.d.mts → types-JSZHpUEj.d.mts} +83 -84
  45. package/dist/{value-BvilP0oz.mjs → value-Bi71Agyf.mjs} +1 -1
  46. package/dist/vite/ambient.d.ts +311 -390
  47. package/dist/vite.d.mts +5 -6
  48. package/dist/vite.mjs +3 -4
  49. package/docs/dialects-and-execution.md +121 -81
  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 +33 -33
  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-CXMxx8Hq.mjs +0 -118
  90. package/dist/column-CYMbKbOy.mjs +0 -290
  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 -50
  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/naming-QVCOnSj2.mjs +0 -20
  101. package/dist/postgres-DEBBeh52.mjs +0 -235
  102. package/dist/runtime-Cn_Xgzta.mjs +0 -193
  103. package/dist/sqlite-BU6DBxef.mjs +0 -320
  104. package/dist/types-Ctlxz1ip.d.mts +0 -45
@@ -12,19 +12,19 @@ Read and map one namespace in strict mode, then pass that exact result to the
12
12
  generator:
13
13
 
14
14
  ```ts
15
- import { writeFile } from 'node:fs/promises'
16
- import { generateSchemaSource } from 'qubu/codegen'
17
- import { mapCatalogToSnapshot, readSqliteCatalog } from 'qubu/introspection'
15
+ import { writeFile } from "node:fs/promises"
16
+ import { generateSchemaSource } from "qubu/codegen"
17
+ import { mapCatalogToSnapshot, readSqliteCatalog } from "qubu/introspection"
18
18
 
19
- const catalog = await readSqliteCatalog(connection, { namespace: 'main' })
20
- const introspection = mapCatalogToSnapshot(catalog, { namespace: 'main' })
19
+ const catalog = await readSqliteCatalog(connection, { namespace: "main" })
20
+ const introspection = mapCatalogToSnapshot(catalog, { namespace: "main" })
21
21
  const generated = generateSchemaSource(introspection)
22
22
 
23
23
  if (!generated.ok) {
24
- throw new Error(generated.diagnostics.map(issue => issue.message).join('\n'))
24
+ throw new Error(generated.diagnostics.map((issue) => issue.message).join("\n"))
25
25
  }
26
26
 
27
- await writeFile('src/schema.generated.ts', generated.source, 'utf8')
27
+ await writeFile("src/schema.generated.ts", generated.source, "utf8")
28
28
  ```
29
29
 
30
30
  `writeFile()` belongs to the application; `generateSchemaSource()` only
@@ -48,13 +48,13 @@ the generated module is accepted, its serialized snapshot becomes the identity
48
48
  baseline for the next catalog read:
49
49
 
50
50
  ```ts
51
- import { mapCatalogToSnapshot } from 'qubu/introspection'
52
- import { createSqliteSchemaSnapshot } from 'qubu/snapshot'
53
- import { mainSchema } from './schema.generated.ts'
51
+ import { mapCatalogToSnapshot } from "qubu/introspection"
52
+ import { createSqliteSchemaSnapshot } from "qubu/snapshot"
53
+ import { mainSchema } from "./schema.generated.ts"
54
54
 
55
55
  const previousSnapshot = createSqliteSchemaSnapshot(mainSchema)
56
56
  const next = mapCatalogToSnapshot(nextCatalog, {
57
- namespace: 'main',
57
+ namespace: "main",
58
58
  previousSnapshot,
59
59
  })
60
60
  ```
@@ -75,17 +75,17 @@ Use the controlled callbacks to adopt trusted names or application mappings:
75
75
  ```ts
76
76
  const generated = generateSchemaSource(introspection, {
77
77
  naming(context) {
78
- if (context.kind === 'table' && context.physicalName === 'user_records') {
79
- return 'users'
78
+ if (context.kind === "table" && context.physicalName === "user_records") {
79
+ return "users"
80
80
  }
81
81
  },
82
82
  mapColumn(context) {
83
- if (context.columnPhysicalName === 'account_id') {
83
+ if (context.columnPhysicalName === "account_id") {
84
84
  return {
85
- output: 'string',
86
- insert: 'string',
87
- update: 'string',
88
- sqlDomain: 'uuid',
85
+ output: "string",
86
+ insert: "string",
87
+ update: "string",
88
+ sqlDomain: "uuid",
89
89
  }
90
90
  }
91
91
  },
@@ -17,9 +17,9 @@ Use `column<Output, Insert, Update>()` when the driver returns a different type
17
17
  from the type the application writes:
18
18
 
19
19
  ```ts
20
- import { column, integer, table, text } from 'qubu'
20
+ import { column, integer, table, text } from "qubu"
21
21
 
22
- const accounts = table('accounts', {
22
+ const accounts = table("accounts", {
23
23
  id: integer({ generated: true }),
24
24
  email: text(),
25
25
  nickname: text({ nullable: true, hasDefault: true }),
@@ -36,27 +36,19 @@ The legacy hasDefault and generated flags describe the write contract. Use
36
36
  complete metadata when schema tooling also needs the database fact:
37
37
 
38
38
  ```ts
39
- import {
40
- boolean,
41
- generatedColumn,
42
- identityColumn,
43
- integer,
44
- table,
45
- text,
46
- value,
47
- } from 'qubu'
48
- import { defineSchemaExpression } from 'qubu/schema'
49
-
50
- const currentTimestamp = defineSchemaExpression('function', context => {
51
- context.append('CURRENT_TIMESTAMP')
39
+ import { boolean, generatedColumn, identityColumn, integer, table, text, value } from "qubu"
40
+ import { defineSchemaExpression } from "qubu/schema"
41
+
42
+ const currentTimestamp = defineSchemaExpression("function", (context) => {
43
+ context.append("CURRENT_TIMESTAMP")
52
44
  })
53
45
 
54
- const accounts = table('accounts', {
55
- id: integer({ identity: identityColumn('always') }),
56
- status: text({ default: 'pending' }),
46
+ const accounts = table("accounts", {
47
+ id: integer({ identity: identityColumn("always") }),
48
+ status: text({ default: "pending" }),
57
49
  active: boolean({ default: true }),
58
50
  score: integer({
59
- generatedColumn: generatedColumn(value(1), 'stored'),
51
+ generatedColumn: generatedColumn(value(1), "stored"),
60
52
  }),
61
53
  createdAt: text({
62
54
  default: currentTimestamp,
@@ -90,10 +82,10 @@ Use `$type<T>()` to narrow a helper's TypeScript type without changing its
90
82
  runtime column definition:
91
83
 
92
84
  ```ts
93
- import { table, text } from 'qubu'
85
+ import { table, text } from "qubu"
94
86
 
95
- const users = table('users', {
96
- status: text().$type<'active' | 'disabled'>(),
87
+ const users = table("users", {
88
+ status: text().$type<"active" | "disabled">(),
97
89
  })
98
90
  ```
99
91
 
@@ -106,14 +98,14 @@ does not validate values at runtime or add a database constraint.
106
98
  code. The following example uses the accounts table from the earlier example:
107
99
 
108
100
  ```ts
109
- import type { TableInsertInput, TableUpdateInput } from 'qubu'
101
+ import type { TableInsertInput, TableUpdateInput } from "qubu"
110
102
 
111
103
  type AccountInsert = TableInsertInput<typeof accounts.definitions>
112
104
  type AccountUpdate = TableUpdateInput<typeof accounts.definitions>
113
105
 
114
106
  const insert: AccountInsert = {
115
- email: 'ada@example.com',
116
- externalScore: '10',
107
+ email: "ada@example.com",
108
+ externalScore: "10",
117
109
  }
118
110
 
119
111
  const update: AccountUpdate = {
@@ -20,45 +20,38 @@ import {
20
20
  text,
21
21
  unique,
22
22
  value,
23
- } from 'qubu'
23
+ } from "qubu"
24
24
 
25
- const accounts = table(
26
- 'accounts',
27
- { id: integer(), slug: text() },
28
- accounts => ({
29
- constraints: {
30
- accountsPrimary: primaryKey(accounts.id),
31
- },
32
- indexes: {
33
- accountsSlug: index([accounts.slug], { unique: true }),
34
- },
35
- })
36
- )
25
+ const accounts = table("accounts", { id: integer(), slug: text() }, (accounts) => ({
26
+ constraints: {
27
+ accountsPrimary: primaryKey(accounts.id),
28
+ },
29
+ indexes: {
30
+ accountsSlug: index([accounts.slug], { unique: true }),
31
+ },
32
+ }))
37
33
 
38
34
  const memberships = table(
39
- 'memberships',
35
+ "memberships",
40
36
  {
41
37
  id: integer(),
42
38
  accountId: integer(),
43
39
  slug: text(),
44
40
  displayName: text(),
45
41
  },
46
- memberships => ({
42
+ (memberships) => ({
47
43
  constraints: {
48
44
  membershipsPrimary: primaryKey(memberships.id),
49
45
  membershipsUnique: unique(memberships.accountId, memberships.slug),
50
- membershipsAccount: foreignKey(
51
- [memberships.accountId],
52
- references(accounts, accounts.id)
53
- ),
54
- membershipsCheck: check(eq(memberships.slug, value('public'))),
46
+ membershipsAccount: foreignKey([memberships.accountId], references(accounts, accounts.id)),
47
+ membershipsCheck: check(eq(memberships.slug, value("public"))),
55
48
  },
56
49
  indexes: {
57
50
  membershipsAccountSlug: index([memberships.accountId, memberships.slug], {
58
51
  unique: true,
59
52
  }),
60
53
  },
61
- })
54
+ }),
62
55
  )
63
56
  ```
64
57
 
@@ -81,21 +74,17 @@ Use `uniqueConstraint()` when the database enforces uniqueness but the rule shou
81
74
  not prove a functional dependency:
82
75
 
83
76
  ```ts
84
- import { table, text, uniqueConstraint } from 'qubu'
85
-
86
- const accounts = table(
87
- 'accounts',
88
- { email: text({ nullable: true }) },
89
- accounts => ({
90
- constraints: {
91
- emailUnique: uniqueConstraint(accounts.email, {
92
- nulls: 'distinct',
93
- physicalName: 'accounts_email_key',
94
- }),
95
- },
96
- indexes: {},
97
- })
98
- )
77
+ import { table, text, uniqueConstraint } from "qubu"
78
+
79
+ const accounts = table("accounts", { email: text({ nullable: true }) }, (accounts) => ({
80
+ constraints: {
81
+ emailUnique: uniqueConstraint(accounts.email, {
82
+ nulls: "distinct",
83
+ physicalName: "accounts_email_key",
84
+ }),
85
+ },
86
+ indexes: {},
87
+ }))
99
88
  ```
100
89
 
101
90
  `nulls: 'distinct'` describes the common rule where multiple NULLs do not
@@ -115,20 +104,15 @@ eligible unique index. Options such as onUpdate, onDelete, match, deferrable,
115
104
  and initially remain metadata:
116
105
 
117
106
  ```ts
118
- const memberships = table(
119
- 'memberships',
120
- { accountId: integer() },
121
- memberships => ({
122
- constraints: {
123
- accountForeign: foreignKey(
124
- [memberships.accountId],
125
- references(accounts, accounts.id),
126
- { onDelete: 'cascade', onUpdate: 'cascade' }
127
- ),
128
- },
129
- indexes: {},
130
- })
131
- )
107
+ const memberships = table("memberships", { accountId: integer() }, (memberships) => ({
108
+ constraints: {
109
+ accountForeign: foreignKey([memberships.accountId], references(accounts, accounts.id), {
110
+ onDelete: "cascade",
111
+ onUpdate: "cascade",
112
+ }),
113
+ },
114
+ indexes: {},
115
+ }))
132
116
  ```
133
117
 
134
118
  Use the preliminary callback table for direct self-references. Wrap the target
@@ -145,12 +129,12 @@ Grouping every column in a declared candidate key lets Qubu select other
145
129
  columns from that source:
146
130
 
147
131
  ```ts
148
- import { count, from, groupBy, select } from 'qubu'
132
+ import { count, from, groupBy, select } from "qubu"
149
133
 
150
134
  const summary = select(
151
135
  { displayName: memberships.displayName, total: count() },
152
136
  from(memberships),
153
- groupBy(memberships.accountId, memberships.slug)
137
+ groupBy(memberships.accountId, memberships.slug),
154
138
  )
155
139
  ```
156
140
 
@@ -8,13 +8,13 @@ migration journal. Preflight runs before rendering, so a blocked or incompatible
8
8
  plan returns diagnostics and no SQL.
9
9
 
10
10
  ```ts
11
- import { emitMigrationPlan } from 'qubu/ddl'
12
- import { postgresSchemaDialect } from 'qubu/snapshot'
11
+ import { emitMigrationPlan } from "qubu/ddl"
12
+ import { postgresSchemaDialect } from "qubu/snapshot"
13
13
 
14
14
  const result = emitMigrationPlan(plan, postgresSchemaDialect)
15
15
  if (!result.ok) {
16
16
  // Review result.diagnostics. result.sql is an empty string.
17
- throw new Error(result.diagnostics.map(item => item.message).join('\n'))
17
+ throw new Error(result.diagnostics.map((item) => item.message).join("\n"))
18
18
  }
19
19
 
20
20
  for (const statement of result.statements) {
@@ -44,9 +44,9 @@ produces `transaction-conflict`; no transaction is opened by the emitter.
44
44
 
45
45
  ```ts
46
46
  const result = emitMigrationPlan(plan, postgresSchemaDialect, {
47
- transaction: 'managed',
48
- lock: 'exclusive',
49
- serverVersion: '16',
47
+ transaction: "managed",
48
+ lock: "exclusive",
49
+ serverVersion: "16",
50
50
  })
51
51
  ```
52
52
 
@@ -8,7 +8,7 @@ It keeps the object kind, namespace, logical ID, physical name, dialect, path,
8
8
  and catalog evidence on every result object.
9
9
 
10
10
  ```ts
11
- import { diffSnapshots } from 'qubu/diff'
11
+ import { diffSnapshots } from "qubu/diff"
12
12
 
13
13
  const result = diffSnapshots(previousSnapshot, currentSnapshot)
14
14
 
@@ -32,10 +32,10 @@ use an ID, a physical name, or an exact path when a nested scope repeats an ID.
32
32
  const result = diffSnapshots(previousSnapshot, currentSnapshot, {
33
33
  renameHints: [
34
34
  {
35
- kind: 'table',
36
- namespace: 'public',
37
- from: 'legacy_accounts',
38
- to: 'accounts',
35
+ kind: "table",
36
+ namespace: "public",
37
+ from: "legacy_accounts",
38
+ to: "accounts",
39
39
  },
40
40
  ],
41
41
  })
@@ -37,10 +37,10 @@ queries, authenticate, or start a transaction. Adapt the driver you already
37
37
  use to `CatalogConnection`:
38
38
 
39
39
  ```ts
40
- import type { CatalogConnection } from 'qubu/introspection'
40
+ import type { CatalogConnection } from "qubu/introspection"
41
41
 
42
42
  const connection: CatalogConnection = {
43
- dialect: 'sqlite',
43
+ dialect: "sqlite",
44
44
  query(statement, options) {
45
45
  // Adapt this call to the driver used by the application.
46
46
  return db.query(statement, options)
@@ -59,16 +59,16 @@ catalog can later support inspection, source generation, or another snapshot
59
59
  format:
60
60
 
61
61
  ```ts
62
- import { mapCatalogToSnapshot, readSqliteCatalog } from 'qubu/introspection'
62
+ import { mapCatalogToSnapshot, readSqliteCatalog } from "qubu/introspection"
63
63
 
64
- const catalog = await readSqliteCatalog(connection, { namespace: 'main' })
64
+ const catalog = await readSqliteCatalog(connection, { namespace: "main" })
65
65
  const result = mapCatalogToSnapshot(catalog, {
66
- namespace: 'main',
67
- mode: 'strict',
66
+ namespace: "main",
67
+ mode: "strict",
68
68
  })
69
69
 
70
70
  if (!result.ok) {
71
- throw new Error(result.diagnostics.map(issue => issue.message).join('\n'))
71
+ throw new Error(result.diagnostics.map((issue) => issue.message).join("\n"))
72
72
  }
73
73
 
74
74
  result.snapshot.tables // canonical Snapshot v1 data
@@ -8,7 +8,7 @@ logical and physical identity evidence, dependency edges, preconditions, safety,
8
8
  lock and transaction requirements, and reversibility markers.
9
9
 
10
10
  ```ts
11
- import { createMigrationPlan } from 'qubu/migration'
11
+ import { createMigrationPlan } from "qubu/migration"
12
12
 
13
13
  const result = createMigrationPlan(diff)
14
14
  if (!result.ok) {
@@ -31,11 +31,11 @@ kind, namespace, and path, and each decision carries a review reason.
31
31
  ```ts
32
32
  const reviewed = createMigrationPlan(diff, {
33
33
  decisions: result.plan.operations
34
- .filter(operation => operation.status === 'decision-required')
35
- .map(operation => ({
34
+ .filter((operation) => operation.status === "decision-required")
35
+ .map((operation) => ({
36
36
  operationId: operation.id,
37
- action: 'allow',
38
- reason: 'Reviewed against the deployment change request',
37
+ action: "allow",
38
+ reason: "Reviewed against the deployment change request",
39
39
  })),
40
40
  })
41
41
  ```
@@ -48,10 +48,10 @@ dialect, safety declaration, reason, reversibility, and dependency position:
48
48
  createMigrationPlan(diff, {
49
49
  customSql: [
50
50
  {
51
- sql: 'ALTER TABLE accounts VALIDATE CONSTRAINT accounts_check',
52
- dialect: { name: 'postgresql', version: 1 },
53
- safety: 'review-required',
54
- reason: 'The dialect emitter does not model this catalog fact yet',
51
+ sql: "ALTER TABLE accounts VALIDATE CONSTRAINT accounts_check",
52
+ dialect: { name: "postgresql", version: 1 },
53
+ safety: "review-required",
54
+ reason: "The dialect emitter does not model this catalog fact yet",
55
55
  reversible: false,
56
56
  position: 3,
57
57
  },
@@ -8,7 +8,7 @@ hashed, checked into source control, and handed to a dialect adapter. Importing
8
8
  the snapshot entrypoint is optional; ordinary query imports do not load it.
9
9
 
10
10
  ```ts
11
- import { createSchemaSnapshot, encodeSchemaSnapshot } from 'qubu/snapshot'
11
+ import { createSchemaSnapshot, encodeSchemaSnapshot } from "qubu/snapshot"
12
12
 
13
13
  const snapshot = createSchemaSnapshot(appSchema)
14
14
  const json = encodeSchemaSnapshot(snapshot)
@@ -29,12 +29,12 @@ expression retains its dialect tag and is rejected when it does not match the
29
29
  selected snapshot dialect.
30
30
 
31
31
  ```ts
32
- import { decodeSchemaSnapshot } from 'qubu/snapshot'
32
+ import { decodeSchemaSnapshot } from "qubu/snapshot"
33
33
 
34
34
  const decoded = decodeSchemaSnapshot(json)
35
35
  if (!decoded.ok) {
36
36
  for (const issue of decoded.diagnostics) {
37
- console.error(issue.path.join('.'), issue.code, issue.message)
37
+ console.error(issue.path.join("."), issue.code, issue.message)
38
38
  }
39
39
  }
40
40
  ```
@@ -33,10 +33,10 @@ Use a dialect-native descriptor when a column needs an exact vendor
33
33
  declaration:
34
34
 
35
35
  ```ts
36
- import { nativeColumn, nativeStorage, table } from 'qubu'
36
+ import { nativeColumn, nativeStorage, table } from "qubu"
37
37
 
38
- const accounts = table('accounts', {
39
- handle: nativeColumn(nativeStorage('postgresql', 'citext COLLATE "C"')),
38
+ const accounts = table("accounts", {
39
+ handle: nativeColumn(nativeStorage("postgresql", 'citext COLLATE "C"')),
40
40
  })
41
41
  ```
42
42
 
@@ -52,12 +52,12 @@ can be rendered for checks, generated columns, indexes, and other declaration
52
52
  metadata:
53
53
 
54
54
  ```ts
55
- import { eq, table, text } from 'qubu'
56
- import { renderSchemaSql } from 'qubu/schema'
55
+ import { eq, table, text } from "qubu"
56
+ import { renderSchemaSql } from "qubu/schema"
57
57
 
58
- const accounts = table('accounts', { status: text() })
58
+ const accounts = table("accounts", { status: text() })
59
59
 
60
- renderSchemaSql(eq(accounts.status, 'active'), { mode: 'check' })
60
+ renderSchemaSql(eq(accounts.status, "active"), { mode: "check" })
61
61
  // ("status" = 'active')
62
62
  ```
63
63
 
@@ -74,13 +74,13 @@ subqueries are rejected in every schema mode.
74
74
  An extension must opt into the schema contract with `defineSchemaExpression()`:
75
75
 
76
76
  ```ts
77
- import { defineSchemaExpression, renderSchemaSql } from 'qubu/schema'
77
+ import { defineSchemaExpression, renderSchemaSql } from "qubu/schema"
78
78
 
79
- const currentDate = defineSchemaExpression('function', context => {
80
- context.append('CURRENT_DATE')
79
+ const currentDate = defineSchemaExpression("function", (context) => {
80
+ context.append("CURRENT_DATE")
81
81
  })
82
82
 
83
- renderSchemaSql(currentDate, { mode: 'default' })
83
+ renderSchemaSql(currentDate, { mode: "default" })
84
84
  ```
85
85
 
86
86
  ## Use raw schema SQL only when necessary
@@ -12,17 +12,17 @@ record keys become logical table IDs and stay stable when a physical SQL name
12
12
  changes:
13
13
 
14
14
  ```ts
15
- import { integer, schema, table, text } from 'qubu'
15
+ import { integer, schema, table, text } from "qubu"
16
16
 
17
- const accounts = table('account_records', {
17
+ const accounts = table("account_records", {
18
18
  id: integer(),
19
19
  email: text(),
20
20
  })
21
- const memberships = table('membership_records', {
21
+ const memberships = table("membership_records", {
22
22
  accountId: integer(),
23
23
  })
24
24
 
25
- const appSchema = schema({ accounts, memberships }, { namespace: 'public' })
25
+ const appSchema = schema({ accounts, memberships }, { namespace: "public" })
26
26
  ```
27
27
 
28
28
  appSchema.registry.accounts.id is "accounts", while the physical table name is
@@ -47,17 +47,14 @@ Write schema keys in camelCase. Qubu converts them to snake_case in SQL, then
47
47
  uses the camelCase keys in the returned row:
48
48
 
49
49
  ```ts
50
- import { from, select, table, timestamp, uuid } from 'qubu'
50
+ import { from, select, table, timestamp, uuid } from "qubu"
51
51
 
52
- const events = table('events', {
52
+ const events = table("events", {
53
53
  userId: uuid(),
54
54
  createdAt: timestamp(),
55
55
  })
56
56
 
57
- const query = select(
58
- { userId: events.userId, createdAt: events.createdAt },
59
- from(events)
60
- )
57
+ const query = select({ userId: events.userId, createdAt: events.createdAt }, from(events))
61
58
  ```
62
59
 
63
60
  The query selects "events"."user_id" and "events"."created_at", then aliases
@@ -76,8 +73,8 @@ Prefer `userId` and `apiKey` when you control the TypeScript name. Use `sqlName`
76
73
  the database name does not follow the convention:
77
74
 
78
75
  ```ts
79
- const events = table('events', {
80
- createdAt: timestamp({ sqlName: 'creation_timestamp' }),
76
+ const events = table("events", {
77
+ createdAt: timestamp({ sqlName: "creation_timestamp" }),
81
78
  })
82
79
  ```
83
80
 
@@ -89,9 +86,9 @@ their projected names stay snake_case. Only the outer result projection uses
89
86
  camelCase aliases. Pass the database relation name explicitly:
90
87
 
91
88
  ```ts
92
- import { table, uuid } from 'qubu'
89
+ import { table, uuid } from "qubu"
93
90
 
94
- const accounts = table('user_accounts', {
91
+ const accounts = table("user_accounts", {
95
92
  id: uuid(),
96
93
  })
97
94
  ```
@@ -43,10 +43,10 @@ Specific result domains are still retained. `lower(textColumn)` produces
43
43
  `SqlText`, while `avg(integerColumn)` produces `SqlDecimal`:
44
44
 
45
45
  ```ts
46
- import { avg, integer, lower, table, text } from 'qubu'
47
- import type { SqlTypeOf } from 'qubu'
46
+ import { avg, integer, lower, table, text } from "qubu"
47
+ import type { SqlTypeOf } from "qubu"
48
48
 
49
- const metrics = table('metrics', {
49
+ const metrics = table("metrics", {
50
50
  label: text(),
51
51
  sampleCount: integer(),
52
52
  })
@@ -72,15 +72,15 @@ semantics. Contextual JavaScript literals remain ergonomic for compatible
72
72
  operators:
73
73
 
74
74
  ```ts
75
- import { asc, eq, inList, like, lower, orderBy, table, uuid } from 'qubu'
75
+ import { asc, eq, inList, like, lower, orderBy, table, uuid } from "qubu"
76
76
 
77
- const records = table('records', { id: uuid() })
77
+ const records = table("records", { id: uuid() })
78
78
 
79
- eq(records.id, '108cb836-20d2-41b2-8c23-f0c94700aa7e') // valid
80
- inList(records.id, ['first-id', 'second-id']) // valid
79
+ eq(records.id, "108cb836-20d2-41b2-8c23-f0c94700aa7e") // valid
80
+ inList(records.id, ["first-id", "second-id"]) // valid
81
81
 
82
82
  lower(records.id) // TypeScript error: SqlUuid is not SqlTextLike
83
- like(records.id, '%uuid%') // TypeScript error: SqlUuid is not SqlTextLike
83
+ like(records.id, "%uuid%") // TypeScript error: SqlUuid is not SqlTextLike
84
84
  orderBy(asc(records.id)) // TypeScript error: SqlUuid is not portably orderable
85
85
  ```
86
86
 
@@ -90,10 +90,10 @@ operation. This contextual typing does not relabel an expression: comparing a
90
90
  the database operation intentionally changes domains:
91
91
 
92
92
  ```ts
93
- import { cast, like, text } from 'qubu'
93
+ import { cast, like, text } from "qubu"
94
94
 
95
95
  const idAsText = cast(records.id, text())
96
- like(idAsText, '108c%')
96
+ like(idAsText, "108c%")
97
97
  ```
98
98
 
99
99
  Built-in definitions carry logical cast targets, so the active dialect can