uql-orm 0.21.0 → 0.23.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 (214) hide show
  1. package/README.md +5 -4
  2. package/dist/browser/uql-browser.min.js.map +3 -3
  3. package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
  4. package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
  5. package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
  6. package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
  7. package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
  8. package/dist/cockroachdb/cockroachDialect.js +33 -18
  9. package/dist/d1/d1Querier.d.ts +4 -3
  10. package/dist/d1/d1Querier.js +0 -3
  11. package/dist/d1/d1SqliteDialect.d.ts +7 -0
  12. package/dist/d1/d1SqliteDialect.js +9 -0
  13. package/dist/dialect/abstractDialect.d.ts +8 -3
  14. package/dist/dialect/abstractDialect.js +9 -4
  15. package/dist/dialect/abstractSqlDialect.d.ts +49 -8
  16. package/dist/dialect/abstractSqlDialect.js +95 -47
  17. package/dist/dialect/index.d.ts +1 -0
  18. package/dist/dialect/index.js +1 -0
  19. package/dist/dialect/indexSqlDialect.d.ts +57 -0
  20. package/dist/dialect/indexSqlDialect.js +101 -0
  21. package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
  22. package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
  23. package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
  24. package/dist/dialect/pgLikeSqlDialect.js +97 -68
  25. package/dist/dialect/vectorCast.d.ts +21 -0
  26. package/dist/dialect/vectorCast.js +28 -0
  27. package/dist/dialect/vectorSqlDialect.d.ts +10 -3
  28. package/dist/dialect/vectorSqlDialect.js +19 -8
  29. package/dist/entity/decorator/bag.d.ts +35 -0
  30. package/dist/entity/decorator/bag.js +54 -0
  31. package/dist/entity/decorator/entity.d.ts +28 -2
  32. package/dist/entity/decorator/entity.js +40 -2
  33. package/dist/entity/decorator/members.d.ts +51 -0
  34. package/dist/entity/decorator/members.js +51 -0
  35. package/dist/entity/index.d.ts +3 -1
  36. package/dist/entity/index.js +3 -1
  37. package/dist/entity/metadata/definition.d.ts +23 -8
  38. package/dist/entity/metadata/definition.js +56 -100
  39. package/dist/http/contract.js +1 -2
  40. package/dist/http/handler.js +5 -26
  41. package/dist/http/query.js +1 -1
  42. package/dist/libsql/libsqlDialect.d.ts +12 -2
  43. package/dist/libsql/libsqlDialect.js +14 -2
  44. package/dist/libsql/libsqlQuerier.d.ts +9 -22
  45. package/dist/libsql/libsqlQuerier.js +9 -85
  46. package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
  47. package/dist/libsql/libsqlQuerierPool.js +17 -15
  48. package/dist/maria/mariaDialect.d.ts +26 -4
  49. package/dist/maria/mariaDialect.js +54 -15
  50. package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
  51. package/dist/migrate/acquireQuerierForMigrations.js +28 -0
  52. package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
  53. package/dist/migrate/builder/migrationBuilder.js +58 -76
  54. package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
  55. package/dist/migrate/builder/splitSqlStatements.js +0 -2
  56. package/dist/migrate/builder/tableBuilder.d.ts +12 -2
  57. package/dist/migrate/builder/tableBuilder.js +71 -119
  58. package/dist/migrate/builder/types.d.ts +30 -41
  59. package/dist/migrate/cli-config.js +24 -8
  60. package/dist/migrate/cli.d.ts +3 -2
  61. package/dist/migrate/cli.js +26 -49
  62. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
  63. package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
  64. package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
  65. package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
  66. package/dist/migrate/codegen/index.d.ts +0 -3
  67. package/dist/migrate/codegen/index.js +0 -6
  68. package/dist/migrate/drift/driftDetector.d.ts +10 -1
  69. package/dist/migrate/drift/driftDetector.js +22 -5
  70. package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
  71. package/dist/migrate/generator/indexNodeToSchema.js +14 -0
  72. package/dist/migrate/generator/mongoCommand.d.ts +57 -0
  73. package/dist/migrate/generator/mongoCommand.js +26 -0
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
  76. package/dist/migrate/index.d.ts +0 -1
  77. package/dist/migrate/index.js +0 -1
  78. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
  79. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
  80. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
  81. package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
  82. package/dist/migrate/introspection/mongoIntrospector.js +9 -15
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
  84. package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
  86. package/dist/migrate/introspection/postgresIntrospector.js +4 -10
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
  88. package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
  89. package/dist/migrate/migrator.d.ts +23 -3
  90. package/dist/migrate/migrator.js +101 -150
  91. package/dist/migrate/schemaGenerator.d.ts +26 -37
  92. package/dist/migrate/schemaGenerator.js +69 -174
  93. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
  94. package/dist/migrate/schemaGeneratorAsync.js +3 -3
  95. package/dist/migrate/storage/databaseStorage.d.ts +2 -0
  96. package/dist/migrate/storage/databaseStorage.js +12 -27
  97. package/dist/mongo/mongoDialect.d.ts +2 -0
  98. package/dist/mongo/mongoDialect.js +14 -3
  99. package/dist/mongo/mongodbQuerier.js +171 -229
  100. package/dist/mysql/mysqlDialect.d.ts +8 -0
  101. package/dist/mysql/mysqlDialect.js +13 -0
  102. package/dist/nestjs/uqlContextInterceptor.js +58 -22
  103. package/dist/nestjs/uqlModule.d.ts +2 -11
  104. package/dist/nestjs/uqlModule.js +93 -44
  105. package/dist/postgres/postgresDialect.js +2 -2
  106. package/dist/querier/abstractQuerier.d.ts +28 -0
  107. package/dist/querier/abstractQuerier.js +47 -22
  108. package/dist/querier/abstractQuerierPool.d.ts +3 -0
  109. package/dist/querier/abstractQuerierPool.js +5 -3
  110. package/dist/querier/abstractSqlQuerier.d.ts +1 -3
  111. package/dist/querier/abstractSqlQuerier.js +48 -96
  112. package/dist/querier/index.d.ts +3 -1
  113. package/dist/querier/index.js +3 -1
  114. package/dist/querier/querierContext.browser.d.ts +12 -0
  115. package/dist/querier/querierContext.browser.js +18 -0
  116. package/dist/querier/querierContext.d.ts +22 -0
  117. package/dist/querier/querierContext.js +42 -0
  118. package/dist/querier/queryError.d.ts +22 -0
  119. package/dist/querier/queryError.js +20 -0
  120. package/dist/querier/transactional.d.ts +26 -0
  121. package/dist/querier/transactional.js +43 -0
  122. package/dist/schema/canonicalType.d.ts +14 -2
  123. package/dist/schema/canonicalType.js +41 -31
  124. package/dist/schema/index.d.ts +1 -1
  125. package/dist/schema/schemaASTBuilder.d.ts +9 -2
  126. package/dist/schema/schemaASTBuilder.js +58 -49
  127. package/dist/schema/types.d.ts +10 -58
  128. package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
  129. package/dist/sqlite/abstractSqliteQuerier.js +44 -0
  130. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
  131. package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
  132. package/dist/sqlite/hranaQuerier.d.ts +54 -0
  133. package/dist/sqlite/hranaQuerier.js +68 -0
  134. package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
  135. package/dist/sqlite/hranaQuerierPool.js +25 -0
  136. package/dist/sqlite/index.d.ts +3 -1
  137. package/dist/sqlite/index.js +3 -1
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
  139. package/dist/sqlite/localSqliteQuerierPool.js +34 -0
  140. package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
  141. package/dist/sqlite/nodeSqliteAdapter.js +28 -0
  142. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
  143. package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
  144. package/dist/sqlite/sqliteDialect.d.ts +18 -5
  145. package/dist/sqlite/sqliteDialect.js +23 -36
  146. package/dist/sqlite/sqliteQuerier.d.ts +16 -9
  147. package/dist/sqlite/sqliteQuerier.js +4 -27
  148. package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
  149. package/dist/sqlite/sqliteQuerierPool.js +16 -24
  150. package/dist/turso/index.d.ts +3 -0
  151. package/dist/turso/index.js +3 -0
  152. package/dist/turso/local.d.ts +3 -0
  153. package/dist/turso/local.js +3 -0
  154. package/dist/turso/tursoDialect.d.ts +15 -0
  155. package/dist/turso/tursoDialect.js +18 -0
  156. package/dist/turso/tursoLocalQuerier.d.ts +25 -0
  157. package/dist/turso/tursoLocalQuerier.js +20 -0
  158. package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
  159. package/dist/turso/tursoLocalQuerierPool.js +39 -0
  160. package/dist/turso/tursoQuerier.d.ts +10 -0
  161. package/dist/turso/tursoQuerier.js +10 -0
  162. package/dist/turso/tursoQuerierPool.d.ts +39 -0
  163. package/dist/turso/tursoQuerierPool.js +38 -0
  164. package/dist/type/config.d.ts +0 -6
  165. package/dist/type/dialect.d.ts +12 -4
  166. package/dist/type/dialect.js +7 -1
  167. package/dist/type/entity.d.ts +154 -26
  168. package/dist/type/migration.d.ts +32 -23
  169. package/dist/type/querier.d.ts +6 -0
  170. package/dist/type/vector.d.ts +5 -2
  171. package/dist/util/hook.util.js +1 -1
  172. package/dist/util/index.d.ts +1 -0
  173. package/dist/util/index.js +1 -0
  174. package/dist/util/indexColumn.util.d.ts +6 -0
  175. package/dist/util/indexColumn.util.js +26 -0
  176. package/package.json +21 -16
  177. package/dist/entity/decorator/field.d.ts +0 -2
  178. package/dist/entity/decorator/field.js +0 -7
  179. package/dist/entity/decorator/filter.d.ts +0 -7
  180. package/dist/entity/decorator/filter.js +0 -11
  181. package/dist/entity/decorator/hook.d.ts +0 -7
  182. package/dist/entity/decorator/hook.js +0 -14
  183. package/dist/entity/decorator/id.d.ts +0 -2
  184. package/dist/entity/decorator/id.js +0 -7
  185. package/dist/entity/decorator/index-decorator.d.ts +0 -29
  186. package/dist/entity/decorator/index-decorator.js +0 -26
  187. package/dist/entity/decorator/index.d.ts +0 -8
  188. package/dist/entity/decorator/index.js +0 -8
  189. package/dist/entity/decorator/relation.d.ts +0 -8
  190. package/dist/entity/decorator/relation.js +0 -19
  191. package/dist/migrate/codegen/entityMerger.d.ts +0 -114
  192. package/dist/migrate/codegen/entityMerger.js +0 -293
  193. package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
  194. package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
  195. package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
  196. package/dist/migrate/codegen/smartRelationDetector.js +0 -134
  197. package/dist/migrate/generator/index.d.ts +0 -2
  198. package/dist/migrate/generator/index.js +0 -2
  199. package/dist/migrate/sync/index.d.ts +0 -6
  200. package/dist/migrate/sync/index.js +0 -6
  201. package/dist/migrate/sync/schemaSync.d.ts +0 -131
  202. package/dist/migrate/sync/schemaSync.js +0 -259
  203. package/dist/querier/decorator/index.d.ts +0 -4
  204. package/dist/querier/decorator/index.js +0 -4
  205. package/dist/querier/decorator/injectQuerier.d.ts +0 -3
  206. package/dist/querier/decorator/injectQuerier.js +0 -33
  207. package/dist/querier/decorator/log.d.ts +0 -32
  208. package/dist/querier/decorator/log.js +0 -52
  209. package/dist/querier/decorator/serialized.d.ts +0 -6
  210. package/dist/querier/decorator/serialized.js +0 -13
  211. package/dist/querier/decorator/transactional.d.ts +0 -6
  212. package/dist/querier/decorator/transactional.js +0 -48
  213. package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
  214. package/dist/sqlite/betterSqlite3Dialect.js +0 -9
