uql-orm 0.21.0 → 0.22.0

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 (142) hide show
  1. package/README.md +5 -4
  2. package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
  3. package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
  4. package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
  5. package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
  6. package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
  7. package/dist/cockroachdb/cockroachDialect.js +33 -18
  8. package/dist/d1/d1Querier.d.ts +4 -3
  9. package/dist/d1/d1Querier.js +0 -3
  10. package/dist/d1/d1SqliteDialect.d.ts +7 -0
  11. package/dist/d1/d1SqliteDialect.js +9 -0
  12. package/dist/dialect/abstractDialect.d.ts +8 -3
  13. package/dist/dialect/abstractDialect.js +9 -4
  14. package/dist/dialect/abstractSqlDialect.d.ts +35 -7
  15. package/dist/dialect/abstractSqlDialect.js +76 -35
  16. package/dist/dialect/index.d.ts +1 -0
  17. package/dist/dialect/index.js +1 -0
  18. package/dist/dialect/indexSqlDialect.d.ts +57 -0
  19. package/dist/dialect/indexSqlDialect.js +101 -0
  20. package/dist/dialect/mysqlLikeSqlDialect.d.ts +16 -1
  21. package/dist/dialect/mysqlLikeSqlDialect.js +32 -1
  22. package/dist/dialect/pgLikeSqlDialect.d.ts +33 -15
  23. package/dist/dialect/pgLikeSqlDialect.js +90 -56
  24. package/dist/dialect/vectorCast.d.ts +21 -0
  25. package/dist/dialect/vectorCast.js +28 -0
  26. package/dist/dialect/vectorSqlDialect.d.ts +10 -3
  27. package/dist/dialect/vectorSqlDialect.js +19 -8
  28. package/dist/entity/decorator/index-decorator.d.ts +2 -9
  29. package/dist/entity/metadata/definition.d.ts +5 -4
  30. package/dist/entity/metadata/definition.js +4 -3
  31. package/dist/libsql/libsqlDialect.d.ts +12 -2
  32. package/dist/libsql/libsqlDialect.js +14 -2
  33. package/dist/libsql/libsqlQuerier.d.ts +9 -22
  34. package/dist/libsql/libsqlQuerier.js +9 -85
  35. package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
  36. package/dist/libsql/libsqlQuerierPool.js +17 -15
  37. package/dist/maria/mariaDialect.d.ts +26 -4
  38. package/dist/maria/mariaDialect.js +54 -15
  39. package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
  40. package/dist/migrate/builder/migrationBuilder.js +58 -76
  41. package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
  42. package/dist/migrate/builder/splitSqlStatements.js +0 -2
  43. package/dist/migrate/builder/tableBuilder.d.ts +12 -2
  44. package/dist/migrate/builder/tableBuilder.js +71 -119
  45. package/dist/migrate/builder/types.d.ts +30 -41
  46. package/dist/migrate/cli.d.ts +3 -2
  47. package/dist/migrate/cli.js +21 -48
  48. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
  49. package/dist/migrate/codegen/entityCodeGenerator.js +5 -63
  50. package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
  51. package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
  52. package/dist/migrate/codegen/index.d.ts +0 -2
  53. package/dist/migrate/codegen/index.js +0 -4
  54. package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
  55. package/dist/migrate/generator/indexNodeToSchema.js +14 -0
  56. package/dist/migrate/generator/mongoCommand.d.ts +57 -0
  57. package/dist/migrate/generator/mongoCommand.js +26 -0
  58. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
  59. package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
  60. package/dist/migrate/index.d.ts +0 -1
  61. package/dist/migrate/index.js +0 -1
  62. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +25 -10
  63. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +46 -32
  64. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
  65. package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
  66. package/dist/migrate/introspection/mongoIntrospector.js +2 -2
  67. package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
  68. package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
  69. package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
  70. package/dist/migrate/introspection/postgresIntrospector.js +4 -10
  71. package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
  72. package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
  73. package/dist/migrate/migrator.d.ts +23 -3
  74. package/dist/migrate/migrator.js +57 -69
  75. package/dist/migrate/schemaGenerator.d.ts +26 -37
  76. package/dist/migrate/schemaGenerator.js +69 -174
  77. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
  78. package/dist/migrate/schemaGeneratorAsync.js +3 -3
  79. package/dist/migrate/storage/databaseStorage.js +4 -4
  80. package/dist/mongo/mongoDialect.d.ts +2 -0
  81. package/dist/mongo/mongoDialect.js +14 -3
  82. package/dist/mysql/mysqlDialect.d.ts +8 -0
  83. package/dist/mysql/mysqlDialect.js +13 -0
  84. package/dist/schema/canonicalType.d.ts +14 -2
  85. package/dist/schema/canonicalType.js +41 -31
  86. package/dist/schema/index.d.ts +1 -1
  87. package/dist/schema/schemaASTBuilder.d.ts +8 -1
  88. package/dist/schema/schemaASTBuilder.js +56 -47
  89. package/dist/schema/types.d.ts +10 -37
  90. package/dist/sqlite/abstractSqliteQuerier.d.ts +39 -0
  91. package/dist/sqlite/abstractSqliteQuerier.js +44 -0
  92. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
  93. package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
  94. package/dist/sqlite/hranaQuerier.d.ts +54 -0
  95. package/dist/sqlite/hranaQuerier.js +90 -0
  96. package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
  97. package/dist/sqlite/hranaQuerierPool.js +25 -0
  98. package/dist/sqlite/index.d.ts +2 -1
  99. package/dist/sqlite/index.js +2 -1
  100. package/dist/sqlite/sqliteDialect.d.ts +18 -4
  101. package/dist/sqlite/sqliteDialect.js +24 -24
  102. package/dist/sqlite/sqliteQuerier.d.ts +16 -9
  103. package/dist/sqlite/sqliteQuerier.js +4 -27
  104. package/dist/sqlite/sqliteQuerierPool.d.ts +14 -4
  105. package/dist/sqlite/sqliteQuerierPool.js +16 -5
  106. package/dist/turso/index.d.ts +3 -0
  107. package/dist/turso/index.js +3 -0
  108. package/dist/turso/local.d.ts +3 -0
  109. package/dist/turso/local.js +3 -0
  110. package/dist/turso/tursoDialect.d.ts +15 -0
  111. package/dist/turso/tursoDialect.js +18 -0
  112. package/dist/turso/tursoLocalQuerier.d.ts +25 -0
  113. package/dist/turso/tursoLocalQuerier.js +20 -0
  114. package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
  115. package/dist/turso/tursoLocalQuerierPool.js +39 -0
  116. package/dist/turso/tursoQuerier.d.ts +10 -0
  117. package/dist/turso/tursoQuerier.js +10 -0
  118. package/dist/turso/tursoQuerierPool.d.ts +39 -0
  119. package/dist/turso/tursoQuerierPool.js +38 -0
  120. package/dist/type/config.d.ts +0 -6
  121. package/dist/type/dialect.d.ts +12 -4
  122. package/dist/type/dialect.js +7 -1
  123. package/dist/type/entity.d.ts +66 -5
  124. package/dist/type/migration.d.ts +32 -23
  125. package/dist/type/vector.d.ts +5 -2
  126. package/dist/util/index.d.ts +1 -0
  127. package/dist/util/index.js +1 -0
  128. package/dist/util/indexColumn.util.d.ts +6 -0
  129. package/dist/util/indexColumn.util.js +26 -0
  130. package/package.json +16 -3
  131. package/dist/migrate/codegen/entityMerger.d.ts +0 -114
  132. package/dist/migrate/codegen/entityMerger.js +0 -293
  133. package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
  134. package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
  135. package/dist/migrate/generator/index.d.ts +0 -2
  136. package/dist/migrate/generator/index.js +0 -2
  137. package/dist/migrate/sync/index.d.ts +0 -6
  138. package/dist/migrate/sync/index.js +0 -6
  139. package/dist/migrate/sync/schemaSync.d.ts +0 -131
  140. package/dist/migrate/sync/schemaSync.js +0 -259
  141. package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
  142. package/dist/sqlite/betterSqlite3Dialect.js +0 -9
