qubu 0.6.0 → 0.6.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 (95) hide show
  1. package/README.md +112 -0
  2. package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
  3. package/dist/codegen.d.mts +2 -2
  4. package/dist/codegen.mjs +180 -90
  5. package/dist/column-Cyc2CMnG.mjs +116 -0
  6. package/dist/column-DDRvD7SF.mjs +721 -0
  7. package/dist/{constraints-YGyNPQ_z.mjs → constraints-CAmi18Uk.mjs} +8 -3
  8. package/dist/core.d.mts +3 -3
  9. package/dist/core.mjs +4 -5
  10. package/dist/diagnostics-I9vVtXkc.mjs +40 -0
  11. package/dist/diff.d.mts +9 -9
  12. package/dist/diff.mjs +152 -102
  13. package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
  14. package/dist/expressions-_6JF_J77.mjs +130 -0
  15. package/dist/index-CaxrMD1A.d.mts +1 -0
  16. package/dist/index.d.mts +2 -2
  17. package/dist/index.mjs +414 -77
  18. package/dist/introspection/mysql.d.mts +26 -0
  19. package/dist/introspection/mysql.mjs +1278 -0
  20. package/dist/introspection/postgres.d.mts +43 -0
  21. package/dist/introspection/postgres.mjs +1889 -0
  22. package/dist/introspection/sqlite.d.mts +15 -0
  23. package/dist/introspection/sqlite.mjs +1172 -0
  24. package/dist/introspection.d.mts +26 -87
  25. package/dist/introspection.mjs +3 -4351
  26. package/dist/mysql.d.mts +3 -3
  27. package/dist/mysql.mjs +8 -7
  28. package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
  29. package/dist/{on-conflict-DZQ85f1t.mjs → on-conflict-B2rFyHGF.mjs} +78 -8
  30. package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
  31. package/dist/postgres-hFhd0I9n.mjs +68 -0
  32. package/dist/postgres.d.mts +3 -3
  33. package/dist/postgres.mjs +3 -52
  34. package/dist/registry-BXE_4M9P.mjs +127 -0
  35. package/dist/{relational-CxnLCqZQ.mjs → relational-CoPBETjI.mjs} +3 -3
  36. package/dist/schema.d.mts +2 -2
  37. package/dist/schema.mjs +8 -7
  38. package/dist/{serialize-BN07IK0v.mjs → serialize-CyobNEx-.mjs} +174 -30
  39. package/dist/serialize-Du2UPZMt.d.mts +92 -0
  40. package/dist/snapshot/mysql.d.mts +5 -5
  41. package/dist/snapshot/mysql.mjs +32 -32
  42. package/dist/snapshot/postgres.d.mts +5 -5
  43. package/dist/snapshot/postgres.mjs +25 -25
  44. package/dist/snapshot/sqlite.d.mts +5 -5
  45. package/dist/snapshot/sqlite.mjs +30 -30
  46. package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
  47. package/dist/snapshot.d.mts +4 -5
  48. package/dist/snapshot.mjs +3 -4
  49. package/dist/{source-SqrKWjFJ.mjs → source-DYSUqzvb.mjs} +2 -2
  50. package/dist/sqlite.d.mts +2 -2
  51. package/dist/sqlite.mjs +10 -10
  52. package/dist/{table-BwflqeAj.mjs → table-B8zEq0az.mjs} +4 -4
  53. package/dist/{types-BIJsj2fJ.mjs → types-CYHpSPwj.mjs} +10 -7
  54. package/dist/{types-CTCqtFlS.d.mts → types-CiMvKi5V.d.mts} +14 -4
  55. package/dist/{types-C0VkiwpR.d.mts → types-Dqr4o2I1.d.mts} +640 -189
  56. package/dist/value-CpaUFtjw.mjs +45 -0
  57. package/dist/vite/ambient.d.ts +2 -0
  58. package/dist/vite.d.mts +1 -1
  59. package/dist/vite.mjs +2 -0
  60. package/docs/dialects-and-execution.md +61 -11
  61. package/docs/getting-started.md +1 -1
  62. package/docs/guides/compose-queries.md +22 -0
  63. package/docs/guides/drizzle.md +11 -11
  64. package/docs/guides/extensions/typed-expressions.md +5 -3
  65. package/docs/guides/json.md +95 -2
  66. package/docs/guides/mutations.md +125 -0
  67. package/docs/guides/valtio-sync.md +113 -0
  68. package/docs/index.md +1 -1
  69. package/docs/migrations/adapters.md +58 -21
  70. package/docs/migrations/index.md +50 -12
  71. package/docs/migrations/operations.md +20 -6
  72. package/docs/migrations/recovery.md +7 -1
  73. package/docs/query-model/fragments.md +21 -1
  74. package/docs/reference/introspection-support.md +16 -18
  75. package/docs/reference/supported-surface.md +23 -11
  76. package/docs/schema/catalog-model.md +20 -7
  77. package/docs/schema/code-generation.md +4 -3
  78. package/docs/schema/diff.md +2 -2
  79. package/docs/schema/introspection.md +24 -16
  80. package/docs/schema/snapshots.md +18 -8
  81. package/docs/schema/storage-and-schema-sql.md +7 -2
  82. package/docs/sql-semantic-types.md +8 -0
  83. package/package.json +14 -1
  84. package/dist/canonical-DMvR9yBe.mjs +0 -972
  85. package/dist/column-Da37jYSD.mjs +0 -309
  86. package/dist/column-r1Y4ivwt.mjs +0 -327
  87. package/dist/complete-types-CY0KbzNw.d.mts +0 -371
  88. package/dist/index-B2rZf3-2.d.mts +0 -32
  89. package/dist/json-Db7XRD91.mjs +0 -169
  90. package/dist/omit-OxV58AwX.mjs +0 -5
  91. package/dist/registry-BRcUuazJ.mjs +0 -256
  92. package/dist/serialize-CEIIlWhC.d.mts +0 -66
  93. package/dist/snapshot-Xam8-q0j.mjs +0 -392
  94. package/dist/standard-DfcZEVOj.mjs +0 -12
  95. package/dist/value-D14I_XgL.mjs +0 -29
