uql-orm 0.62.0 → 0.64.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 (104) hide show
  1. package/dist/bunSql/bunSql.util.d.ts +15 -15
  2. package/dist/bunSql/bunSql.util.js +22 -35
  3. package/dist/bunSql/bunSqlQuerier.d.ts +2 -2
  4. package/dist/bunSql/bunSqlQuerier.js +3 -3
  5. package/dist/bunSql/bunSqlQuerierPool.d.ts +1 -13
  6. package/dist/bunSql/bunSqlQuerierPool.js +3 -34
  7. package/dist/d1/d1Querier.d.ts +12 -4
  8. package/dist/d1/d1Querier.js +6 -11
  9. package/dist/d1/d1QuerierPool.d.ts +7 -3
  10. package/dist/d1/d1QuerierPool.js +5 -3
  11. package/dist/entity/decorator/entity.d.ts +5 -9
  12. package/dist/entity/decorator/entity.js +3 -7
  13. package/dist/entity/metadata/definition.d.ts +2 -2
  14. package/dist/entity/metadata/definition.js +13 -22
  15. package/dist/libsql/libsqlDialect.d.ts +1 -1
  16. package/dist/libsql/libsqlDialect.js +1 -1
  17. package/dist/libsql/libsqlQuerierPool.d.ts +18 -9
  18. package/dist/libsql/libsqlQuerierPool.js +32 -20
  19. package/dist/migrate/builder/expressions.js +1 -15
  20. package/dist/migrate/builder/migrationBuilder.d.ts +6 -28
  21. package/dist/migrate/builder/migrationBuilder.js +9 -83
  22. package/dist/migrate/builder/types.d.ts +14 -24
  23. package/dist/migrate/cli.d.ts +1 -6
  24. package/dist/migrate/cli.js +3 -10
  25. package/dist/migrate/codegen/index.d.ts +1 -1
  26. package/dist/migrate/codegen/index.js +1 -1
  27. package/dist/migrate/codegen/indexDecoratorSource.d.ts +1 -1
  28. package/dist/migrate/codegen/indexDecoratorSource.js +4 -8
  29. package/dist/migrate/codegen/migrationFile.d.ts +9 -5
  30. package/dist/migrate/codegen/migrationFile.js +2 -3
  31. package/dist/migrate/ddl/indexDdl.d.ts +4 -2
  32. package/dist/migrate/ddl/indexDdl.js +16 -15
  33. package/dist/migrate/generator/mongoCommand.d.ts +9 -9
  34. package/dist/migrate/generator/mongoCommand.js +1 -1
  35. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +21 -26
  36. package/dist/migrate/generator/mongoSchemaGenerator.js +103 -80
  37. package/dist/migrate/index.d.ts +1 -1
  38. package/dist/migrate/index.js +1 -1
  39. package/dist/migrate/indexPredicate.d.ts +8 -0
  40. package/dist/migrate/indexPredicate.js +52 -0
  41. package/dist/migrate/migrationTarget.d.ts +23 -0
  42. package/dist/migrate/migrationTarget.js +48 -0
  43. package/dist/migrate/migrator.d.ts +17 -45
  44. package/dist/migrate/migrator.js +80 -179
  45. package/dist/migrate/schemaGenerator.d.ts +12 -13
  46. package/dist/migrate/schemaGenerator.js +43 -9
  47. package/dist/mongo/mongoDialect.d.ts +6 -1
  48. package/dist/querier/abstractSharedHandleQuerierPool.d.ts +5 -5
  49. package/dist/querier/abstractSharedHandleQuerierPool.js +5 -5
  50. package/dist/schema/schemaASTBuilder.d.ts +2 -0
  51. package/dist/schema/schemaASTBuilder.js +9 -22
  52. package/dist/sqlite/abstractSqliteQuerier.d.ts +11 -28
  53. package/dist/sqlite/abstractSqliteQuerier.js +14 -33
  54. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +5 -4
  55. package/dist/sqlite/bunSqliteAdapter.bun.js +1 -1
  56. package/dist/sqlite/hranaQuerier.d.ts +6 -4
  57. package/dist/sqlite/hranaQuerier.js +4 -13
  58. package/dist/sqlite/index.d.ts +0 -1
  59. package/dist/sqlite/index.js +0 -1
  60. package/dist/sqlite/localSqliteQuerierPool.d.ts +14 -5
  61. package/dist/sqlite/nodeSqliteAdapter.d.ts +3 -4
  62. package/dist/sqlite/nodeSqliteAdapter.js +3 -6
  63. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -3
  64. package/dist/sqlite/nodeSqliteQuerierPool.js +3 -1
  65. package/dist/sqlite/sqliteDialect.d.ts +1 -1
  66. package/dist/sqlite/sqliteDialect.js +1 -1
  67. package/dist/sqlite/sqlitePragmas.d.ts +2 -11
  68. package/dist/sqlite/sqlitePragmas.js +1 -1
  69. package/dist/sqlite/sqliteQuerier.d.ts +29 -10
  70. package/dist/sqlite/sqliteQuerier.js +26 -4
  71. package/dist/sqlite/sqliteQuerierPool.d.ts +7 -3
  72. package/dist/sqlite/sqliteQuerierPool.js +12 -8
  73. package/dist/turso/index.d.ts +1 -0
  74. package/dist/turso/index.js +1 -0
  75. package/dist/turso/local.d.ts +1 -1
  76. package/dist/turso/local.js +1 -1
  77. package/dist/turso/tursoDialect.d.ts +4 -9
  78. package/dist/turso/tursoDialect.js +4 -12
  79. package/dist/turso/tursoLocalDialect.d.ts +10 -0
  80. package/dist/turso/tursoLocalDialect.js +13 -0
  81. package/dist/turso/tursoLocalQuerierPool.d.ts +8 -13
  82. package/dist/turso/tursoLocalQuerierPool.js +6 -5
  83. package/dist/turso/tursoQuerierPool.d.ts +15 -30
  84. package/dist/turso/tursoQuerierPool.js +13 -23
  85. package/dist/turso/tursoSessionQuerier.d.ts +57 -0
  86. package/dist/turso/tursoSessionQuerier.js +50 -0
  87. package/dist/type/entity.d.ts +37 -62
  88. package/dist/type/migration.d.ts +11 -40
  89. package/dist/type/queryRaw.d.ts +8 -0
  90. package/dist/type/queryRaw.js +11 -0
  91. package/dist/util/ddlExpression.util.d.ts +5 -3
  92. package/dist/util/ddlExpression.util.js +19 -12
  93. package/dist/util/raw.d.ts +6 -1
  94. package/dist/util/raw.js +11 -8
  95. package/dist/util/sqlLiteral.js +5 -3
  96. package/dist/util/wideNumber.d.ts +2 -2
  97. package/dist/util/wideNumber.js +2 -2
  98. package/package.json +2 -2
  99. package/dist/migrate/schemaGeneratorAsync.d.ts +0 -7
  100. package/dist/migrate/schemaGeneratorAsync.js +0 -12
  101. package/dist/sqlite/hranaQuerierPool.d.ts +0 -20
  102. package/dist/sqlite/hranaQuerierPool.js +0 -26
  103. package/dist/turso/tursoLocalQuerier.d.ts +0 -24
  104. package/dist/turso/tursoLocalQuerier.js +0 -20