@@ -9,8 +9,9 @@
9
9
  * - Indexes
10
10
  * - JSDoc comments for sync-added fields
11
11
  */
12
- import { canonicalToColumnType, canonicalToTypeScript } from '../../schema/canonicalType.js';
12
+ import { canonicalToTypeScript } from '../../schema/canonicalType.js';
13
13
  import { camelCase, pascalCase, singularize } from '../../util/string.util.js';
14
+ import { buildFieldOptionsSource } from './fieldOptionsSource.js';
14
15
  /**
15
16
  * Generates TypeScript entity code from SchemaAST.
16
17
  */
@@ -82,7 +83,6 @@ export class EntityCodeGenerator {
82
83
  if (this.options.includeRelations) {
83
84
  for (const rel of [...table.incomingRelations, ...table.outgoingRelations]) {
84
85
  uqlImports.add(this.getRelationDecoratorName(rel.type));
85
- uqlImports.add('Relation');
86
86
  const relatedTable = rel.from.table === table ? rel.to.table : rel.from.table;
87
87
  const relatedClassName = this.options.classNameTransformer(relatedTable.name);
88
88
  if (!relatedImports.includes(relatedClassName)) {
@@ -183,44 +183,9 @@ export class EntityCodeGenerator {
183
183
  * Build Field decorator options.
184
184
  */
185
185
  buildFieldOptions(col) {
186
- const options = [];
187
- // Column type
188
- const columnType = canonicalToColumnType(col.type);
189
- if (columnType) {
190
- options.push(`columnType: '${columnType}'`);
191
- }
192
- // Length
193
- if (col.type.length && col.type.category === 'string') {
194
- options.push(`length: ${col.type.length}`);
195
- }
196
- // Precision and scale
197
- if (col.type.precision) {
198
- options.push(`precision: ${col.type.precision}`);
199
- if (col.type.scale) {
200
- options.push(`scale: ${col.type.scale}`);
201
- }
202
- }
203
- // Nullable
204
- if (col.nullable) {
205
- options.push('nullable: true');
206
- }
207
- // Unique
208
- if (col.isUnique) {
209
- options.push('unique: true');
210
- }
211
- // Default value
212
- if (col.defaultValue !== undefined) {
213
- options.push(`defaultValue: ${this.formatDefaultValue(col.defaultValue)}`);
214
- }
215
- // Index (single column)
216
- if (this.options.includeIndexes) {
217
- const indexes = this.ast.getTableIndexes(col.table.name);
218
- const singleColIndex = indexes.find((idx) => idx.columns.length === 1 && idx.columns[0].name === col.name);
219
- if (singleColIndex) {
220
- options.push(`index: '${singleColIndex.name}'`);
221
- }
222
- }
223
- return options.length > 0 ? `{ ${options.join(', ')} }` : '';
186
+ const indexes = this.options.includeIndexes ? this.ast.getTableIndexes(col.table.name) : [];
187
+ const singleColIndex = indexes.find((idx) => idx.columns.length === 1 && idx.columns[0].name === col.name);
188
+ return buildFieldOptionsSource(col, singleColIndex?.name);
224
189
  }
225
190
  /**
226
191
  * Build relation definitions.
@@ -272,7 +237,7 @@ export class EntityCodeGenerator {
272
237
  // Decorator
273
238
  lines.push(` @${decoratorName}({ entity: () => ${relatedClassName} })`);
274
239
  // Property
275
- lines.push(` ${propertyName}?: Relation<${relatedClassName}>;`);
240
+ lines.push(` ${propertyName}?: ${relatedClassName};`);
276
241
  return lines.join('\n');
277
242
  }
278
243
  /**
@@ -296,10 +261,10 @@ export class EntityCodeGenerator {
296
261
  lines.push(` @${decoratorName}({ entity: () => ${relatedClassName}, references: '${inverseProp}' })`);
297
262
  // Property
298
263
  if (inverseType === 'OneToMany' || inverseType === 'ManyToMany') {
299
- lines.push(` ${propertyName}?: Relation<${relatedClassName}[]>;`);
264
+ lines.push(` ${propertyName}?: ${relatedClassName}[];`);
300
265
  }
301
266
  else {
302
- lines.push(` ${propertyName}?: Relation<${relatedClassName}>;`);
267
+ lines.push(` ${propertyName}?: ${relatedClassName};`);
303
268
  }
304
269
  return lines.join('\n');
305
270
  }
@@ -340,30 +305,6 @@ export class EntityCodeGenerator {
340
305
  desc += ' UNSIGNED';
341
306
  return desc;
342
307
  }
343
- /**
344
- * Format default value for code.
345
- */
346
- formatDefaultValue(value) {
347
- if (typeof value === 'string') {
348
- // Check for SQL expressions
349
- if (value.includes('(') ||
350
- value.toUpperCase() === 'CURRENT_TIMESTAMP' ||
351
- value.toUpperCase().includes('NEXTVAL')) {
352
- return `'${value}'`; // Keep as string for expressions
353
- }
354
- return `'${value.replace(/'/g, "\\'")}'`;
355
- }
356
- if (typeof value === 'boolean') {
357
- return value.toString();
358
- }
359
- if (typeof value === 'number') {
360
- return value.toString();
361
- }
362
- if (value === null) {
363
- return 'null';
364
- }
365
- return JSON.stringify(value);
366
- }
367
308
  /**
368
309
  * Default class name transformer: table_name -> TableName (PascalCase, singular).
369
310
  */
@@ -0,0 +1,10 @@
1
+ import type { ColumnNode } from '../../schema/types.js';
2
+ /**
3
+ * A column's `@Field({ ... })` options as source, or `''` when it needs none.
4
+ *
5
+ * Shared by the entity generator and the merger because they emit the same decorator. They each had
6
+ * their own copy, and the copies had drifted: the merger's dropped `unique` and `defaultValue`, so
7
+ * merging a column into an existing entity file quietly produced a weaker field than generating the
8
+ * file from scratch.
9
+ */
10
+ export declare function buildFieldOptionsSource(col: ColumnNode, indexName?: string): string;
@@ -0,0 +1,55 @@
1
+ import { canonicalToColumnType } from '../../schema/canonicalType.js';
2
+ /**
3
+ * A column's `@Field({ ... })` options as source, or `''` when it needs none.
4
+ *
5
+ * Shared by the entity generator and the merger because they emit the same decorator. They each had
6
+ * their own copy, and the copies had drifted: the merger's dropped `unique` and `defaultValue`, so
7
+ * merging a column into an existing entity file quietly produced a weaker field than generating the
8
+ * file from scratch.
9
+ */
10
+ export function buildFieldOptionsSource(col, indexName) {
11
+ const options = [];
12
+ const columnType = canonicalToColumnType(col.type);
13
+ if (columnType) {
14
+ options.push(`columnType: '${columnType}'`);
15
+ }
16
+ if (col.type.length && col.type.category === 'string') {
17
+ options.push(`length: ${col.type.length}`);
18
+ }
19
+ if (col.type.precision !== undefined) {
20
+ options.push(`precision: ${col.type.precision}`);
21
+ if (col.type.scale !== undefined) {
22
+ options.push(`scale: ${col.type.scale}`);
23
+ }
24
+ }
25
+ if (col.nullable) {
26
+ options.push('nullable: true');
27
+ }
28
+ if (col.isUnique) {
29
+ options.push('unique: true');
30
+ }
31
+ if (col.defaultValue !== undefined) {
32
+ options.push(`defaultValue: ${formatDefaultValueSource(col.defaultValue)}`);
33
+ }
34
+ if (indexName) {
35
+ options.push(`index: '${indexName}'`);
36
+ }
37
+ return options.length > 0 ? `{ ${options.join(', ')} }` : '';
38
+ }
39
+ /**
40
+ * A default value as source. Strings stay single-quoted, expressions included: `defaultValue: 'now()'`
41
+ * is what reaches the DDL. The generator used to branch on `CURRENT_TIMESTAMP`/`NEXTVAL`/`(` first, but
42
+ * both branches emitted a quoted string and only the fallthrough escaped embedded quotes.
43
+ */
44
+ function formatDefaultValueSource(value) {
45
+ if (typeof value === 'string') {
46
+ return `'${value.replace(/'/g, "\\'")}'`;
47
+ }
48
+ if (typeof value === 'boolean' || typeof value === 'number') {
49
+ return value.toString();
50
+ }
51
+ if (value === null) {
52
+ return 'null';
53
+ }
54
+ return JSON.stringify(value);
55
+ }
@@ -4,7 +4,4 @@
4
4
  * Generates TypeScript entity code from database schemas.
5
5
  */
6
6
  export { createEntityCodeGenerator, EntityCodeGenerator, type EntityCodeGeneratorOptions, type GeneratedEntity, } from './entityCodeGenerator.js';
7
- export { createEntityMerger, EntityMerger, type EntityMergerOptions, type FieldToAdd, type FieldToDeprecate, type MergeResult, } from './entityMerger.js';
8
- export { createMigrationCodeGenerator, type GeneratedMigration, MigrationCodeGenerator, type MigrationCodeOptions, } from './migrationCodeGenerator.js';
9
7
  export { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCall, emitSqlRunCalls, type SqlMigrationModuleOptions, } from './migrationFile.js';
10
- export { createRelationDetector, type RelationDetectorOptions, SmartRelationDetector, } from './smartRelationDetector.js';
@@ -5,10 +5,4 @@
5
5
  */
6
6
  // Entity code generator
7
7
  export { createEntityCodeGenerator, EntityCodeGenerator, } from './entityCodeGenerator.js';
8
- // Entity merger
9
- export { createEntityMerger, EntityMerger, } from './entityMerger.js';
10
- // Migration code generator
11
- export { createMigrationCodeGenerator, MigrationCodeGenerator, } from './migrationCodeGenerator.js';
12
8
  export { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCall, emitSqlRunCalls, } from './migrationFile.js';
13
- // Smart relation detector
14
- export { createRelationDetector, SmartRelationDetector, } from './smartRelationDetector.js';
@@ -19,8 +19,17 @@ export interface DriftDetectorOptions {
19
19
  checkIndexes?: boolean;
20
20
  /** Include foreign key differences */
21
21
  checkForeignKeys?: boolean;
22
- /** Include default value differences */
22
+ /**
23
+ * Include default value differences. Off by default: an engine reports a default as it stored it
24
+ * (`now()`, `CURRENT_TIMESTAMP`, `'active'::text`), which rarely matches the entity's literal.
25
+ */
23
26
  checkDefaults?: boolean;
27
+ /**
28
+ * Tables to leave out of the comparison. The migrations bookkeeping table belongs here - it exists in
29
+ * the database by design and has no entity, so reporting it as unexpected told every project to
30
+ * "create entity or drop table" for its own migration log.
31
+ */
32
+ excludeTables?: string[];
24
33
  /** Dialect instance for type formatting */
25
34
  dialect?: AbstractDialect;
26
35
  }
@@ -22,6 +22,7 @@ export class DriftDetector {
22
22
  checkIndexes: options.checkIndexes ?? true,
23
23
  checkForeignKeys: options.checkForeignKeys ?? true,
24
24
  checkDefaults: options.checkDefaults ?? false,
25
+ excludeTables: options.excludeTables ?? [],
25
26
  dialect: options.dialect,
26
27
  };
27
28
  }
@@ -30,7 +31,11 @@ export class DriftDetector {
30
31
  */
31
32
  detect() {
32
33
  const differ = new SchemaASTDiffer();
33
- const diff = differ.diff(this.expectedAST, this.actualAST);
34
+ const diff = differ.diff(this.expectedAST, this.actualAST, {
35
+ compareIndexes: this.options.checkIndexes,
36
+ compareRelationships: this.options.checkForeignKeys,
37
+ excludeTables: this.options.excludeTables,
38
+ });
34
39
  const drifts = [
35
40
  ...this.detectTableDrifts(diff),
36
41
  ...this.detectColumnDrifts(diff),
@@ -137,13 +142,27 @@ export class DriftDetector {
137
142
  });
138
143
  }
139
144
  }
145
+ if (this.options.checkDefaults && colDiff.expected && colDiff.actual) {
146
+ const expected = String(colDiff.expected.defaultValue ?? 'NULL');
147
+ const actual = String(colDiff.actual.defaultValue ?? 'NULL');
148
+ if (expected !== actual) {
149
+ drifts.push({
150
+ type: 'constraint_mismatch',
151
+ severity: 'info',
152
+ table: colDiff.table,
153
+ column: colDiff.column,
154
+ expected,
155
+ actual,
156
+ details: `Default mismatch for "${colDiff.column}"`,
157
+ suggestion: 'Align the default in the entity or the database',
158
+ });
159
+ }
160
+ }
140
161
  }
141
162
  /**
142
163
  * Detect index drifts.
143
164
  */
144
165
  detectIndexDrifts(diff) {
145
- if (!this.options.checkIndexes)
146
- return [];
147
166
  const drifts = [];
148
167
  for (const idxDiff of diff.indexDiffs) {
149
168
  if (idxDiff.type === 'create') {
@@ -173,8 +192,6 @@ export class DriftDetector {
173
192
  * Detect relationship/FK drifts.
174
193
  */
175
194
  detectRelationshipDrifts(diff) {
176
- if (!this.options.checkForeignKeys)
177
- return [];
178
195
  const drifts = [];
179
196
  for (const relDiff of diff.relationshipDiffs) {
180
197
  if (relDiff.type === 'create') {
@@ -0,0 +1,9 @@
1
+ import type { IndexNode } from '../../schema/types.js';
2
+ import type { IndexSchema } from '../../type/index.js';
3
+ /**
4
+ * An AST index as the generators and dialects want it. Spread rather than copied field by field:
5
+ * rebuilding it by hand is how the partial-index `where` once vanished without a trace. The node-only
6
+ * keys (`table`, `source`, `syncStatus`) are ignored downstream, and the vector type travels along
7
+ * because pgvector's operator-class names are built from it.
8
+ */
9
+ export declare function indexNodeToSchema(index: IndexNode): IndexSchema;
@@ -0,0 +1,14 @@
1
+ import { isVectorCategory } from '../../schema/canonicalType.js';
2
+ /**
3
+ * An AST index as the generators and dialects want it. Spread rather than copied field by field:
4
+ * rebuilding it by hand is how the partial-index `where` once vanished without a trace. The node-only
5
+ * keys (`table`, `source`, `syncStatus`) are ignored downstream, and the vector type travels along
6
+ * because pgvector's operator-class names are built from it.
7
+ */
8
+ export function indexNodeToSchema(index) {
9
+ return {
10
+ ...index,
11
+ columns: index.entries ?? index.columns.map((col) => ({ column: col.name })),
12
+ vectorType: index.columns.map((col) => col.type?.category).find(isVectorCategory),
13
+ };
14
+ }
@@ -0,0 +1,57 @@
1
+ /** The direction, or `'text'`, of one field in a MongoDB index key spec. */
2
+ export type MongoIndexKey = Record<string, 1 | -1 | 'text'>;
3
+ /**
4
+ * `SchemaGenerator` yields one string per statement, so {@link MongoSchemaGenerator} emits its
5
+ * commands as JSON and this is their schema.
6
+ *
7
+ * Declared next to the generator that writes them because the migrator used to restate the shape
8
+ * inline from `JSON.parse`, with `cmd.name!` assertions and a bare `action: string` - where a command
9
+ * it had no branch for (`renameCollection`) was silently a no-op.
10
+ */
11
+ export type MongoCommand = {
12
+ readonly action: 'createCollection';
13
+ readonly name: string;
14
+ } | {
15
+ readonly action: 'dropCollection';
16
+ readonly name: string;
17
+ } | {
18
+ readonly action: 'renameCollection';
19
+ readonly from: string;
20
+ readonly to: string;
21
+ } | {
22
+ readonly action: 'createIndex';
23
+ readonly collection: string;
24
+ readonly name: string;
25
+ readonly key: MongoIndexKey;
26
+ readonly options: {
27
+ readonly unique: boolean;
28
+ readonly name: string;
29
+ };
30
+ } | {
31
+ readonly action: 'dropIndex';
32
+ readonly collection: string;
33
+ readonly name: string;
34
+ };
35
+ export declare function serializeMongoCommand(command: MongoCommand): string;
36
+ /**
37
+ * What executing these commands needs of a database handle. Declared structurally rather than as the
38
+ * driver's `Db` so this module carries no dependency on the optional `mongodb` peer, and so a test can
39
+ * satisfy it with a plain object.
40
+ */
41
+ export type MongoCommandTarget = {
42
+ createCollection(name: string): Promise<unknown>;
43
+ renameCollection(from: string, to: string): Promise<unknown>;
44
+ collection(name: string): {
45
+ drop(): Promise<unknown>;
46
+ createIndex(key: MongoIndexKey, options: {
47
+ unique: boolean;
48
+ name: string;
49
+ }): Promise<unknown>;
50
+ dropIndex(name: string): Promise<unknown>;
51
+ };
52
+ };
53
+ /**
54
+ * Execute one emitted command. The single cast lives here, where the union it casts to is defined
55
+ * alongside the only code that writes these strings.
56
+ */
57
+ export declare function runMongoCommand(db: MongoCommandTarget, statement: string): Promise<unknown>;
@@ -0,0 +1,26 @@
1
+ export function serializeMongoCommand(command) {
2
+ return JSON.stringify(command);
3
+ }
4
+ /**
5
+ * Execute one emitted command. The single cast lives here, where the union it casts to is defined
6
+ * alongside the only code that writes these strings.
7
+ */
8
+ export function runMongoCommand(db, statement) {
9
+ const command = JSON.parse(statement);
10
+ switch (command.action) {
11
+ case 'createCollection':
12
+ return db.createCollection(command.name);
13
+ case 'dropCollection':
14
+ return db.collection(command.name).drop();
15
+ case 'renameCollection':
16
+ return db.renameCollection(command.from, command.to);
17
+ case 'createIndex':
18
+ return db.collection(command.collection).createIndex(command.key, command.options);
19
+ case 'dropIndex':
20
+ return db.collection(command.collection).dropIndex(command.name);
21
+ default:
22
+ // Unreachable for a command this module produced; a hand-written statement lands here rather
23
+ // than being silently skipped, which is how `renameCollection` went unnoticed.
24
+ throw new TypeError(`unsupported MongoDB migration command: ${statement}`);
25
+ }
26
+ }
@@ -11,38 +11,27 @@ export declare class MongoSchemaGenerator extends AbstractDialect implements Sch
11
11
  generateCreateTable<E>(entity: Type<E>, _options?: {
12
12
  ifNotExists?: boolean;
13
13
  }): string[];
14
- generateDropTable<E>(entity: Type<E>): string;
14
+ generateDropTable(tableName: string): string;
15
15
  generateAlterTable(diff: SchemaDiff): string[];
16
16
  generateAlterTableDown(diff: SchemaDiff): string[];
17
+ /**
18
+ * MongoDB's key spec is where its index options live: `-1` for a descending entry and `'text'` for a
19
+ * full-text index, which is what `$text` needs since a text index declares its own fields.
20
+ *
21
+ * @remarks The SQL-only modifiers are refused rather than dropped, for the same reason the SQL
22
+ * dialects refuse each other's - a silently weaker index is worse than a clear failure. `where`
23
+ * included: MongoDB's partial indexes take a filter document, not a SQL predicate.
24
+ */
17
25
  generateCreateIndex(tableName: string, index: IndexSchema): string;
18
26
  generateDropIndex(tableName: string, indexName: string): string;
19
27
  getSqlType(fieldOptions: FieldOptions, fieldType?: unknown): string;
20
- getBooleanType(): string;
21
- generateColumnDefinitions(): string[];
22
- generateColumnDefinition(): string;
23
- generateColumnDefinitionFromSchema(): string;
24
- generateTableConstraints(): string[];
25
- generateAlterColumnStatements(): string[];
26
- getTableOptions(): string;
27
- generateColumnComment(): string;
28
- formatDefaultValue(): string;
29
28
  generateCreateTableFromNode(table: TableNode, _options?: {
30
29
  ifNotExists?: boolean;
31
30
  }): string[];
32
31
  generateCreateIndexFromNode(index: IndexNode): string;
33
- generateDropTableFromNode(table: TableNode): string;
34
32
  generateCreateTableFromDefinition(table: TableDefinition, _options?: {
35
33
  ifNotExists?: boolean;
36
34
  }): string[];
37
- generateDropTableSql(tableName: string): string;
38
35
  generateRenameTableSql(oldName: string, newName: string): string;
39
- generateAddColumnSql(): string;
40
- generateDropColumnSql(): string;
41
- generateRenameColumnSql(): string;
42
- generateAlterColumnSql(): string;
43
- generateCreateIndexSql(tableName: string, index: IndexSchema): string;
44
- generateDropIndexSql(tableName: string, indexName: string): string;
45
- generateAddForeignKeySql(): string;
46
- generateDropForeignKeySql(): string;
47
36
  diffSchema<E>(entity: Type<E>, currentTable: TableNode | undefined): SchemaDiff | undefined;
48
37
  }
@@ -2,6 +2,8 @@ import { AbstractDialect } from '../../dialect/abstractDialect.js';
2
2
  import { getMeta } from '../../entity/index.js';
3
3
  import { mongoDialectFeatures } from '../../mongo/mongoDialect.js';
4
4
  import { getKeys } from '../../util/index.js';
5
+ import { indexNodeToSchema } from './indexNodeToSchema.js';
6
+ import { serializeMongoCommand } from './mongoCommand.js';
5
7
  export class MongoSchemaGenerator extends AbstractDialect {
6
8
  defaultForeignKeyAction;
7
9
  dialectName = 'mongodb';
@@ -22,17 +24,20 @@ export class MongoSchemaGenerator extends AbstractDialect {
22
24
  const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
23
25
  indexes.push({
24
26
  name: indexName,
25
- columns: [columnName],
27
+ columns: [{ column: columnName }],
26
28
  unique: !!field.unique,
27
29
  });
28
30
  }
29
31
  }
30
- return [JSON.stringify({ action: 'createCollection', name: collectionName, indexes })];
32
+ // One `createIndex` command each, mirroring the SQL generator's `[CREATE TABLE, ...CREATE INDEX]`,
33
+ // so the key spec is built here and the migrator only executes it.
34
+ return [
35
+ serializeMongoCommand({ action: 'createCollection', name: collectionName }),
36
+ ...indexes.map((index) => this.generateCreateIndex(collectionName, index)),
37
+ ];
31
38
  }
32
- generateDropTable(entity) {
33
- const meta = getMeta(entity);
34
- const collectionName = this.resolveTableName(entity, meta);
35
- return JSON.stringify({ action: 'dropCollection', name: collectionName });
39
+ generateDropTable(tableName) {
40
+ return serializeMongoCommand({ action: 'dropCollection', name: tableName });
36
41
  }
37
42
  generateAlterTable(diff) {
38
43
  const statements = [];
@@ -52,12 +57,26 @@ export class MongoSchemaGenerator extends AbstractDialect {
52
57
  }
53
58
  return statements;
54
59
  }
60
+ /**
61
+ * MongoDB's key spec is where its index options live: `-1` for a descending entry and `'text'` for a
62
+ * full-text index, which is what `$text` needs since a text index declares its own fields.
63
+ *
64
+ * @remarks The SQL-only modifiers are refused rather than dropped, for the same reason the SQL
65
+ * dialects refuse each other's - a silently weaker index is worse than a clear failure. `where`
66
+ * included: MongoDB's partial indexes take a filter document, not a SQL predicate.
67
+ */
55
68
  generateCreateIndex(tableName, index) {
56
69
  const key = {};
57
- for (const col of index.columns) {
58
- key[col] = 1;
70
+ for (const entry of index.columns) {
71
+ if (entry.expression || entry.length !== undefined || entry.nulls || entry.opsClass) {
72
+ throw new TypeError(`mongodb does not support that index column option (index "${index.name}")`);
73
+ }
74
+ key[entry.column] = index.type === 'fulltext' ? 'text' : entry.order === 'desc' ? -1 : 1;
59
75
  }
60
- return JSON.stringify({
76
+ if (index.where) {
77
+ throw new TypeError(`mongodb does not support partial indexes from a SQL predicate (index "${index.name}")`);
78
+ }
79
+ return serializeMongoCommand({
61
80
  action: 'createIndex',
62
81
  collection: tableName,
63
82
  name: index.name,
@@ -66,7 +85,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
66
85
  });
67
86
  }
68
87
  generateDropIndex(tableName, indexName) {
69
- return JSON.stringify({
88
+ return serializeMongoCommand({
70
89
  action: 'dropIndex',
71
90
  collection: tableName,
72
91
  name: indexName,
@@ -75,84 +94,23 @@ export class MongoSchemaGenerator extends AbstractDialect {
75
94
  getSqlType(fieldOptions, fieldType) {
76
95
  return '';
77
96
  }
78
- getBooleanType() {
79
- return '';
80
- }
81
- generateColumnDefinitions() {
82
- return [];
83
- }
84
- generateColumnDefinition() {
85
- return '';
86
- }
87
- generateColumnDefinitionFromSchema() {
88
- return '';
89
- }
90
- generateTableConstraints() {
91
- return [];
92
- }
93
- generateAlterColumnStatements() {
94
- return [];
95
- }
96
- getTableOptions() {
97
- return '';
98
- }
99
- generateColumnComment() {
100
- return '';
101
- }
102
- formatDefaultValue() {
103
- return '';
104
- }
105
97
  generateCreateTableFromNode(table, _options) {
106
- return [JSON.stringify({ action: 'createCollection', name: table.name })];
98
+ return [
99
+ serializeMongoCommand({ action: 'createCollection', name: table.name }),
100
+ ...table.indexes.map((index) => this.generateCreateIndexFromNode(index)),
101
+ ];
107
102
  }
108
103
  generateCreateIndexFromNode(index) {
109
- const key = {};
110
- for (const col of index.columns) {
111
- key[col.name] = 1;
112
- }
113
- return JSON.stringify({
114
- action: 'createIndex',
115
- collection: index.table.name,
116
- name: index.name,
117
- key,
118
- options: { unique: index.unique, name: index.name },
119
- });
120
- }
121
- generateDropTableFromNode(table) {
122
- return JSON.stringify({ action: 'dropCollection', name: table.name });
104
+ return this.generateCreateIndex(index.table.name, indexNodeToSchema(index));
123
105
  }
124
106
  generateCreateTableFromDefinition(table, _options) {
125
- return [JSON.stringify({ action: 'createCollection', name: table.name })];
126
- }
127
- generateDropTableSql(tableName) {
128
- return JSON.stringify({ action: 'dropCollection', name: tableName });
107
+ return [
108
+ serializeMongoCommand({ action: 'createCollection', name: table.name }),
109
+ ...table.indexes.map((index) => this.generateCreateIndex(table.name, index)),
110
+ ];
129
111
  }
130
112
  generateRenameTableSql(oldName, newName) {
131
- return JSON.stringify({ action: 'renameCollection', from: oldName, to: newName });
132
- }
133
- generateAddColumnSql() {
134
- return '';
135
- }
136
- generateDropColumnSql() {
137
- return '';
138
- }
139
- generateRenameColumnSql() {
140
- return '';
141
- }
142
- generateAlterColumnSql() {
143
- return '';
144
- }
145
- generateCreateIndexSql(tableName, index) {
146
- return this.generateCreateIndex(tableName, index);
147
- }
148
- generateDropIndexSql(tableName, indexName) {
149
- return this.generateDropIndex(tableName, indexName);
150
- }
151
- generateAddForeignKeySql() {
152
- return '';
153
- }
154
- generateDropForeignKeySql() {
155
- return '';
113
+ return serializeMongoCommand({ action: 'renameCollection', from: oldName, to: newName });
156
114
  }
157
115
  diffSchema(entity, currentTable) {
158
116
  const meta = getMeta(entity);
@@ -170,7 +128,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
170
128
  if (!existingIndexes.has(indexName)) {
171
129
  indexesToAdd.push({
172
130
  name: indexName,
173
- columns: [columnName],
131
+ columns: [{ column: columnName }],
174
132
  unique: !!field.unique,
175
133
  });
176
134
  }
@@ -12,4 +12,3 @@ export { createSchemaGenerator, SqlSchemaGenerator } from './schemaGenerator.js'
12
12
  export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
13
13
  export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
14
14
  export { JsonMigrationStorage } from './storage/jsonStorage.js';
15
- export * from './sync/index.js';
@@ -19,4 +19,3 @@ export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
19
19
  export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
20
20
  export { JsonMigrationStorage } from './storage/jsonStorage.js';
21
21
  // Schema sync
22
- export * from './sync/index.js';