@@ -9,7 +9,7 @@
9
9
  // ============================================================================
10
10
  // Vector Category Helpers
11
11
  // ============================================================================
12
- /** Type guard for all vector-family categories. */
12
+ /** Whether a category is one of the vector types, narrowing it to the cast pgvector names use. */
13
13
  export function isVectorCategory(category) {
14
14
  return category === 'vector' || category === 'halfvec' || category === 'sparsevec';
15
15
  }
@@ -86,7 +86,15 @@ const SQL_TO_CANONICAL = {
86
86
  halfvec: { category: 'halfvec' },
87
87
  sparsevec: { category: 'sparsevec' },
88
88
  };
89
- const PG_TYPE_MAP = {
89
+ /**
90
+ * pgvector is the only engine with three vector column types, so every other dialect maps all three
91
+ * canonical categories onto the single type it does have (see {@link MULTI_VECTOR_TYPE_DIALECTS}, the
92
+ * dialect-side half of the same fact).
93
+ */
94
+ function withVectorType(scalars, vector) {
95
+ return { ...scalars, vector, halfvec: vector, sparsevec: vector };
96
+ }
97
+ const PG_SCALAR_MAP = {
90
98
  integer: 'INTEGER',
91
99
  float: 'REAL',
92
100
  decimal: 'NUMERIC',
@@ -98,22 +106,18 @@ const PG_TYPE_MAP = {
98
106
  json: 'JSONB',
99
107
  uuid: 'UUID',
100
108
  blob: 'BYTEA',
109
+ };
110
+ const PG_TYPE_MAP = {
111
+ ...PG_SCALAR_MAP,
101
112
  vector: 'VECTOR',
102
113
  halfvec: 'HALFVEC',
103
114
  sparsevec: 'SPARSEVEC',
104
115
  };
105
- // CockroachDB's VECTOR type is native (no extension), but it has no HALFVEC/SPARSEVEC types at
106
- // all - verified live: `CREATE TABLE t (v HALFVEC(3))` is a syntax error, not just unsupported at
107
- // runtime. Falls back to VECTOR, same as MariaDB (see its own halfvec/sparsevec below).
108
- const CRDB_TYPE_MAP = {
109
- ...PG_TYPE_MAP,
110
- halfvec: 'VECTOR',
111
- sparsevec: 'VECTOR',
112
- };
113
116
  const CANONICAL_TO_SQL = {
114
117
  postgres: PG_TYPE_MAP,
115
- cockroachdb: CRDB_TYPE_MAP,
116
- mysql: {
118
+ // CockroachDB's VECTOR is native, no extension needed.
119
+ cockroachdb: withVectorType(PG_SCALAR_MAP, 'VECTOR'),
120
+ mysql: withVectorType({
117
121
  integer: 'INT',
118
122
  float: 'FLOAT',
119
123
  decimal: 'DECIMAL',
@@ -125,11 +129,11 @@ const CANONICAL_TO_SQL = {
125
129
  json: 'JSON',
126
130
  uuid: 'CHAR(36)',
127
131
  blob: 'BLOB',
128
- vector: 'JSON', // MySQL doesn't have native vector type
129
- halfvec: 'JSON',
130
- sparsevec: 'JSON',
131
- },
132
- sqlite: {
132
+ },
133
+ // MySQL 9.x does have a `VECTOR` type, but no distance function outside HeatWave and no vector
134
+ // index, so JSON keeps the column queryable with the JSON operators and needs no conversion.
135
+ 'JSON'),
136
+ sqlite: withVectorType({
133
137
  integer: 'INTEGER',
134
138
  float: 'REAL',
135
139
  decimal: 'REAL',
@@ -141,11 +145,8 @@ const CANONICAL_TO_SQL = {
141
145
  json: 'TEXT',
142
146
  uuid: 'TEXT',
143
147
  blob: 'BLOB',
144
- vector: 'TEXT',
145
- halfvec: 'TEXT',
146
- sparsevec: 'TEXT',
147
- },
148
- mariadb: {
148
+ }, 'TEXT'),
149
+ mariadb: withVectorType({
149
150
  integer: 'INT',
150
151
  float: 'FLOAT',
151
152
  decimal: 'DECIMAL',
@@ -157,12 +158,9 @@ const CANONICAL_TO_SQL = {
157
158
  json: 'JSON',
158
159
  uuid: 'CHAR(36)',
159
160
  blob: 'BLOB',
160
- vector: 'VECTOR',
161
- halfvec: 'VECTOR', // MariaDB only supports VECTOR; no native halfvec/sparsevec
162
- sparsevec: 'VECTOR',
163
- },
161
+ }, 'VECTOR'),
164
162
  // MongoDB uses BSON types, not SQL types. These are placeholders for compatibility.
165
- mongodb: {
163
+ mongodb: withVectorType({
166
164
  integer: 'int',
167
165
  float: 'double',
168
166
  decimal: 'decimal128',
@@ -174,10 +172,7 @@ const CANONICAL_TO_SQL = {
174
172
  json: 'object',
175
173
  uuid: 'binData',
176
174
  blob: 'binData',
177
- vector: 'array',
178
- halfvec: 'array',
179
- sparsevec: 'array',
180
- },
175
+ }, 'array'),
181
176
  };
182
177
  /**
183
178
  * Size variant modifiers for SQL types.
@@ -318,6 +313,21 @@ export function sqlToCanonical(sqlType) {
318
313
  }
319
314
  return result;
320
315
  }
316
+ /**
317
+ * The canonical type for a column an engine reported, merging the metadata columns it reports beside
318
+ * the type name (`character_maximum_length` and friends) over whatever the name itself carried. This is
319
+ * the one place a SQL type string is parsed: everything downstream compares and renders canonical
320
+ * types, so no consumer has to know that `TINYINT(1)` means boolean on MySQL.
321
+ */
322
+ export function canonicalColumnType(sqlType, reported = {}) {
323
+ const base = sqlToCanonical(sqlType);
324
+ return {
325
+ ...base,
326
+ length: reported.length ?? base.length,
327
+ precision: reported.precision ?? base.precision,
328
+ scale: reported.scale ?? base.scale,
329
+ };
330
+ }
321
331
  /**
322
332
  * Convert a canonical type to a SQL type string for a specific dialect instance.
323
333
  */
@@ -22,4 +22,4 @@ export type { BuildFromEntitiesOptions } from './schemaASTBuilder.js';
22
22
  export { SchemaASTBuilder } from './schemaASTBuilder.js';
23
23
  export type { DiffOptions } from './schemaASTDiffer.js';
24
24
  export { diffSchemas, SchemaASTDiffer } from './schemaASTDiffer.js';
25
- export type { CanonicalType, ColumnDiff, ColumnNode, ConflictResolution, DetectedRelation, Drift, DriftReport, DriftSeverity, DriftStatus, DriftType, ForeignKeyAction, IndexDiff, IndexNode, IndexSource, IndexSyncStatus, IndexType, RelationshipDiff, RelationshipNode, RelationshipSource, RelationshipType, SchemaAST as ISchemaAST, SchemaDiffResult, SizeVariant, SyncDirection, SyncOptions, TableDiff, TableNode, TypeCategory, ValidationError, ValidationErrorType, } from './types.js';
25
+ export type { CanonicalType, ColumnDiff, ColumnNode, DetectedRelation, Drift, DriftReport, DriftSeverity, DriftStatus, DriftType, ForeignKeyAction, IndexDiff, IndexNode, IndexSource, IndexSyncStatus, IndexType, RelationshipDiff, RelationshipNode, RelationshipSource, RelationshipType, SchemaAST as ISchemaAST, SchemaDiffResult, SizeVariant, TableDiff, TableNode, TypeCategory, ValidationError, ValidationErrorType, } from './types.js';
@@ -71,7 +71,14 @@ export declare class SchemaASTBuilder {
71
71
  */
72
72
  private addRelationshipsFromEntity;
73
73
  /**
74
- * Add indexes from field options.
74
+ * Add indexes from field options (`@Field({ index })`) and from `@Index([...])`, which have nothing
75
+ * in common beyond their target table.
75
76
  */
76
77
  private addIndexesFromEntity;
78
+ /**
79
+ * One `@Index([...])`. Its entries keep the authored form (expression, prefix length, order) with
80
+ * names resolved, so the generator renders exactly what was declared; `columns` is the resolvable
81
+ * subset, which is what diffing and introspection compare.
82
+ */
83
+ private addCompositeIndex;
77
84
  }
@@ -160,8 +160,12 @@ export class SchemaASTBuilder {
160
160
  const relatedTable = this.ast.getTable(relatedTableName);
161
161
  if (!relatedTable)
162
162
  continue;
163
- // Only create FK for owning side (m1 and owner side of 11)
164
- if (relation.cardinality === 'm1' || (relation.cardinality === '11' && relation.references)) {
163
+ // Only the owning side gets the FK. `mappedBy` marks the inverse side of a one-to-one, whose
164
+ // `references` describe how to join back (its own primary key against the owner's FK column) -
165
+ // reading those as a foreign key emitted a reversed constraint (`User(id) REFERENCES
166
+ // user_profile(creatorId)`), which SQLite rejects outright as a foreign key mismatch.
167
+ const ownsForeignKey = relation.cardinality === 'm1' || (relation.cardinality === '11' && !!relation.references && !relation.mappedBy);
168
+ if (ownsForeignKey) {
165
169
  const references = relation.references ?? [{ local: `${key}Id`, foreign: relatedMeta.id }];
166
170
  const localPropName = references[0].local;
167
171
  const foreignPropName = references[0].foreign;
@@ -192,67 +196,72 @@ export class SchemaASTBuilder {
192
196
  }
193
197
  }
194
198
  /**
195
- * Add indexes from field options.
199
+ * Add indexes from field options (`@Field({ index })`) and from `@Index([...])`, which have nothing
200
+ * in common beyond their target table.
196
201
  */
197
202
  addIndexesFromEntity(entity, meta, resolveTableName, resolveColumnName) {
198
203
  const tableName = resolveTableName(entity, meta);
199
204
  const table = this.ast.getTable(tableName);
200
205
  if (!table)
201
206
  return;
202
- // 1. Single column indexes from @Field({ index: true })
203
- const indexFields = meta.fields;
204
- for (const key of Object.keys(indexFields)) {
205
- const field = indexFields[key];
207
+ for (const key of Object.keys(meta.fields)) {
208
+ const field = meta.fields[key];
206
209
  if (!field?.index)
207
210
  continue;
208
- const columnName = resolveColumnName(key, field);
209
- const column = table.columns.get(columnName);
211
+ const column = table.columns.get(resolveColumnName(key, field));
210
212
  if (!column)
211
213
  continue;
212
- const indexName = typeof field.index === 'string' ? field.index : `idx_${tableName}_${columnName}`;
213
- const indexNode = {
214
- name: indexName,
214
+ this.ast.addIndex({
215
+ name: typeof field.index === 'string' ? field.index : `idx_${tableName}_${column.name}`,
215
216
  table,
216
217
  columns: [column],
217
218
  unique: field.unique ?? false,
218
219
  source: 'entity',
219
220
  syncStatus: 'entity_only',
220
- };
221
- this.ast.addIndex(indexNode);
221
+ });
222
222
  }
223
- // 2. Composite indexes from @Index([...])
224
- if (meta.indexes) {
225
- for (const idxMeta of meta.indexes) {
226
- const columns = [];
227
- for (const propName of idxMeta.columns) {
228
- const field = meta.fields[propName];
229
- if (!field)
230
- continue;
231
- const colName = resolveColumnName(propName, field);
232
- const column = table.columns.get(colName);
233
- if (column) {
234
- columns.push(column);
235
- }
236
- }
237
- if (columns.length > 0) {
238
- const indexName = idxMeta.name ?? `idx_${tableName}_${columns.map((c) => c.name).join('_')}`;
239
- const indexNode = {
240
- name: indexName,
241
- table,
242
- columns,
243
- unique: idxMeta.unique ?? false,
244
- type: idxMeta.type,
245
- where: idxMeta.where,
246
- distance: idxMeta.distance,
247
- m: idxMeta.m,
248
- efConstruction: idxMeta.efConstruction,
249
- lists: idxMeta.lists,
250
- source: 'entity',
251
- syncStatus: 'entity_only',
252
- };
253
- this.ast.addIndex(indexNode);
254
- }
255
- }
223
+ for (const idxMeta of meta.indexes ?? []) {
224
+ this.addCompositeIndex(table, meta, idxMeta, resolveColumnName);
256
225
  }
257
226
  }
227
+ /**
228
+ * One `@Index([...])`. Its entries keep the authored form (expression, prefix length, order) with
229
+ * names resolved, so the generator renders exactly what was declared; `columns` is the resolvable
230
+ * subset, which is what diffing and introspection compare.
231
+ */
232
+ addCompositeIndex(table, meta, idxMeta, resolveColumnName) {
233
+ // An entry survives if it is an expression (nothing to resolve) or names a column that exists;
234
+ // an index left with none is dropped, the same as one naming only unknown columns always was.
235
+ const entries = idxMeta.columns
236
+ .map((entry) => {
237
+ if (entry.expression)
238
+ return entry;
239
+ const field = meta.fields[entry.column];
240
+ const column = field && resolveColumnName(entry.column, field);
241
+ return column && table.columns.has(column) ? { ...entry, column } : undefined;
242
+ })
243
+ .filter((entry) => entry !== undefined);
244
+ if (!entries.length)
245
+ return;
246
+ const columns = entries
247
+ .map((entry) => (entry.expression ? undefined : table.columns.get(entry.column)))
248
+ .filter((column) => column !== undefined);
249
+ const named = columns.length ? columns.map((column) => column.name) : entries.map((_, at) => `expr${at}`);
250
+ this.ast.addIndex({
251
+ name: idxMeta.name ?? `idx_${table.name}_${named.join('_')}`,
252
+ table,
253
+ columns,
254
+ entries,
255
+ include: idxMeta.include,
256
+ unique: idxMeta.unique ?? false,
257
+ type: idxMeta.type,
258
+ where: idxMeta.where,
259
+ distance: idxMeta.distance,
260
+ m: idxMeta.m,
261
+ efConstruction: idxMeta.efConstruction,
262
+ lists: idxMeta.lists,
263
+ source: 'entity',
264
+ syncStatus: 'entity_only',
265
+ });
266
+ }
258
267
  }
@@ -4,7 +4,8 @@
4
4
  * A unified graph representation of database schema with relationships as first-class citizens.
5
5
  * Enables reliable diffing, smart relation detection, and dialect-agnostic schema operations.
6
6
  */
7
- import type { VectorIndexOptions } from '../type/vector.js';
7
+ import type { IndexColumnSchema } from '../type/entity.js';
8
+ import type { IndexSchema } from '../type/migration.js';
8
9
  /**
9
10
  * Type categories universal across SQL dialects.
10
11
  * These represent logical/semantic types, not specific SQL types.
@@ -148,24 +149,21 @@ export interface RelationshipNode {
148
149
  * Index node in the schema graph.
149
150
  * Represents a database index on one or more columns.
150
151
  */
151
- export type IndexNode = {
152
- /** Index name */
153
- readonly name: string;
152
+ export type IndexNode = Omit<IndexSchema, 'columns'> & {
154
153
  /** Reference to the table this index belongs to */
155
154
  readonly table: TableNode;
156
- /** Columns included in the index (order matters) */
155
+ /** Columns included in the index (order matters). Empty for an index over expressions only. */
157
156
  readonly columns: ColumnNode[];
158
- /** Whether this is a unique index */
159
- readonly unique: boolean;
160
- /** Index algorithm/type */
161
- readonly type?: IndexType;
162
- /** Partial index condition (WHERE clause) */
163
- readonly where?: string;
157
+ /**
158
+ * The index as authored, with names resolved: expressions, prefix lengths and per-column order that
159
+ * a `ColumnNode` cannot represent. The generator renders these; `columns` is what diffing compares.
160
+ */
161
+ readonly entries?: readonly IndexColumnSchema[];
164
162
  /** Where this index was defined */
165
163
  readonly source?: IndexSource;
166
164
  /** Current sync status */
167
165
  readonly syncStatus?: IndexSyncStatus;
168
- } & VectorIndexOptions;
166
+ };
169
167
  /**
170
168
  * Root of the schema graph.
171
169
  * Contains all tables, relationships, and provides graph operations.
@@ -320,28 +318,3 @@ export interface DriftReport {
320
318
  readonly info: number;
321
319
  };
322
320
  }
323
- /**
324
- * Direction for schema synchronization.
325
- */
326
- export type SyncDirection = 'bidirectional' | 'entity-to-db' | 'db-to-entity';
327
- /**
328
- * Strategy for resolving conflicts in bidirectional sync.
329
- */
330
- export type ConflictResolution = 'prompt' | 'entity-wins' | 'db-wins' | 'skip';
331
- /**
332
- * Options for sync operations.
333
- */
334
- export interface SyncOptions {
335
- /** Direction of synchronization */
336
- readonly direction?: SyncDirection;
337
- /** How to resolve conflicts (for bidirectional) */
338
- readonly conflictResolution?: ConflictResolution;
339
- /** Prevent destructive changes (DROP, type narrowing) */
340
- readonly safe?: boolean;
341
- /** Include indexes in sync */
342
- readonly includeIndexes?: boolean;
343
- /** Include foreign keys in sync */
344
- readonly includeForeignKeys?: boolean;
345
- /** Dry run - report changes without applying */
346
- readonly dryRun?: boolean;
347
- }
@@ -1,3 +1,42 @@
1
1
  import { AbstractSqlQuerier } from '../querier/index.js';
2
+ /** Values every SQLite driver accepts as a bound parameter. */
3
+ export type SqliteBindValue = null | string | number | bigint | boolean | Uint8Array;
4
+ /** Header a SQLite driver returns for a statement without a `RETURNING` clause. */
5
+ export type SqliteRunResult = {
6
+ changes: number;
7
+ lastInsertRowid: number | bigint;
8
+ };
9
+ /** Bound parameters reach a driver as `unknown[]` from the compiler; every driver types them narrowly. */
10
+ export declare function toSqliteBindValues(values?: unknown[]): SqliteBindValue[];
11
+ /**
12
+ * A prepared statement from a driver with better-sqlite3 semantics. `better-sqlite3` and `bun:sqlite`
13
+ * answer synchronously, `@tursodatabase/database` with promises, and {@link PreparedSqliteQuerier}
14
+ * awaits either.
15
+ */
16
+ export type SqlitePreparedStatement = {
17
+ /** True for any statement returning rows, including one with a `RETURNING` clause. */
18
+ readonly reader: boolean;
19
+ all(...values: SqliteBindValue[]): unknown[] | Promise<unknown[]>;
20
+ run(...values: SqliteBindValue[]): SqliteRunResult | Promise<SqliteRunResult>;
21
+ iterate(...values: SqliteBindValue[]): Iterable<unknown> | AsyncIterable<unknown>;
22
+ };
2
23
  export declare abstract class AbstractSqliteQuerier extends AbstractSqlQuerier {
24
+ /**
25
+ * SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
26
+ * a querier returns nothing; it only asserts the unit of work was finished. Drivers owning a
27
+ * closable per-querier connection override this.
28
+ */
29
+ internalRelease(): Promise<void>;
30
+ }
31
+ /**
32
+ * Querier for the SQLite drivers that expose prepared statements: `better-sqlite3`, `bun:sqlite`
33
+ * (through `adaptBunSqlite`) and the embedded Turso engine. They differ only in whether preparing and
34
+ * stepping are synchronous, which `await` and `for await` absorb, so the read/write/stream logic -
35
+ * including the `reader` rule below, whose loss silently drops inserted ids - is written once.
36
+ */
37
+ export declare abstract class PreparedSqliteQuerier extends AbstractSqliteQuerier {
38
+ protected abstract prepare(query: string): SqlitePreparedStatement | Promise<SqlitePreparedStatement>;
39
+ internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
40
+ internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, unknown>;
41
+ internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
3
42
  }
@@ -1,3 +1,47 @@
1
1
  import { AbstractSqlQuerier } from '../querier/index.js';
2
+ import { throwPendingTransaction } from '../util/index.js';
3
+ /** Bound parameters reach a driver as `unknown[]` from the compiler; every driver types them narrowly. */
4
+ export function toSqliteBindValues(values) {
5
+ return (values || []);
6
+ }
2
7
  export class AbstractSqliteQuerier extends AbstractSqlQuerier {
8
+ /**
9
+ * SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
10
+ * a querier returns nothing; it only asserts the unit of work was finished. Drivers owning a
11
+ * closable per-querier connection override this.
12
+ */
13
+ async internalRelease() {
14
+ if (this.hasOpenTransaction) {
15
+ throwPendingTransaction();
16
+ }
17
+ }
18
+ }
19
+ /**
20
+ * Querier for the SQLite drivers that expose prepared statements: `better-sqlite3`, `bun:sqlite`
21
+ * (through `adaptBunSqlite`) and the embedded Turso engine. They differ only in whether preparing and
22
+ * stepping are synchronous, which `await` and `for await` absorb, so the read/write/stream logic -
23
+ * including the `reader` rule below, whose loss silently drops inserted ids - is written once.
24
+ */
25
+ export class PreparedSqliteQuerier extends AbstractSqliteQuerier {
26
+ async internalAll(query, values) {
27
+ const stmt = await this.prepare(query);
28
+ return (await stmt.all(...toSqliteBindValues(values)));
29
+ }
30
+ async *internalStream(query, values) {
31
+ const stmt = await this.prepare(query);
32
+ for await (const row of stmt.iterate(...toSqliteBindValues(values))) {
33
+ yield row;
34
+ }
35
+ }
36
+ async internalRun(query, values) {
37
+ const stmt = await this.prepare(query);
38
+ // `reader` is true for any statement with a RETURNING clause; `.run()` silently discards
39
+ // returned rows, so those statements must go through `.all()` instead.
40
+ if (stmt.reader) {
41
+ const rows = (await stmt.all(...toSqliteBindValues(values)));
42
+ return this.buildUpdateResult({ rows });
43
+ }
44
+ const { changes, lastInsertRowid } = await stmt.run(...toSqliteBindValues(values));
45
+ return this.buildUpdateResult({ changes, id: lastInsertRowid });
46
+ }
3
47
  }
@@ -0,0 +1,26 @@
1
+ import type { SqliteBindValue, SqliteRunResult } from './abstractSqliteQuerier.js';
2
+ import type { SqliteDatabase } from './sqliteQuerier.js';
3
+ /** A `bun:sqlite` statement: better-sqlite3-shaped, except it reports columns instead of `reader`. */
4
+ type BunStatement = {
5
+ columnNames: string[];
6
+ all(...values: SqliteBindValue[]): unknown[];
7
+ run(...values: SqliteBindValue[]): SqliteRunResult;
8
+ iterate(...values: SqliteBindValue[]): Iterable<unknown>;
9
+ };
10
+ type BunDatabase = {
11
+ prepare(sql: string): BunStatement;
12
+ loadExtension(path: string): void;
13
+ close(): unknown;
14
+ };
15
+ /**
16
+ * Presents a `bun:sqlite` handle as a {@link SqliteDatabase}.
17
+ *
18
+ * @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
19
+ * would take the `run()` path, which discards returned rows, and inserts would report no ids.
20
+ * `columnNames` is non-empty for exactly the statements better-sqlite3 marks as readers.
21
+ *
22
+ * Lives in a `.bun.ts` file because it only ever executes under Bun: the Node coverage run cannot
23
+ * reach it, and `sqliteQuerier.bun.test.ts` covers it under `test:bun` instead.
24
+ */
25
+ export declare function adaptBunSqlite(db: BunDatabase): SqliteDatabase;
26
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Presents a `bun:sqlite` handle as a {@link SqliteDatabase}.
3
+ *
4
+ * @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
5
+ * would take the `run()` path, which discards returned rows, and inserts would report no ids.
6
+ * `columnNames` is non-empty for exactly the statements better-sqlite3 marks as readers.
7
+ *
8
+ * Lives in a `.bun.ts` file because it only ever executes under Bun: the Node coverage run cannot
9
+ * reach it, and `sqliteQuerier.bun.test.ts` covers it under `test:bun` instead.
10
+ */
11
+ export function adaptBunSqlite(db) {
12
+ return {
13
+ prepare: (sql) => {
14
+ const stmt = db.prepare(sql);
15
+ return {
16
+ reader: stmt.columnNames.length > 0,
17
+ all: (...values) => stmt.all(...values),
18
+ run: (...values) => stmt.run(...values),
19
+ iterate: (...values) => stmt.iterate(...values),
20
+ };
21
+ },
22
+ loadExtension: (path) => db.loadExtension(path),
23
+ close: () => db.close(),
24
+ };
25
+ }
@@ -0,0 +1,54 @@
1
+ import type { ExtraOptions, TransactionOptions } from '../type/index.js';
2
+ import { AbstractSqliteQuerier, type SqliteBindValue } from './abstractSqliteQuerier.js';
3
+ import type { SqliteDialect } from './sqliteDialect.js';
4
+ /**
5
+ * Structural subset of the Hrana client API, the wire protocol shared by `@libsql/client` and
6
+ * `@tursodatabase/serverless/compat`. Declared here rather than imported so a querier works with
7
+ * any client of this shape (including `@libsql/client/web` and `@libsql/client-wasm`) without
8
+ * depending on vendor types.
9
+ */
10
+ export type HranaInValue = SqliteBindValue | ArrayBuffer | Date;
11
+ export type HranaResultSet = {
12
+ rows: unknown[];
13
+ rowsAffected: number;
14
+ lastInsertRowid?: bigint;
15
+ };
16
+ export type HranaExecutor = {
17
+ execute(stmt: {
18
+ sql: string;
19
+ args?: HranaInValue[];
20
+ }): Promise<HranaResultSet>;
21
+ };
22
+ export type HranaTransaction = HranaExecutor & {
23
+ commit(): Promise<void>;
24
+ rollback(): Promise<void>;
25
+ };
26
+ export type HranaClient = HranaExecutor & {
27
+ transaction(mode?: 'write' | 'read' | 'deferred'): Promise<HranaTransaction>;
28
+ close(): void;
29
+ };
30
+ /** Connection lifecycle for a {@link HranaQuerier} (separate from {@link ExtraOptions}). */
31
+ export type HranaQuerierConnectionOptions = {
32
+ /** When set, {@link release} closes {@link HranaQuerier.client} (one-shot migration connections). */
33
+ closeClientOnRelease?: boolean;
34
+ };
35
+ /**
36
+ * Querier for SQLite databases reached through a Hrana client.
37
+ *
38
+ * @remarks Transactions use the client's own session handle rather than `BEGIN`/`COMMIT` statements,
39
+ * because over plain HTTP consecutive requests need not share a connection.
40
+ */
41
+ export declare class HranaQuerier extends AbstractSqliteQuerier {
42
+ readonly client: HranaClient;
43
+ readonly extra?: ExtraOptions | undefined;
44
+ private tx?;
45
+ private readonly closeClientOnRelease;
46
+ constructor(client: HranaClient, dialect: SqliteDialect, extra?: ExtraOptions | undefined, connection?: HranaQuerierConnectionOptions);
47
+ internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
48
+ internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
49
+ get hasOpenTransaction(): boolean;
50
+ beginTransaction(_opts?: TransactionOptions): Promise<void>;
51
+ commitTransaction(): Promise<void>;
52
+ rollbackTransaction(): Promise<void>;
53
+ internalRelease(): Promise<void>;
54
+ }
@@ -0,0 +1,90 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Serialized } from '../querier/decorator/index.js';
11
+ import { throwNoPendingTransaction, throwPendingTransaction } from '../util/index.js';
12
+ import { AbstractSqliteQuerier } from './abstractSqliteQuerier.js';
13
+ /**
14
+ * Querier for SQLite databases reached through a Hrana client.
15
+ *
16
+ * @remarks Transactions use the client's own session handle rather than `BEGIN`/`COMMIT` statements,
17
+ * because over plain HTTP consecutive requests need not share a connection.
18
+ */
19
+ export class HranaQuerier extends AbstractSqliteQuerier {
20
+ client;
21
+ extra;
22
+ tx;
23
+ closeClientOnRelease;
24
+ constructor(client, dialect, extra, connection) {
25
+ super(dialect, extra);
26
+ this.client = client;
27
+ this.extra = extra;
28
+ this.closeClientOnRelease = connection?.closeClientOnRelease ?? false;
29
+ }
30
+ async internalAll(query, values) {
31
+ const target = this.tx || this.client;
32
+ const res = await target.execute({ sql: query, args: values });
33
+ return res.rows;
34
+ }
35
+ async internalRun(query, values) {
36
+ const target = this.tx || this.client;
37
+ const res = await target.execute({ sql: query, args: values });
38
+ const rows = res.rows;
39
+ // `rowsAffected` is unreliably 0 whenever the statement has a RETURNING clause, so prefer
40
+ // the actual row count when rows were returned.
41
+ return this.buildUpdateResult({ rows, changes: rows.length || res.rowsAffected, id: res.lastInsertRowid });
42
+ }
43
+ get hasOpenTransaction() {
44
+ return !!this.tx;
45
+ }
46
+ async beginTransaction(_opts) {
47
+ if (this.tx) {
48
+ throwPendingTransaction();
49
+ }
50
+ this.tx = await this.client.transaction('write');
51
+ }
52
+ async commitTransaction() {
53
+ if (!this.tx) {
54
+ throwNoPendingTransaction();
55
+ }
56
+ await this.tx.commit();
57
+ this.tx = undefined;
58
+ }
59
+ async rollbackTransaction() {
60
+ if (!this.tx) {
61
+ throwNoPendingTransaction();
62
+ }
63
+ await this.tx.rollback();
64
+ this.tx = undefined;
65
+ }
66
+ async internalRelease() {
67
+ await super.internalRelease();
68
+ if (this.closeClientOnRelease) {
69
+ this.client.close();
70
+ }
71
+ }
72
+ }
73
+ __decorate([
74
+ Serialized(),
75
+ __metadata("design:type", Function),
76
+ __metadata("design:paramtypes", [Object]),
77
+ __metadata("design:returntype", Promise)
78
+ ], HranaQuerier.prototype, "beginTransaction", null);
79
+ __decorate([
80
+ Serialized(),
81
+ __metadata("design:type", Function),
82
+ __metadata("design:paramtypes", []),
83
+ __metadata("design:returntype", Promise)
84
+ ], HranaQuerier.prototype, "commitTransaction", null);
85
+ __decorate([
86
+ Serialized(),
87
+ __metadata("design:type", Function),
88
+ __metadata("design:paramtypes", []),
89
+ __metadata("design:returntype", Promise)
90
+ ], HranaQuerier.prototype, "rollbackTransaction", null);