@@ -1,12 +1,18 @@
1
1
  /** The direction, or `'text'`, of one field in a MongoDB index key spec. */
2
2
  export type MongoIndexKey = Record<string, 1 | -1 | 'text'>;
3
+ /** What a `createIndex` command hands the driver beside its key spec. */
4
+ export type MongoIndexOptions = {
5
+ readonly unique: boolean;
6
+ readonly name: string;
7
+ readonly partialFilterExpression?: Readonly<Record<string, unknown>>;
8
+ };
3
9
  /**
4
10
  * `SchemaGenerator` yields one string per statement, so {@link MongoSchemaGenerator} emits its
5
11
  * commands as JSON and this is their schema.
6
12
  *
7
13
  * Declared next to the generator that writes them because the migrator used to restate the shape
8
14
  * 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.
15
+ * it had no branch for was silently a no-op.
10
16
  */
11
17
  export type MongoCommand = {
12
18
  readonly action: 'createCollection';
@@ -23,10 +29,7 @@ export type MongoCommand = {
23
29
  readonly collection: string;
24
30
  readonly name: string;
25
31
  readonly key: MongoIndexKey;
26
- readonly options: {
27
- readonly unique: boolean;
28
- readonly name: string;
29
- };
32
+ readonly options: MongoIndexOptions;
30
33
  } | {
31
34
  readonly action: 'dropIndex';
32
35
  readonly collection: string;
@@ -43,10 +46,7 @@ export type MongoCommandTarget = {
43
46
  renameCollection(from: string, to: string): Promise<unknown>;
44
47
  collection(name: string): {
45
48
  drop(): Promise<unknown>;
46
- createIndex(key: MongoIndexKey, options: {
47
- unique: boolean;
48
- name: string;
49
- }): Promise<unknown>;
49
+ createIndex(key: MongoIndexKey, options: MongoIndexOptions): Promise<unknown>;
50
50
  dropIndex(name: string): Promise<unknown>;
51
51
  };
52
52
  };
@@ -27,7 +27,7 @@ export function runMongoCommand(db, statement) {
27
27
  return db.collection(command.collection).dropIndex(command.name);
28
28
  default:
29
29
  // Unreachable for a command this module produced; a hand-written statement lands here rather
30
- // than being silently skipped, which is how `renameCollection` went unnoticed.
30
+ // than being silently skipped.
31
31
  throw unsupportedMongoCommand(statement);
32
32
  }
33
33
  }
@@ -1,12 +1,9 @@
1
- import { AbstractDialect } from '../../dialect/abstractDialect.js';
2
- import type { ForeignKeyAction, IndexNode, TableNode } from '../../schema/types.js';
3
- import type { CreateSchemaOptions, FieldOptions, IndexSchema, InsertIdSource, NamingStrategy, SchemaDiff, SchemaGenerator, Type } from '../../type/index.js';
4
- import type { TableDefinition } from '../builder/types.js';
5
- export declare class MongoSchemaGenerator extends AbstractDialect implements SchemaGenerator {
1
+ import { MongoDialect } from '../../mongo/mongoDialect.js';
2
+ import type { ForeignKeyAction, TableNode } from '../../schema/types.js';
3
+ import { type CreateSchemaOptions, type EntityWhereMeta, type IndexSchema, type NamingStrategy, type SchemaDiff, type SchemaGenerator, type Type } from '../../type/index.js';
4
+ import type { AnyMigrationOperation } from '../builder/types.js';
5
+ export declare class MongoSchemaGenerator extends MongoDialect implements SchemaGenerator {
6
6
  protected readonly defaultForeignKeyAction?: ForeignKeyAction | undefined;
7
- readonly dialectName = "mongodb";
8
- protected readonly featureDefaults: import("../../type/dialect.js").DialectFeatures;
9
- readonly insertIdSource: InsertIdSource;
10
7
  constructor(namingStrategy?: NamingStrategy, defaultForeignKeyAction?: ForeignKeyAction | undefined);
11
8
  /** A collection has no SQL to render a check, a computed column or an index expression into. */
12
9
  compileDdl(): string;
@@ -19,13 +16,18 @@ export declare class MongoSchemaGenerator extends AbstractDialect implements Sch
19
16
  generateDropSchema(entities: readonly Type<object>[]): string[];
20
17
  private selected;
21
18
  /**
22
- * The indexes `@Field({ index })` declares, as the collection would hold them.
23
- *
24
- * One owner because two paths need it: creating a collection, and working out which of its indexes
25
- * are missing. Derived twice, they drifted the moment either changed how a name is settled.
19
+ * The indexes an entity declares, as the collection would hold them. One owner because two paths need
20
+ * it: creating a collection, and working out which of its indexes are missing.
21
+ */
22
+ private indexesOf;
23
+ /** One declared index, its members resolved to document paths and its `where` to a filter document. */
24
+ private indexSchema;
25
+ /**
26
+ * The JSON of the document `partialFilterExpression` takes, refused where the predicate reaches past
27
+ * what that holds or what a migration carries as JSON.
26
28
  */
27
- private fieldIndexes;
28
- generateCreateTable<E>(entity: Type<E>, _options?: {
29
+ compileIndexPredicate(where: EntityWhereMeta<object>, entity: Type<object>, indexName: string): string;
30
+ generateCreateTable<E extends object>(entity: Type<E>, _options?: {
29
31
  ifNotExists?: boolean;
30
32
  }): string[];
31
33
  generateDropTable(tableName: string): string;
@@ -35,20 +37,13 @@ export declare class MongoSchemaGenerator extends AbstractDialect implements Sch
35
37
  * MongoDB's key spec is where its index options live: `-1` for a descending entry and `'text'` for a
36
38
  * full-text index, which is what `$text` needs since a text index declares its own fields.
37
39
  *
38
- * @remarks The SQL-only modifiers are refused rather than dropped, for the same reason the SQL
39
- * dialects refuse each other's - a silently weaker index is worse than a clear failure. `where`
40
- * included: MongoDB's partial indexes take a filter document, not a SQL predicate.
40
+ * @remarks The SQL-only options are refused by the checks the SQL dialects refuse each other's with - a
41
+ * silently weaker index is worse than a clear failure. `where` is the filter's JSON, as
42
+ * {@link compileIndexPredicate} writes it.
41
43
  */
42
44
  generateCreateIndex(tableName: string, index: IndexSchema): string;
43
45
  generateDropIndex(tableName: string, indexName: string): string;
44
- getSqlType(fieldOptions: FieldOptions): string;
45
- generateCreateTableFromNode(table: TableNode, _options?: {
46
- ifNotExists?: boolean;
47
- }): string[];
48
- generateCreateIndexFromNode(index: IndexNode): string;
49
- generateCreateTableFromDefinition(table: TableDefinition, _options?: {
50
- ifNotExists?: boolean;
51
- }): string[];
52
- generateRenameTableSql(oldName: string, newName: string): string;
46
+ /** A collection and its indexes, which is all a document store has: a column, a constraint or SQL throws. */
47
+ generateOperation(operation: AnyMigrationOperation): string[];
53
48
  diffSchema(entity: Type<object>, currentTable: TableNode | undefined): SchemaDiff | undefined;
54
49
  }
@@ -1,16 +1,18 @@
1
- import { AbstractDialect } from '../../dialect/abstractDialect.js';
2
1
  import { getMeta } from '../../entity/index.js';
3
- import { mongoDialectFeatures } from '../../mongo/mongoDialect.js';
4
- import { getKeys } from '../../util/index.js';
2
+ import { MongoDialect } from '../../mongo/mongoDialect.js';
3
+ import { QueryRaw, } from '../../type/index.js';
4
+ import { declaredIndexes, indexNameParts, renderIndexColumn } from '../../util/ddlExpression.util.js';
5
5
  import { derivedIndexName } from '../../util/sql.util.js';
6
+ import { assertIndexFeatures, assertIndexType } from '../ddl/indexDdl.js';
7
+ import { assertIndexPredicate, refusedIndexPredicate } from '../indexPredicate.js';
6
8
  import { renderIndexDefinition } from './definitionToNode.js';
7
- import { indexNodeToSchema } from './indexNodeToSchema.js';
8
9
  import { serializeMongoCommand } from './mongoCommand.js';
9
- export class MongoSchemaGenerator extends AbstractDialect {
10
+ /** The index types a key spec can say: a plain key, or `'text'`. */
11
+ const MONGO_INDEX_TYPES = new Set(['btree', 'fulltext']);
12
+ /** A key spec's one feature beyond its keys: a partial filter. */
13
+ const MONGO_INDEX_FEATURES = new Set(['partial']);
14
+ export class MongoSchemaGenerator extends MongoDialect {
10
15
  defaultForeignKeyAction;
11
- dialectName = 'mongodb';
12
- featureDefaults = mongoDialectFeatures;
13
- insertIdSource = 'returning';
14
16
  constructor(namingStrategy, defaultForeignKeyAction) {
15
17
  super({ namingStrategy });
16
18
  this.defaultForeignKeyAction = defaultForeignKeyAction;
@@ -38,113 +40,117 @@ export class MongoSchemaGenerator extends AbstractDialect {
38
40
  return entities.filter((entity) => wanted.has(this.resolveTableName(getMeta(entity))));
39
41
  }
40
42
  /**
41
- * The indexes `@Field({ index })` declares, as the collection would hold them.
42
- *
43
- * One owner because two paths need it: creating a collection, and working out which of its indexes
44
- * are missing. Derived twice, they drifted the moment either changed how a name is settled.
43
+ * The indexes an entity declares, as the collection would hold them. One owner because two paths need
44
+ * it: creating a collection, and working out which of its indexes are missing.
45
45
  */
46
- fieldIndexes(meta, collectionName) {
47
- return getKeys(meta.fields).flatMap((key) => {
48
- const field = meta.fields[key];
49
- if (!field?.index) {
50
- return [];
51
- }
52
- const columnName = this.resolveColumnName(key, field);
53
- return [
54
- {
55
- name: typeof field.index === 'string' ? field.index : derivedIndexName(collectionName, [columnName]),
56
- entries: [{ column: columnName }],
57
- unique: !!field.unique,
58
- },
59
- ];
60
- });
46
+ indexesOf(meta, collectionName) {
47
+ return declaredIndexes(meta).map((index) => this.indexSchema(meta, collectionName, index));
48
+ }
49
+ /** One declared index, its members resolved to document paths and its `where` to a filter document. */
50
+ indexSchema(meta, collectionName, index) {
51
+ const entries = index.columns
52
+ .map((entry) => renderIndexColumn(entry, () => this.compileDdl()))
53
+ .map((entry) => ({ ...entry, column: this.columnOf(meta, entry.column) }));
54
+ const name = index.name ?? derivedIndexName(collectionName, indexNameParts(entries));
55
+ return {
56
+ name,
57
+ entries,
58
+ unique: index.unique ?? false,
59
+ type: index.type,
60
+ include: index.include,
61
+ where: index.where && this.compileIndexPredicate(index.where, meta.entity, name),
62
+ };
63
+ }
64
+ /**
65
+ * The JSON of the document `partialFilterExpression` takes, refused where the predicate reaches past
66
+ * what that holds or what a migration carries as JSON.
67
+ */
68
+ compileIndexPredicate(where, entity, indexName) {
69
+ if (where instanceof QueryRaw) {
70
+ throw new TypeError(`mongodb does not support partial indexes from a SQL predicate (index "${indexName}")`);
71
+ }
72
+ assertIndexPredicate(where, this.dialectName, indexName);
73
+ const filter = this.renderFilter(entity, where);
74
+ const refused = refusedFilterValue(filter);
75
+ if (refused) {
76
+ throw refusedIndexPredicate(this.dialectName, refused, indexName);
77
+ }
78
+ return JSON.stringify(filter);
61
79
  }
62
80
  generateCreateTable(entity, _options) {
63
81
  const meta = getMeta(entity);
64
82
  const collectionName = this.resolveTableName(meta);
65
- const indexes = this.fieldIndexes(meta, collectionName);
66
83
  // One `createIndex` command each, mirroring the SQL generator's `[CREATE TABLE, ...CREATE INDEX]`,
67
84
  // so the key spec is built here and the migrator only executes it.
68
85
  return [
69
86
  serializeMongoCommand({ action: 'createCollection', name: collectionName }),
70
- ...indexes.map((index) => this.generateCreateIndex(collectionName, index)),
87
+ ...this.indexesOf(meta, collectionName).map((index) => this.generateCreateIndex(collectionName, index)),
71
88
  ];
72
89
  }
73
90
  generateDropTable(tableName) {
74
91
  return serializeMongoCommand({ action: 'dropCollection', name: tableName });
75
92
  }
76
93
  generateAlterTable(diff) {
77
- const statements = [];
78
- if (diff.indexesToAdd?.length) {
79
- for (const index of diff.indexesToAdd) {
80
- statements.push(this.generateCreateIndex(diff.tableName, index));
81
- }
82
- }
83
- return statements;
94
+ return (diff.indexesToAdd ?? []).map((index) => this.generateCreateIndex(diff.tableName, index));
84
95
  }
85
96
  generateAlterTableDown(diff) {
86
- const statements = [];
87
- if (diff.indexesToAdd?.length) {
88
- for (const index of diff.indexesToAdd) {
89
- statements.push(this.generateDropIndex(diff.tableName, index.name));
90
- }
91
- }
92
- return statements;
97
+ return (diff.indexesToAdd ?? []).map((index) => this.generateDropIndex(diff.tableName, index.name));
93
98
  }
94
99
  /**
95
100
  * MongoDB's key spec is where its index options live: `-1` for a descending entry and `'text'` for a
96
101
  * full-text index, which is what `$text` needs since a text index declares its own fields.
97
102
  *
98
- * @remarks The SQL-only modifiers are refused rather than dropped, for the same reason the SQL
99
- * dialects refuse each other's - a silently weaker index is worse than a clear failure. `where`
100
- * included: MongoDB's partial indexes take a filter document, not a SQL predicate.
103
+ * @remarks The SQL-only options are refused by the checks the SQL dialects refuse each other's with - a
104
+ * silently weaker index is worse than a clear failure. `where` is the filter's JSON, as
105
+ * {@link compileIndexPredicate} writes it.
101
106
  */
102
107
  generateCreateIndex(tableName, index) {
108
+ assertIndexType(index, MONGO_INDEX_TYPES, this.dialectName);
109
+ assertIndexFeatures(index, MONGO_INDEX_FEATURES, this.dialectName);
103
110
  const key = {};
104
111
  for (const entry of index.entries) {
105
- if (entry.expression || entry.jsonArray || entry.length !== undefined || entry.nulls || entry.opsClass) {
106
- throw new TypeError(`mongodb does not support that index column option (index "${index.name}")`);
107
- }
108
112
  key[entry.column] = index.type === 'fulltext' ? 'text' : entry.order === 'desc' ? -1 : 1;
109
113
  }
110
- if (index.where) {
111
- throw new TypeError(`mongodb does not support partial indexes from a SQL predicate (index "${index.name}")`);
112
- }
113
114
  return serializeMongoCommand({
114
115
  action: 'createIndex',
115
116
  collection: tableName,
116
117
  name: index.name,
117
118
  key,
118
- options: { unique: index.unique, name: index.name },
119
+ options: {
120
+ unique: index.unique,
121
+ name: index.name,
122
+ partialFilterExpression: index.where && JSON.parse(index.where),
123
+ },
119
124
  });
120
125
  }
121
126
  generateDropIndex(tableName, indexName) {
122
- return serializeMongoCommand({
123
- action: 'dropIndex',
124
- collection: tableName,
125
- name: indexName,
126
- });
127
- }
128
- getSqlType(fieldOptions) {
129
- return '';
130
- }
131
- generateCreateTableFromNode(table, _options) {
132
- return [
133
- serializeMongoCommand({ action: 'createCollection', name: table.name }),
134
- ...table.indexes.map((index) => this.generateCreateIndexFromNode(index)),
135
- ];
136
- }
137
- generateCreateIndexFromNode(index) {
138
- return this.generateCreateIndex(index.table.name, indexNodeToSchema(index));
139
- }
140
- generateCreateTableFromDefinition(table, _options) {
141
- return [
142
- serializeMongoCommand({ action: 'createCollection', name: table.name }),
143
- ...table.indexes.map((index) => this.generateCreateIndex(table.name, renderIndexDefinition(index, () => this.compileDdl()))),
144
- ];
145
- }
146
- generateRenameTableSql(oldName, newName) {
147
- return serializeMongoCommand({ action: 'renameCollection', from: oldName, to: newName });
127
+ return serializeMongoCommand({ action: 'dropIndex', collection: tableName, name: indexName });
128
+ }
129
+ /** A collection and its indexes, which is all a document store has: a column, a constraint or SQL throws. */
130
+ generateOperation(operation) {
131
+ const render = (index) => renderIndexDefinition(index, () => this.compileDdl());
132
+ switch (operation.type) {
133
+ case 'createTable': {
134
+ const { name, columns, indexes } = operation.table;
135
+ if (columns.length) {
136
+ throw new TypeError(`mongodb does not support columns in a migration (collection "${name}")`);
137
+ }
138
+ return [
139
+ serializeMongoCommand({ action: 'createCollection', name }),
140
+ ...indexes.map((index) => this.generateCreateIndex(name, render(index))),
141
+ ];
142
+ }
143
+ case 'dropTable':
144
+ return [this.generateDropTable(operation.tableName)];
145
+ case 'renameTable':
146
+ return [serializeMongoCommand({ action: 'renameCollection', from: operation.oldName, to: operation.newName })];
147
+ case 'createIndex':
148
+ return [this.generateCreateIndex(operation.tableName, render(operation.index))];
149
+ case 'dropIndex':
150
+ return [this.generateDropIndex(operation.tableName, operation.indexName)];
151
+ default:
152
+ throw new TypeError(`mongodb does not support ${operation.type} in a migration`);
153
+ }
148
154
  }
149
155
  diffSchema(entity, currentTable) {
150
156
  const meta = getMeta(entity);
@@ -153,7 +159,7 @@ export class MongoSchemaGenerator extends AbstractDialect {
153
159
  return { tableName: collectionName, type: 'create' };
154
160
  }
155
161
  const existingIndexes = new Set(currentTable.indexes.map((i) => i.name));
156
- const indexesToAdd = this.fieldIndexes(meta, collectionName).filter((index) => !existingIndexes.has(index.name));
162
+ const indexesToAdd = this.indexesOf(meta, collectionName).filter((index) => !existingIndexes.has(index.name));
157
163
  if (indexesToAdd.length === 0) {
158
164
  return undefined;
159
165
  }
@@ -164,3 +170,20 @@ export class MongoSchemaGenerator extends AbstractDialect {
164
170
  };
165
171
  }
166
172
  }
173
+ /** The first value `partialFilterExpression` refuses, `null`, or a migration cannot carry as JSON, such as a `Date`. */
174
+ function refusedFilterValue(value) {
175
+ if (value === null) {
176
+ return 'null';
177
+ }
178
+ if (Array.isArray(value)) {
179
+ return value.map(refusedFilterValue).find(Boolean);
180
+ }
181
+ if (typeof value !== 'object') {
182
+ return typeof value === 'bigint' ? 'a bigint' : undefined;
183
+ }
184
+ if (Object.getPrototypeOf(value) === Object.prototype) {
185
+ return Object.values(value).map(refusedFilterValue).find(Boolean);
186
+ }
187
+ const typeName = value.constructor.name;
188
+ return `${/^[aeiou]/i.test(typeName) ? 'an' : 'a'} ${typeName}`;
189
+ }
@@ -8,9 +8,9 @@ export * from './codegen/index.js';
8
8
  export * from './ddl/index.js';
9
9
  export * from './drift/index.js';
10
10
  export * from './introspection/index.js';
11
+ export { migrationBuilderFor } from './migrationTarget.js';
11
12
  export { type BuilderMigrationDefinition, defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
12
13
  export { createSchemaGenerator, SqlSchemaGenerator } from './schemaGenerator.js';
13
- export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
14
14
  export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
15
15
  export { JsonMigrationStorage } from './storage/jsonStorage.js';
16
16
  export { MongoMigrationStorage } from './storage/mongoStorage.js';
@@ -13,10 +13,10 @@ export * from './drift/index.js';
13
13
  // Schema introspection
14
14
  export * from './introspection/index.js';
15
15
  // Main migrator
16
+ export { migrationBuilderFor } from './migrationTarget.js';
16
17
  export { defineBuilderMigration, defineMigration, Migrator } from './migrator.js';
17
18
  // Schema generators
18
19
  export { createSchemaGenerator, SqlSchemaGenerator } from './schemaGenerator.js';
19
- export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
20
20
  // Storage implementations
21
21
  export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
22
22
  export { JsonMigrationStorage } from './storage/jsonStorage.js';
@@ -0,0 +1,8 @@
1
+ import type { DialectName, EntityWhereMeta } from '../type/index.js';
2
+ /**
3
+ * Refuses a partial index's predicate reaching past what the engine takes, before it compiles to what
4
+ * only the server would reject, naming the operator as its author wrote it. A `raw` one is left to the server.
5
+ */
6
+ export declare function assertIndexPredicate<E>(where: EntityWhereMeta<E>, dialectName: DialectName, indexName: string): void;
7
+ /** What refusing any part of a partial index's predicate reports. */
8
+ export declare function refusedIndexPredicate(dialectName: string, part: string, indexName: string): TypeError;
@@ -0,0 +1,52 @@
1
+ import { QueryRaw } from '../type/queryRaw.js';
2
+ import { isOperatorObject } from '../util/object.util.js';
3
+ /**
4
+ * The `$where` a partial index takes, on the engines where that is less than a query's: SQL Server's
5
+ * `CREATE INDEX` `<filter_predicate>` (comparisons and `IN` on plain columns, joined by `AND`), and
6
+ * MongoDB's `partialFilterExpression` (https://www.mongodb.com/docs/manual/core/index-partial/).
7
+ */
8
+ const PREDICATE_GRAMMARS = {
9
+ mssql: {
10
+ rootOps: new Set(['$and']),
11
+ fieldOps: new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in', '$isNull', '$isNotNull']),
12
+ jsonPaths: false,
13
+ },
14
+ mongodb: {
15
+ rootOps: new Set(['$and', '$or']),
16
+ fieldOps: new Set(['$eq', '$gt', '$gte', '$lt', '$lte', '$between', '$in']),
17
+ jsonPaths: true,
18
+ },
19
+ };
20
+ /**
21
+ * Refuses a partial index's predicate reaching past what the engine takes, before it compiles to what
22
+ * only the server would reject, naming the operator as its author wrote it. A `raw` one is left to the server.
23
+ */
24
+ export function assertIndexPredicate(where, dialectName, indexName) {
25
+ const grammar = PREDICATE_GRAMMARS[dialectName];
26
+ const refused = grammar && !(where instanceof QueryRaw) ? refusedOperator(where, grammar) : undefined;
27
+ if (refused) {
28
+ throw refusedIndexPredicate(dialectName, refused, indexName);
29
+ }
30
+ }
31
+ /** What refusing any part of a partial index's predicate reports. */
32
+ export function refusedIndexPredicate(dialectName, part, indexName) {
33
+ return new TypeError(`${dialectName} does not support ${part} in a partial index predicate (index "${indexName}")`);
34
+ }
35
+ /** The first part of `where` outside `grammar`, depth-first; a `raw` clause is left alone. */
36
+ function refusedOperator(where, grammar) {
37
+ return Object.entries(where)
38
+ .map(([key, value]) => {
39
+ if (key.includes('.') && !grammar.jsonPaths) {
40
+ return 'a JSON path';
41
+ }
42
+ if (!key.startsWith('$')) {
43
+ return isOperatorObject(value) ? Object.keys(value).find((op) => !grammar.fieldOps.has(op)) : undefined;
44
+ }
45
+ if (!grammar.rootOps.has(key)) {
46
+ return key;
47
+ }
48
+ const clauses = Array.isArray(value) ? value.filter((clause) => !(clause instanceof QueryRaw)) : [];
49
+ return clauses.map((clause) => refusedOperator(clause, grammar)).find(Boolean);
50
+ })
51
+ .find(Boolean);
52
+ }
@@ -0,0 +1,23 @@
1
+ import type { ForeignKeyAction } from '../schema/types.js';
2
+ import { type MigrationStorage, type MigratorDialect, type Querier, type QuerierPool, type SchemaGenerator } from '../type/index.js';
3
+ import { MigrationBuilder } from './builder/migrationBuilder.js';
4
+ import { type MigrationSource } from './codegen/migrationFile.js';
5
+ /** A migration querier as its engine family drives it. */
6
+ export type MigrationSession = {
7
+ readonly querier: Querier;
8
+ /** Runs one statement its generator wrote. */
9
+ run(statement: string): Promise<unknown>;
10
+ /** `work` in one transaction where the engine takes DDL in one; MongoDB creates collections outside any. */
11
+ transaction(work: () => Promise<void>): Promise<void>;
12
+ };
13
+ /** Everything a migrator does differently per engine family, chosen once from its dialect. */
14
+ export type MigrationTarget = {
15
+ readonly source: MigrationSource;
16
+ storage(pool: QuerierPool, tableName: string | undefined): MigrationStorage;
17
+ /** The schema generator, `undefined` for a dialect with none. Async because MongoDB's loads its optional peer. */
18
+ generator(dialect: MigratorDialect, defaultForeignKeyAction?: ForeignKeyAction): Promise<SchemaGenerator | undefined>;
19
+ withSession<T>(pool: QuerierPool, task: (session: MigrationSession) => Promise<T>): Promise<T>;
20
+ };
21
+ export declare function migrationTargetFor(dialect: MigratorDialect): MigrationTarget;
22
+ /** A builder running each operation on `querier`, as SQL or as MongoDB driver commands. */
23
+ export declare function migrationBuilderFor(querier: Querier): Promise<MigrationBuilder>;
@@ -0,0 +1,48 @@
1
+ import { isKnownMigratorDialect, isMongoQuerier, isSqlQuerier, } from '../type/index.js';
2
+ import { withMongoQuerierForMigrations, withSqlQuerierForMigrations } from './acquireQuerierForMigrations.js';
3
+ import { MigrationBuilder } from './builder/migrationBuilder.js';
4
+ import { migrationSource } from './codegen/migrationFile.js';
5
+ import { runMongoCommand } from './generator/mongoCommand.js';
6
+ import { createSchemaGenerator, SqlSchemaGenerator } from './schemaGenerator.js';
7
+ import { DatabaseMigrationStorage } from './storage/databaseStorage.js';
8
+ import { MongoMigrationStorage } from './storage/mongoStorage.js';
9
+ const sqlSession = (querier) => ({
10
+ querier,
11
+ run: (statement) => querier.run(statement),
12
+ transaction: (work) => querier.transaction(work),
13
+ });
14
+ const mongoSession = (querier) => ({
15
+ querier,
16
+ run: (statement) => runMongoCommand(querier.db, statement),
17
+ transaction: (work) => work(),
18
+ });
19
+ /** Imported on use, so the optional `mongodb` peer loads only on MongoDB. */
20
+ async function mongoSchemaGenerator(namingStrategy, defaultForeignKeyAction) {
21
+ const { MongoSchemaGenerator } = await import('./generator/mongoSchemaGenerator.js');
22
+ return new MongoSchemaGenerator(namingStrategy, defaultForeignKeyAction);
23
+ }
24
+ const sqlTarget = {
25
+ source: migrationSource.SqlQuerier,
26
+ storage: (pool, tableName) => new DatabaseMigrationStorage(pool, { tableName }),
27
+ generator: async (dialect, defaultForeignKeyAction) => isKnownMigratorDialect(dialect.dialectName) ? createSchemaGenerator(dialect, defaultForeignKeyAction) : undefined,
28
+ withSession: (pool, task) => withSqlQuerierForMigrations(pool, 'Migrator', (querier) => task(sqlSession(querier))),
29
+ };
30
+ const mongoTarget = {
31
+ source: migrationSource.MongoQuerier,
32
+ storage: (pool, tableName) => new MongoMigrationStorage(pool, { tableName }),
33
+ generator: (dialect, defaultForeignKeyAction) => mongoSchemaGenerator(dialect.namingStrategy, defaultForeignKeyAction),
34
+ withSession: (pool, task) => withMongoQuerierForMigrations(pool, 'Migrator', (querier) => task(mongoSession(querier))),
35
+ };
36
+ export function migrationTargetFor(dialect) {
37
+ return dialect.dialectName === 'mongodb' ? mongoTarget : sqlTarget;
38
+ }
39
+ /** A builder running each operation on `querier`, as SQL or as MongoDB driver commands. */
40
+ export async function migrationBuilderFor(querier) {
41
+ if (isSqlQuerier(querier)) {
42
+ return new MigrationBuilder(new SqlSchemaGenerator(querier.dialect), sqlSession(querier).run);
43
+ }
44
+ if (isMongoQuerier(querier)) {
45
+ return new MigrationBuilder(await mongoSchemaGenerator(), mongoSession(querier).run);
46
+ }
47
+ throw new TypeError('A migration builder needs a SQL or a MongoDB querier');
48
+ }