@@ -1,6 +1,6 @@
1
1
  # Database introspection
2
2
 
3
- > Read one existing database namespace into explainable catalog data and an optional canonical Snapshot v1 or complete Snapshot v2 without giving Qubu ownership of the connection.
3
+ > Read one existing database namespace into explainable catalog data and a canonical Snapshot v1 without giving Qubu ownership of the connection.
4
4
 
5
5
  Database introspection is an optional capability exported from
6
6
  `qubu/introspection`. It discovers database facts; it does not recreate the
@@ -59,9 +59,10 @@ 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 } from "qubu/introspection"
63
+ import { readCatalog } from "qubu/introspection/sqlite"
63
64
 
64
- const catalog = await readSqliteCatalog(connection, { namespace: "main" })
65
+ const catalog = await readCatalog(connection, { namespace: "main" })
65
66
  const result = mapCatalogToSnapshot(catalog, {
66
67
  namespace: "main",
67
68
  mode: "strict",
@@ -84,8 +85,8 @@ catalog. Use `createCompleteIntrospectionCatalog()` to materialize and freeze
84
85
  all optional collections, then `mapCatalogToCompleteSnapshot()` when an
85
86
  adapter-supported family such as views, routines, triggers, partitions,
86
87
  collations, comments, or retained opaque and deferred objects must cross the
87
- strict Snapshot v2 boundary. Snapshot v1 is still selected explicitly by
88
- `mapCatalogToSnapshot()` and remains table-shaped.
88
+ strict Snapshot v1 boundary. `mapCatalogToSnapshot()` delegates to the complete
89
+ mapper, so the canonical result is always Snapshot v1.
89
90
 
90
91
  The result is successful only when Snapshot v1 validation succeeds. A failed
91
92
  result may retain the partial catalog and structured diagnostics, but it has no
@@ -107,9 +108,9 @@ rename. Pass the previous snapshot or an identity hint when a later diff must
107
108
  preserve identity across a rename. See [snapshot diffing](diff.md) for the
108
109
  comparison and hint boundary.
109
110
 
110
- The first version selects one namespace: a PostgreSQL schema, MySQL database,
111
- or SQLite database such as `main`. It does not combine attached databases or
112
- multiple PostgreSQL schemas into one Snapshot v1 value.
111
+ Each Snapshot v1 result selects one namespace: a PostgreSQL schema, MySQL
112
+ database, or SQLite database such as `main`. It does not combine attached
113
+ databases or multiple PostgreSQL schemas into one value.
113
114
 
114
115
  ## Strict and lossy output
115
116
 
@@ -126,12 +127,20 @@ generated expressions, checks, predicates, and expression index terms remain
126
127
  dialect-tagged SQL. Falsy values such as `0`, `false`, `NULL`, and empty
127
128
  strings are preserved.
128
129
 
130
+ References to nested columns, constraints, and indexes include their owning
131
+ table, view, or domain in the complete Snapshot v1 output. References to
132
+ top-level objects remain unscoped, and table-local backing relationships must
133
+ resolve within the table that contains them. Dialect provenance, extensions,
134
+ native storage, and typed expressions are checked against the selected
135
+ snapshot dialect; arbitrary catalog `data` and `configuration` JSON remains
136
+ opaque.
137
+
129
138
  PostgreSQL readers expose views, materialized views, sequences, enums, domains,
130
139
  collations, routines, triggers, policies, partitions, extensions, comments,
131
140
  and ownership as typed complete catalog records. `mapCatalogToCompleteSnapshot`
132
- retains those records in Snapshot v2. The existing `mapCatalogToSnapshot`
133
- mapper still emits the table-only Snapshot v1 and does not fabricate these
134
- objects into tables. If a PostgreSQL catalog row lacks the evidence needed for
141
+ retains those records in Snapshot v1. `mapCatalogToSnapshot()` uses the same
142
+ complete mapping and does not fabricate these objects into tables. If a
143
+ PostgreSQL catalog row lacks the evidence needed for
135
144
  safe normalization, the reader retains a deferred or opaque record and emits a
136
145
  diagnostic.
137
146
 
@@ -154,13 +163,13 @@ collations used by the selected tables or columns, and comments.
154
163
 
155
164
  View definitions come from `INFORMATION_SCHEMA.VIEWS`. The reader cross-
156
165
  references each view with its `INFORMATION_SCHEMA.COLUMNS` rows by physical
157
- table name, so view columns remain attached to the view and Snapshot v2 can
166
+ table name, so view columns remain attached to the view and Snapshot v1 can
158
167
  validate their own column IDs. A missing definition or an unresolved
159
168
  cross-object reference becomes a deferred record with a diagnostic.
160
169
 
161
170
  MySQL scheduled events are kept as `CatalogOpaqueObject` records with their
162
171
  metadata and definition tagged as opaque SQL. The reader emits an
163
- `unmodeled-object` warning, and Snapshot v2 retains the record in
172
+ `unmodeled-object` warning, and Snapshot v1 retains the record in
164
173
  `opaqueObjects` without treating it as a typed routine, trigger, or migration
165
174
  operation.
166
175
 
@@ -177,9 +186,8 @@ The reader keeps that metadata as normalized typed data and never evaluates
177
186
  database-provided SQL. Optional source generation remains a later, pure step
178
187
  with a controlled literal printer.
179
188
 
180
- Use `mapCatalogToCompleteSnapshot()` for the typed MySQL families and its
181
- opaque or deferred boundaries. Use `mapCatalogToSnapshot()` when the caller
182
- needs the table-only Snapshot v1.
189
+ Use `mapCatalogToSnapshot()` or `mapCatalogToCompleteSnapshot()` for the typed
190
+ MySQL families and their opaque or deferred boundaries.
183
191
 
184
192
  ## Diagnostics and safety
185
193
 
@@ -14,10 +14,11 @@ const snapshot = createSchemaSnapshot(appSchema)
14
14
  const json = encodeSchemaSnapshot(snapshot)
15
15
  ```
16
16
 
17
- The v1 envelope contains a format version, an independently versioned dialect
18
- extension, a versioned naming-policy description, an optional namespace, and
19
- arrays of tables. Tables, columns, constraints, and indexes are sorted by
20
- stable logical ID. Physical names are values in the snapshot, not identities:
17
+ The Snapshot v1 envelope contains a format version, an independently versioned
18
+ dialect extension, a versioned naming-policy description, a namespace,
19
+ capability facts, and arrays for every supported object family. Tables,
20
+ columns, constraints, and indexes are sorted by stable logical ID. Physical
21
+ names are values in the snapshot, not identities:
21
22
  changing a physical name does not change the TypeScript field or metadata key.
22
23
 
23
24
  Snapshot data is deliberately not executable Qubu state. Expressions are
@@ -45,6 +46,15 @@ dialect metadata, and broken foreign-key or column references as structured
45
46
  diagnostics. It does not call `process.exit()` and has no runtime validation
46
47
  library dependency.
47
48
 
49
+ References to nested columns, constraints, and indexes carry an explicit
50
+ `owner: { kind, id }` scope. Table columns, constraints, and indexes are owned
51
+ by their table; view columns are owned by their view; and domain constraints
52
+ are owned by their domain. References to top-level objects remain ownerless,
53
+ and the decoder validates each nested scope independently. Dialect metadata is
54
+ checked only in typed snapshot fields. Extension `data`, `configuration`, and
55
+ other opaque JSON payloads are retained as data and are not interpreted as
56
+ typed metadata.
57
+
48
58
  `schemaSnapshotFingerprint()` computes a deterministic content fingerprint from canonical
49
59
  JSON. The fingerprint is useful for cache keys and fixture assertions only. It is not
50
60
  an entity identity, a rename marker, or migration lineage.
@@ -91,15 +101,15 @@ Snapshot serialization remains separate from database introspection,
91
101
  comparison, rename resolution, migration planning, and DDL emission. The
92
102
  optional `qubu/introspection` entrypoint can produce the same canonical
93
103
  Snapshot v1 data from a user-owned catalog connection. The complete normalized
94
- catalog can also be encoded as strict Snapshot v2 with the dedicated
95
- complete-snapshot APIs described in [the catalog model](catalog-model.md).
104
+ catalog can also be encoded with the explicit complete-snapshot APIs described
105
+ in [the catalog model](catalog-model.md).
96
106
  Readers and connection lifecycle do not belong to this pure serialization
97
- layer. Diffing can compare either snapshot version. Resolved diffs feed
107
+ layer. Diffing consumes Snapshot v1. Resolved diffs feed
98
108
  migration plans, and approved plans feed DDL emission. The package-wide
99
109
  [ownership map](../reference/supported-surface.md#ownership-boundary) keeps
100
110
  those pure steps separate from application-owned database execution.
101
111
 
102
112
  The optional [schema source generator](code-generation.md) consumes a complete,
103
113
  non-lossy introspection result and makes its generated schema the next identity
104
- baseline. It does not replace snapshot serialization or generate Snapshot v2
114
+ baseline. It does not replace snapshot serialization or populate non-table
105
115
  object families.
@@ -36,14 +36,19 @@ declaration:
36
36
  import { nativeColumn, nativeStorage, table } from "qubu"
37
37
 
38
38
  const accounts = table("accounts", {
39
- handle: nativeColumn(nativeStorage("postgresql", 'citext COLLATE "C"')),
39
+ handle: nativeColumn(nativeStorage("postgresql", 'citext COLLATE "C"'), {
40
+ sqlType: "postgres.citext",
41
+ }),
40
42
  })
41
43
  ```
42
44
 
43
45
  `nativeStorage()` preserves the declaration text and freezes the descriptor. The
44
46
  `ColumnStorageOf`, `ColumnStorageTypeOf`, `ColumnStorageDialectOf`, and
45
47
  `ColumnStorageDeclarationOf` helpers read its metadata. Native storage is
46
- descriptive. It does not change selection, mutation, or query rendering.
48
+ descriptive. The optional `sqlType` field is the runtime semantic domain passed
49
+ to adapters; provide it for custom domains because the compile-time SQL type is
50
+ not available at runtime. It does not change selection, mutation, or query
51
+ rendering.
47
52
 
48
53
  ## Render deterministic schema expressions
49
54
 
@@ -89,6 +89,14 @@ operation. This contextual typing does not relabel an expression: comparing a
89
89
  `SqlUuid` expression with a `SqlText` expression is still rejected. Cast when
90
90
  the database operation intentionally changes domains:
91
91
 
92
+ Scalar text functions bind primitive operands automatically, and `coalesce()`
93
+ uses its first expression to type primitive fallbacks:
94
+
95
+ ```ts
96
+ upper("Ada") // UPPER(?)
97
+ coalesce(metrics.label, "Anonymous") // COALESCE("metrics"."label", ?)
98
+ ```
99
+
92
100
  ```ts
93
101
  import { cast, like, text } from "qubu"
94
102
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qubu",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/aleclarson/qubu"
@@ -32,6 +32,18 @@
32
32
  "types": "./dist/introspection.d.mts",
33
33
  "import": "./dist/introspection.mjs"
34
34
  },
35
+ "./introspection/mysql": {
36
+ "types": "./dist/introspection/mysql.d.mts",
37
+ "import": "./dist/introspection/mysql.mjs"
38
+ },
39
+ "./introspection/postgres": {
40
+ "types": "./dist/introspection/postgres.d.mts",
41
+ "import": "./dist/introspection/postgres.mjs"
42
+ },
43
+ "./introspection/sqlite": {
44
+ "types": "./dist/introspection/sqlite.d.mts",
45
+ "import": "./dist/introspection/sqlite.mjs"
46
+ },
35
47
  "./mysql": {
36
48
  "types": "./dist/mysql.d.mts",
37
49
  "import": "./dist/mysql.mjs"
@@ -112,6 +124,7 @@
112
124
  "test:type-performance": "node ./scripts/test-type-performance.mjs",
113
125
  "diff-check": "vitest --run test/diff.test.ts && pnpm --filter @qubu/migrate test -- plan.test.ts",
114
126
  "test:bun": "node ./scripts/test-package.mjs --runtime=bun",
127
+ "test:bun-sql": "bun test adapters/bun-sql/test/bun-sql.test.ts",
115
128
  "test:package": "node ./scripts/test-package.mjs",
116
129
  "test:workspace-children": "pnpm run build:workspace-packages && node ./scripts/test-workspace-packages.mjs",
117
130
  "test:workspace-packages": "pnpm run test:package && pnpm run test:workspace-children",