@mikro-orm/core 7.0.17-dev.9 → 7.0.18-dev.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 (69) hide show
  1. package/EntityManager.d.ts +106 -12
  2. package/EntityManager.js +228 -19
  3. package/MikroORM.d.ts +3 -3
  4. package/README.md +2 -1
  5. package/connections/Connection.d.ts +67 -1
  6. package/connections/Connection.js +64 -0
  7. package/drivers/DatabaseDriver.d.ts +1 -0
  8. package/drivers/DatabaseDriver.js +3 -0
  9. package/drivers/IDatabaseDriver.d.ts +50 -7
  10. package/entity/BaseEntity.d.ts +70 -3
  11. package/entity/BaseEntity.js +18 -0
  12. package/entity/Collection.d.ts +6 -3
  13. package/entity/Collection.js +15 -4
  14. package/entity/EntityAssigner.js +8 -0
  15. package/entity/EntityFactory.js +25 -4
  16. package/entity/EntityLoader.d.ts +8 -1
  17. package/entity/EntityLoader.js +41 -16
  18. package/entity/EntityRepository.d.ts +27 -9
  19. package/entity/EntityRepository.js +12 -0
  20. package/entity/Reference.d.ts +42 -1
  21. package/entity/Reference.js +9 -0
  22. package/entity/WrappedEntity.d.ts +2 -2
  23. package/entity/defineEntity.d.ts +92 -17
  24. package/entity/defineEntity.js +17 -6
  25. package/entity/utils.js +4 -5
  26. package/enums.d.ts +8 -1
  27. package/errors.d.ts +2 -0
  28. package/errors.js +4 -0
  29. package/index.d.ts +2 -2
  30. package/index.js +1 -1
  31. package/metadata/EntitySchema.js +3 -0
  32. package/metadata/MetadataDiscovery.d.ts +11 -0
  33. package/metadata/MetadataDiscovery.js +135 -10
  34. package/metadata/MetadataValidator.d.ts +26 -0
  35. package/metadata/MetadataValidator.js +236 -0
  36. package/metadata/Routine.d.ts +74 -0
  37. package/metadata/Routine.js +140 -0
  38. package/metadata/index.d.ts +1 -0
  39. package/metadata/index.js +1 -0
  40. package/metadata/types.d.ts +77 -4
  41. package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
  42. package/naming-strategy/NamingStrategy.d.ts +1 -1
  43. package/package.json +1 -1
  44. package/platforms/Platform.d.ts +7 -0
  45. package/platforms/Platform.js +15 -1
  46. package/serialization/EntitySerializer.d.ts +18 -6
  47. package/serialization/EntitySerializer.js +33 -6
  48. package/typings.d.ts +389 -18
  49. package/typings.js +21 -4
  50. package/unit-of-work/UnitOfWork.js +11 -3
  51. package/utils/AbstractMigrator.d.ts +26 -3
  52. package/utils/AbstractMigrator.js +264 -15
  53. package/utils/AbstractSchemaGenerator.d.ts +1 -1
  54. package/utils/AbstractSchemaGenerator.js +4 -1
  55. package/utils/Configuration.d.ts +40 -3
  56. package/utils/Configuration.js +46 -0
  57. package/utils/DataloaderUtils.d.ts +10 -1
  58. package/utils/DataloaderUtils.js +78 -0
  59. package/utils/QueryHelper.d.ts +16 -0
  60. package/utils/QueryHelper.js +15 -0
  61. package/utils/TransactionManager.js +2 -0
  62. package/utils/Utils.d.ts +1 -1
  63. package/utils/Utils.js +1 -1
  64. package/utils/fs-utils.d.ts +2 -0
  65. package/utils/fs-utils.js +7 -1
  66. package/utils/index.d.ts +1 -0
  67. package/utils/index.js +1 -0
  68. package/utils/partition-utils.d.ts +17 -0
  69. package/utils/partition-utils.js +79 -0
package/typings.js CHANGED
@@ -4,7 +4,6 @@ import { EntityHelper } from './entity/EntityHelper.js';
4
4
  import { helper } from './entity/wrap.js';
5
5
  import { Utils } from './utils/Utils.js';
6
6
  import { EntityComparator } from './utils/EntityComparator.js';
7
- import { BaseEntity } from './entity/BaseEntity.js';
8
7
  /** Symbol used to declare a custom repository type on an entity class (e.g., `[EntityRepositoryType]?: BookRepository`). */
9
8
  export const EntityRepositoryType = Symbol('EntityRepositoryType');
10
9
  /** Symbol used to declare the primary key property name(s) on an entity (e.g., `[PrimaryKeyProp]?: 'id'`). */
@@ -22,6 +21,16 @@ export const Config = Symbol('Config');
22
21
  /** Symbol used to declare the entity name as a string literal type (used by `defineEntity`). */
23
22
  // eslint-disable-next-line @typescript-eslint/no-redeclare
24
23
  export const EntityName = Symbol('EntityName');
24
+ /**
25
+ * Symbol used to declare index-to-column mappings on an entity type.
26
+ * For decorator entities, declare as a phantom property:
27
+ * ```typescript
28
+ * [IndexHints]?: { idx_email: 'email'; idx_name_age: 'name' | 'age' };
29
+ * ```
30
+ * For `defineEntity` entities, index hints are inferred automatically from
31
+ * named indexes (property-level `.index('name')` and entity-level `indexes`/`uniques`).
32
+ */
33
+ export const IndexHints = Symbol('IndexHints');
25
34
  /**
26
35
  * Runtime metadata for an entity, holding its properties, relations, indexes, hooks, and more.
27
36
  * Created during metadata discovery and used throughout the ORM lifecycle.
@@ -39,12 +48,20 @@ export class EntityMetadata {
39
48
  this.indexes = [];
40
49
  this.uniques = [];
41
50
  this.checks = [];
51
+ this.triggers = [];
42
52
  this.referencingProperties = [];
43
53
  this.concurrencyCheckKeys = new Set();
44
54
  Object.assign(this, meta);
45
55
  const name = meta.className ?? meta.name;
46
56
  if (!this.class && name) {
47
- const Class = this.extends === BaseEntity ? { [name]: class extends BaseEntity {
57
+ let Parent;
58
+ if (typeof this.extends === 'function') {
59
+ Parent = this.extends;
60
+ }
61
+ else if (this.extends != null && typeof this.extends.class === 'function') {
62
+ Parent = this.extends.class;
63
+ }
64
+ const Class = Parent ? { [name]: class extends Parent {
48
65
  } }[name] : { [name]: class {
49
66
  } }[name];
50
67
  this.class = Class;
@@ -264,8 +281,8 @@ export class EntityMetadata {
264
281
  }, { __gettersDefined: { value: true, enumerable: false } });
265
282
  }
266
283
  initIndexes(prop) {
267
- const simpleIndex = this.indexes.find(index => index.properties === prop.name && !index.options && !index.type && !index.expression);
268
- const simpleUnique = this.uniques.find(index => index.properties === prop.name && !index.options);
284
+ const simpleIndex = this.indexes.find(index => index.properties === prop.name && !index.options && !index.type && !index.expression && index.where == null);
285
+ const simpleUnique = this.uniques.find(index => index.properties === prop.name && !index.options && !index.expression && index.where == null);
269
286
  const owner = prop.kind === ReferenceKind.MANY_TO_ONE;
270
287
  if (!prop.index && simpleIndex) {
271
288
  Utils.defaultValue(simpleIndex, 'name', true);
@@ -36,6 +36,13 @@ export class UnitOfWork {
36
36
  #flushQueue = [];
37
37
  #working = false;
38
38
  #em;
39
+ /**
40
+ * UoW lives in `@mikro-orm/core` alongside `EntityManager` and reaches `getAbortOptions`
41
+ * (TS-protected) via cast — TS has no `friend` keyword, so this is the documented escape hatch.
42
+ */
43
+ get #abortOptions() {
44
+ return this.#em.getAbortOptions();
45
+ }
39
46
  constructor(em) {
40
47
  this.#em = em;
41
48
  this.#metadata = this.#em.getMetadata();
@@ -1002,7 +1009,7 @@ export class UnitOfWork {
1002
1009
  this.findExtraUpdates(changeSet, props);
1003
1010
  await this.runHooks(EventType.beforeCreate, changeSet, true);
1004
1011
  }
1005
- await this.#changeSetPersister.executeInserts(changeSets, { ctx });
1012
+ await this.#changeSetPersister.executeInserts(changeSets, { ctx, ...this.#abortOptions });
1006
1013
  for (const changeSet of changeSets) {
1007
1014
  // For TPT entities, use the full entity snapshot instead of the partial changeset payload,
1008
1015
  // since each table's changeset only contains its own properties. Without this, the snapshot
@@ -1071,7 +1078,7 @@ export class UnitOfWork {
1071
1078
  for (const changeSet of changeSets) {
1072
1079
  await this.runHooks(EventType.beforeUpdate, changeSet, true);
1073
1080
  }
1074
- await this.#changeSetPersister.executeUpdates(changeSets, batched, { ctx });
1081
+ await this.#changeSetPersister.executeUpdates(changeSets, batched, { ctx, ...this.#abortOptions });
1075
1082
  for (const changeSet of changeSets) {
1076
1083
  const wrapped = helper(changeSet.entity);
1077
1084
  wrapped.__originalEntityData = this.#comparator.prepareEntity(changeSet.entity);
@@ -1094,7 +1101,7 @@ export class UnitOfWork {
1094
1101
  for (const changeSet of changeSets) {
1095
1102
  await this.runHooks(EventType.beforeDelete, changeSet, true);
1096
1103
  }
1097
- await this.#changeSetPersister.executeDeletes(changeSets, { ctx });
1104
+ await this.#changeSetPersister.executeDeletes(changeSets, { ctx, ...this.#abortOptions });
1098
1105
  for (const changeSet of changeSets) {
1099
1106
  this.unsetIdentity(changeSet.entity);
1100
1107
  await this.runHooks(EventType.afterDelete, changeSet);
@@ -1132,6 +1139,7 @@ export class UnitOfWork {
1132
1139
  ctx,
1133
1140
  schema: this.#em.schema,
1134
1141
  loggerContext,
1142
+ ...this.#abortOptions,
1135
1143
  });
1136
1144
  for (const coll of this.#collectionUpdates) {
1137
1145
  coll.takeSnapshot();
@@ -1,4 +1,4 @@
1
- import type { Constructor, IMigrationGenerator, IMigrationRunner, IMigrator, IMigratorStorage, MaybePromise, Migration, MigrationInfo, MigrationRow, MigratorEvent } from '../typings.js';
1
+ import type { Constructor, IMigrationGenerator, IMigrationRunner, IMigrator, IMigratorStorage, MaybePromise, Migration, MigrationInfo, MigrationResult, MigrationRow, MigratorEvent } from '../typings.js';
2
2
  import type { Transaction } from '../connections/Connection.js';
3
3
  import type { Configuration, MigrationsOptions } from './Configuration.js';
4
4
  import type { EntityManagerType, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
@@ -13,6 +13,7 @@ type MigrateOptions = {
13
13
  to?: string | number;
14
14
  migrations?: string[];
15
15
  transaction?: Transaction;
16
+ schema?: string;
16
17
  };
17
18
  export declare abstract class AbstractMigrator<D extends IDatabaseDriver> implements IMigrator {
18
19
  #private;
@@ -57,11 +58,15 @@ export declare abstract class AbstractMigrator<D extends IDatabaseDriver> implem
57
58
  /**
58
59
  * @inheritDoc
59
60
  */
60
- getExecuted(): Promise<MigrationRow[]>;
61
+ getExecuted(options?: {
62
+ schema?: string;
63
+ }): Promise<MigrationRow[]>;
61
64
  /**
62
65
  * @inheritDoc
63
66
  */
64
- getPending(): Promise<MigrationInfo[]>;
67
+ getPending(options?: {
68
+ schema?: string;
69
+ }): Promise<MigrationInfo[]>;
65
70
  /**
66
71
  * @inheritDoc
67
72
  */
@@ -70,7 +75,25 @@ export declare abstract class AbstractMigrator<D extends IDatabaseDriver> implem
70
75
  * @inheritDoc
71
76
  */
72
77
  down(options?: string | string[] | Omit<MigrateOptions, 'from'>): Promise<MigrationInfo[]>;
78
+ /**
79
+ * @inheritDoc
80
+ */
81
+ rollup(migrations?: string[]): Promise<MigrationResult>;
82
+ /**
83
+ * Extracts the body of a method from migration source code using brace counting.
84
+ * Returns the raw lines between the opening and closing braces, or empty string if not found.
85
+ * @internal
86
+ */
87
+ private extractMethodBody;
73
88
  abstract getStorage(): IMigratorStorage;
89
+ /**
90
+ * @inheritDoc
91
+ */
92
+ logMigration(name: string): Promise<void>;
93
+ /**
94
+ * @inheritDoc
95
+ */
96
+ unlogMigration(name: string): Promise<void>;
74
97
  protected init(): Promise<void>;
75
98
  protected initPaths(): Promise<void>;
76
99
  protected initServices(): void;
@@ -38,18 +38,32 @@ export class AbstractMigrator {
38
38
  /**
39
39
  * @inheritDoc
40
40
  */
41
- async getExecuted() {
41
+ async getExecuted(options) {
42
42
  await this.init();
43
- return this.storage.getExecutedMigrations();
43
+ const schema = options?.schema ?? this.options.schema;
44
+ this.storage.setRunSchema?.(schema);
45
+ try {
46
+ return await this.storage.getExecutedMigrations();
47
+ }
48
+ finally {
49
+ this.storage.unsetRunSchema?.();
50
+ }
44
51
  }
45
52
  /**
46
53
  * @inheritDoc
47
54
  */
48
- async getPending() {
55
+ async getPending(options) {
49
56
  await this.init();
50
- const all = await this.discoverMigrations();
51
- const executed = new Set(await this.storage.executed());
52
- return all.filter(m => !executed.has(m.name)).map(m => ({ name: m.name, path: m.path }));
57
+ const schema = options?.schema ?? this.options.schema;
58
+ this.storage.setRunSchema?.(schema);
59
+ try {
60
+ const all = await this.discoverMigrations();
61
+ const executed = new Set(await this.storage.executed());
62
+ return all.filter(m => !executed.has(m.name)).map(m => ({ name: m.name, path: m.path }));
63
+ }
64
+ finally {
65
+ this.storage.unsetRunSchema?.();
66
+ }
53
67
  }
54
68
  /**
55
69
  * @inheritDoc
@@ -63,6 +77,225 @@ export class AbstractMigrator {
63
77
  async down(options) {
64
78
  return this.runMigrations('down', options);
65
79
  }
80
+ /**
81
+ * @inheritDoc
82
+ */
83
+ async rollup(migrations) {
84
+ await this.init();
85
+ const { fs } = await import('@mikro-orm/core/fs-utils');
86
+ const all = await this.discoverMigrations();
87
+ const executedSet = new Set(await this.storage.executed());
88
+ let toRollup;
89
+ if (migrations && migrations.length > 0) {
90
+ const requested = new Set(migrations.map(m => this.getMigrationFilename(m)));
91
+ toRollup = all.filter(m => requested.has(m.name));
92
+ const found = new Set(toRollup.map(m => m.name));
93
+ const notFound = [...requested].filter(name => !found.has(name));
94
+ if (notFound.length > 0) {
95
+ throw new Error(`Migrations not found: ${notFound.join(', ')}`);
96
+ }
97
+ const notExecuted = toRollup.filter(m => !executedSet.has(m.name));
98
+ if (notExecuted.length > 0) {
99
+ throw new Error(`Cannot roll up migrations that have not been executed: ${notExecuted.map(m => m.name).join(', ')}`);
100
+ }
101
+ }
102
+ else {
103
+ toRollup = all.filter(m => executedSet.has(m.name));
104
+ }
105
+ if (toRollup.length < 2) {
106
+ throw new Error('At least 2 executed migrations are required for rollup');
107
+ }
108
+ const withoutPath = toRollup.filter(m => !m.path);
109
+ if (withoutPath.length > 0) {
110
+ throw new Error(`Cannot roll up migrations without file paths (class-based migrations): ${withoutPath.map(m => m.name).join(', ')}`);
111
+ }
112
+ const upBodies = [];
113
+ const downBodies = [];
114
+ const placeholder = `__mikro_orm_rollup_${Date.now()}__`;
115
+ for (const migration of toRollup) {
116
+ const source = await fs.readFile(migration.path);
117
+ const upBody = this.extractMethodBody(source, 'up');
118
+ const downBody = this.extractMethodBody(source, 'down');
119
+ if (upBody) {
120
+ upBodies.push(` // --- merged from ${migration.name} ---\n${upBody}`);
121
+ }
122
+ if (downBody) {
123
+ downBodies.unshift(` // --- merged from ${migration.name} ---\n${downBody}`);
124
+ }
125
+ }
126
+ const diff = {
127
+ up: [placeholder],
128
+ down: downBodies.length > 0 ? [placeholder] : [],
129
+ };
130
+ const [templateCode, fileName] = await this.generator.generate(diff);
131
+ const placeholderRe = new RegExp(`^.*${placeholder}.*$`, 'm');
132
+ let code = templateCode.replace(placeholderRe, upBodies.join('\n'));
133
+ if (downBodies.length > 0) {
134
+ code = code.replace(placeholderRe, downBodies.join('\n'));
135
+ }
136
+ await fs.writeFile(fs.normalizePath(this.absolutePath, fileName), code, { flush: true });
137
+ const updateStorage = async () => {
138
+ for (const migration of toRollup) {
139
+ await this.storage.unlogMigration({ name: migration.name });
140
+ }
141
+ await this.storage.logMigration({ name: fileName.replace(/\.[jt]s$/, '') });
142
+ };
143
+ if (this.options.transactional) {
144
+ await this.driver.getConnection().transactional(async (trx) => {
145
+ this.storage.setMasterMigration(trx);
146
+ try {
147
+ await updateStorage();
148
+ }
149
+ finally {
150
+ this.storage.unsetMasterMigration();
151
+ }
152
+ });
153
+ }
154
+ else {
155
+ await updateStorage();
156
+ }
157
+ await Promise.all(toRollup.map(migration => fs.unlink(migration.path)));
158
+ return { fileName, code, diff: { up: [], down: [] } };
159
+ }
160
+ /**
161
+ * Extracts the body of a method from migration source code using brace counting.
162
+ * Returns the raw lines between the opening and closing braces, or empty string if not found.
163
+ * @internal
164
+ */
165
+ extractMethodBody(source, methodName) {
166
+ const lines = source.split('\n');
167
+ // match method declarations, not occurrences in comments/strings — require preceding whitespace or keyword
168
+ const methodPattern = new RegExp(`^\\s+(?:override\\s+|async\\s+)*${methodName}\\s*\\(`);
169
+ let methodLine = -1;
170
+ for (let i = 0; i < lines.length; i++) {
171
+ if (methodPattern.test(lines[i])) {
172
+ methodLine = i;
173
+ break;
174
+ }
175
+ }
176
+ if (methodLine === -1) {
177
+ return '';
178
+ }
179
+ let braceCount = 0;
180
+ let bodyStart = -1;
181
+ let bodyEnd = -1;
182
+ let bodyStartCol = -1;
183
+ let bodyEndCol = -1;
184
+ let inBacktick = false;
185
+ let inBlockComment = false;
186
+ // stack tracks brace depth at which each template expression `${...}` was entered
187
+ const templateExprStack = [];
188
+ for (let i = methodLine; i < lines.length; i++) {
189
+ const line = lines[i];
190
+ for (let j = 0; j < line.length; j++) {
191
+ // handle multi-line block comments
192
+ if (inBlockComment) {
193
+ if (line[j] === '*' && j + 1 < line.length && line[j + 1] === '/') {
194
+ inBlockComment = false;
195
+ j++;
196
+ }
197
+ continue;
198
+ }
199
+ // handle multi-line template literals
200
+ if (inBacktick) {
201
+ if (line[j] === '\\') {
202
+ j++;
203
+ }
204
+ else if (line[j] === '`') {
205
+ inBacktick = false;
206
+ }
207
+ else if (line[j] === '$' && j + 1 < line.length && line[j + 1] === '{') {
208
+ // entering template expression — resume brace counting
209
+ templateExprStack.push(braceCount);
210
+ inBacktick = false;
211
+ j++; // skip the {
212
+ braceCount++;
213
+ }
214
+ continue;
215
+ }
216
+ const ch = line[j];
217
+ // single/double quoted strings (single-line only)
218
+ if (ch === "'" || ch === '"') {
219
+ const quote = ch;
220
+ j++;
221
+ while (j < line.length) {
222
+ if (line[j] === '\\') {
223
+ j++;
224
+ }
225
+ else if (line[j] === quote) {
226
+ break;
227
+ }
228
+ j++;
229
+ }
230
+ continue;
231
+ }
232
+ // template literal start
233
+ if (ch === '`') {
234
+ inBacktick = true;
235
+ continue;
236
+ }
237
+ // single-line comment
238
+ if (ch === '/' && j + 1 < line.length && line[j + 1] === '/') {
239
+ break;
240
+ }
241
+ // block comment start
242
+ if (ch === '/' && j + 1 < line.length && line[j + 1] === '*') {
243
+ inBlockComment = true;
244
+ j++;
245
+ continue;
246
+ }
247
+ if (ch === '{') {
248
+ if (braceCount === 0) {
249
+ bodyStart = i;
250
+ bodyStartCol = j + 1;
251
+ }
252
+ braceCount++;
253
+ }
254
+ else if (ch === '}') {
255
+ braceCount--;
256
+ // closing a template expression — re-enter backtick mode
257
+ if (templateExprStack.length > 0 && braceCount === templateExprStack[templateExprStack.length - 1]) {
258
+ templateExprStack.pop();
259
+ inBacktick = true;
260
+ continue;
261
+ }
262
+ if (braceCount === 0) {
263
+ bodyEnd = i;
264
+ bodyEndCol = j;
265
+ break;
266
+ }
267
+ }
268
+ }
269
+ if (bodyEnd !== -1) {
270
+ break;
271
+ }
272
+ }
273
+ if (bodyStart === -1 || bodyEnd === -1) {
274
+ return '';
275
+ }
276
+ // single-line method body: extract content between braces on the same line
277
+ if (bodyStart === bodyEnd) {
278
+ const content = lines[bodyStart].slice(bodyStartCol, bodyEndCol).trim();
279
+ return content ? ` ${content}` : '';
280
+ }
281
+ return lines.slice(bodyStart + 1, bodyEnd).join('\n');
282
+ }
283
+ /**
284
+ * @inheritDoc
285
+ */
286
+ async logMigration(name) {
287
+ await this.init();
288
+ await this.storage.ensureTable?.();
289
+ await this.storage.logMigration({ name });
290
+ }
291
+ /**
292
+ * @inheritDoc
293
+ */
294
+ async unlogMigration(name) {
295
+ await this.init();
296
+ await this.storage.ensureTable?.();
297
+ await this.storage.unlogMigration({ name });
298
+ }
66
299
  async init() {
67
300
  if (this.initialized) {
68
301
  return;
@@ -270,13 +503,17 @@ export class AbstractMigrator {
270
503
  return /^\d{14}$/.exec(name) ? this.options.fileName(name) : name;
271
504
  }
272
505
  prefix(options) {
506
+ const base = {};
507
+ if (this.options.schema) {
508
+ base.schema = this.options.schema;
509
+ }
273
510
  if (typeof options === 'string' || Array.isArray(options)) {
274
- return { migrations: Utils.asArray(options).map(name => this.getMigrationFilename(name)) };
511
+ return { ...base, migrations: Utils.asArray(options).map(name => this.getMigrationFilename(name)) };
275
512
  }
276
513
  if (!options) {
277
- return {};
514
+ return base;
278
515
  }
279
- const result = {};
516
+ const result = base;
280
517
  if (options.migrations) {
281
518
  result.migrations = options.migrations.map(name => this.getMigrationFilename(name));
282
519
  }
@@ -289,23 +526,35 @@ export class AbstractMigrator {
289
526
  else if (options.to === 0) {
290
527
  result.to = 0;
291
528
  }
529
+ if (options.schema !== undefined) {
530
+ result.schema = options.schema;
531
+ }
292
532
  return result;
293
533
  }
294
534
  async runMigrations(method, options) {
295
535
  await this.init();
296
- if (!this.options.transactional || !this.options.allOrNothing) {
297
- return this.executeMigrations(method, this.prefix(options));
536
+ const normalized = this.prefix(options);
537
+ this.runner.setRunSchema?.(normalized.schema);
538
+ this.storage.setRunSchema?.(normalized.schema);
539
+ try {
540
+ if (!this.options.transactional || !this.options.allOrNothing) {
541
+ return await this.executeMigrations(method, normalized);
542
+ }
543
+ if (Utils.isObject(options) && options.transaction) {
544
+ return await this.runInTransaction(options.transaction, method, normalized);
545
+ }
546
+ return await this.driver.getConnection().transactional(trx => this.runInTransaction(trx, method, normalized));
298
547
  }
299
- if (Utils.isObject(options) && options.transaction) {
300
- return this.runInTransaction(options.transaction, method, options);
548
+ finally {
549
+ this.runner.unsetRunSchema?.();
550
+ this.storage.unsetRunSchema?.();
301
551
  }
302
- return this.driver.getConnection().transactional(trx => this.runInTransaction(trx, method, options));
303
552
  }
304
553
  async runInTransaction(trx, method, options) {
305
554
  this.runner.setMasterMigration(trx);
306
555
  this.storage.setMasterMigration(trx);
307
556
  try {
308
- return await this.executeMigrations(method, this.prefix(options));
557
+ return await this.executeMigrations(method, options);
309
558
  }
310
559
  finally {
311
560
  this.runner.unsetMasterMigration();
@@ -34,6 +34,6 @@ export declare abstract class AbstractSchemaGenerator<D extends IDatabaseDriver>
34
34
  dropDatabase(name?: string): Promise<void>;
35
35
  execute(query: string): Promise<void>;
36
36
  ensureIndexes(): Promise<void>;
37
- protected getOrderedMetadata(schema?: string): EntityMetadata[];
37
+ protected getOrderedMetadata(schema?: string, includeWildcardSchema?: boolean): EntityMetadata[];
38
38
  protected notImplemented(): never;
39
39
  }
@@ -89,7 +89,7 @@ export class AbstractSchemaGenerator {
89
89
  async ensureIndexes() {
90
90
  this.notImplemented();
91
91
  }
92
- getOrderedMetadata(schema) {
92
+ getOrderedMetadata(schema, includeWildcardSchema = false) {
93
93
  const metadata = [...this.metadata.getAll().values()].filter(meta => {
94
94
  const isRootEntity = meta.root.class === meta.class;
95
95
  const isTPTChild = meta.inheritanceType === 'tpt' && meta.tptParent;
@@ -116,6 +116,9 @@ export class AbstractSchemaGenerator {
116
116
  .sort()
117
117
  .map(cls => this.metadata.getById(cls))
118
118
  .filter(meta => {
119
+ if (includeWildcardSchema && meta.schema === '*') {
120
+ return true;
121
+ }
119
122
  const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
120
123
  return schema ? [schema, '*'].includes(targetSchema) : meta.schema !== '*';
121
124
  });
@@ -6,6 +6,7 @@ import { type Logger, type LoggerNamespace, type LoggerOptions } from '../loggin
6
6
  import type { EntityManager } from '../EntityManager.js';
7
7
  import type { Platform } from '../platforms/Platform.js';
8
8
  import type { EntitySchema } from '../metadata/EntitySchema.js';
9
+ import { Routine } from '../metadata/Routine.js';
9
10
  import { MetadataProvider } from '../metadata/MetadataProvider.js';
10
11
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
11
12
  import type { EventSubscriber } from '../events/EventSubscriber.js';
@@ -18,7 +19,7 @@ import type { MikroORM } from '../MikroORM.js';
18
19
  /** Holds and validates all ORM configuration options, providing access to drivers, loggers, cache adapters, and other services. */
19
20
  export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager<D> = D[typeof EntityManagerType] & EntityManager<D>> {
20
21
  #private;
21
- constructor(options: Partial<Options>, validate?: boolean);
22
+ constructor(options: Partial<Options<any, any, any>>, validate?: boolean);
22
23
  /** Returns the database platform instance. */
23
24
  getPlatform(): ReturnType<D['getPlatform']>;
24
25
  /**
@@ -27,6 +28,10 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
27
28
  get<T extends keyof Options<D, EM>, U extends Options<D, EM>[T]>(key: T, defaultValue?: U): U;
28
29
  /** Returns all configuration options. */
29
30
  getAll(): Options<D, EM>;
31
+ /** Validates the `routines` config option on first access and throws on duplicate `(schema, name)` pairs or non-Routine entries. */
32
+ getRoutines(): readonly Routine[];
33
+ hasRoutine(routine: Routine): boolean;
34
+ private normaliseRoutines;
30
35
  /**
31
36
  * Overrides specified configuration value.
32
37
  */
@@ -99,7 +104,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
99
104
  /**
100
105
  * Type helper to make it easier to use `mikro-orm.config.js`.
101
106
  */
102
- export declare function defineConfig<D extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager<D> = EntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<D, EM, Entities>>): Partial<Options<D, EM, Entities>>;
107
+ export declare function defineConfig<D extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager<D> = EntityManager<D>, Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<D, EM, Entities>>): Partial<Options<D, EM, Entities>>;
103
108
  /**
104
109
  * Connection configuration options for database connections.
105
110
  * @see https://mikro-orm.io/docs/configuration#connection
@@ -238,6 +243,24 @@ export type MigrationsOptions = {
238
243
  fileName?: (timestamp: string, name?: string) => string;
239
244
  /** List of migration classes or objects to use instead of file-based discovery. */
240
245
  migrationsList?: (MigrationObject | Constructor<Migration>)[];
246
+ /**
247
+ * Target schema used when running migrations. When set, the driver's "set current schema" statement
248
+ * (e.g. `SET search_path` on PostgreSQL) is issued before each migration, and the migration tracking
249
+ * table lives in this schema. Can be overridden per call via `migrator.up({ schema })`.
250
+ *
251
+ * Intended for per-deployment-one-schema setups (e.g. PR previews) and for applying a single
252
+ * unqualified migration to multiple schemas (see `includeWildcardSchema`). Not supported on MSSQL.
253
+ */
254
+ schema?: string;
255
+ /**
256
+ * When set, entities with a wildcard schema (`schema: '*'`) are included in generated migrations.
257
+ * The emitted SQL is unqualified — and therefore safe to apply against any schema at runtime via
258
+ * `migrator.up({ schema })` — only when neither `options.schema` nor the ORM's `config.schema`
259
+ * is set. If `config.schema` is set, wildcard tables are qualified with it (useful for local
260
+ * dev runs); in that case, generate migrations from an environment where `config.schema` is unset.
261
+ * @default false
262
+ */
263
+ includeWildcardSchema?: boolean;
241
264
  };
242
265
  /**
243
266
  * Configuration options for database seeders.
@@ -352,7 +375,7 @@ export interface MetadataDiscoveryOptions {
352
375
  * MikroORM configuration options.
353
376
  * @see https://mikro-orm.io/docs/configuration
354
377
  */
355
- export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager<Driver> & Driver[typeof EntityManagerType] = EntityManager<Driver> & Driver[typeof EntityManagerType], Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> extends ConnectionOptions {
378
+ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager<Driver> & Driver[typeof EntityManagerType] = EntityManager<Driver> & Driver[typeof EntityManagerType], Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> extends ConnectionOptions {
356
379
  /** Connection pool configuration. */
357
380
  pool: PoolConfig;
358
381
  /**
@@ -390,6 +413,13 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
390
413
  * Can be class references or instances.
391
414
  */
392
415
  subscribers: Iterable<EventSubscriber | Constructor<EventSubscriber>>;
416
+ /**
417
+ * Stored procedures and functions, declared as {@link Routine} instances.
418
+ *
419
+ * @example
420
+ * routines: [HashUser, AddRecord]
421
+ */
422
+ routines: Iterable<Routine>;
393
423
  /**
394
424
  * Global entity filters to apply.
395
425
  * Filters are applied by default unless explicitly disabled.
@@ -529,6 +559,13 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
529
559
  * @default false
530
560
  */
531
561
  forceUndefined: boolean;
562
+ /**
563
+ * Initialize nullable properties to `null` (or `undefined` when `forceUndefined` is set)
564
+ * during `em.create()` when they are not provided in the data. Without this option,
565
+ * nullable properties remain `undefined` until the entity is loaded from the database.
566
+ * @default false
567
+ */
568
+ initNullableProperties: boolean;
532
569
  /**
533
570
  * Property `onCreate` hooks are normally executed during `flush` operation.
534
571
  * With this option, they will be processed early inside `em.create()` method.