@mikro-orm/core 7.0.17 → 7.0.18-dev.1
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.
- package/EntityManager.d.ts +106 -12
- package/EntityManager.js +228 -19
- package/MikroORM.d.ts +3 -3
- package/README.md +2 -1
- package/connections/Connection.d.ts +67 -1
- package/connections/Connection.js +64 -0
- package/drivers/DatabaseDriver.d.ts +1 -0
- package/drivers/DatabaseDriver.js +3 -0
- package/drivers/IDatabaseDriver.d.ts +50 -7
- package/entity/BaseEntity.d.ts +70 -3
- package/entity/BaseEntity.js +18 -0
- package/entity/Collection.d.ts +6 -3
- package/entity/Collection.js +15 -4
- package/entity/EntityAssigner.js +8 -0
- package/entity/EntityFactory.js +20 -1
- package/entity/EntityLoader.d.ts +8 -1
- package/entity/EntityLoader.js +41 -16
- package/entity/EntityRepository.d.ts +27 -9
- package/entity/EntityRepository.js +12 -0
- package/entity/Reference.d.ts +42 -1
- package/entity/Reference.js +9 -0
- package/entity/WrappedEntity.d.ts +2 -2
- package/entity/defineEntity.d.ts +92 -17
- package/entity/defineEntity.js +17 -6
- package/entity/utils.js +4 -5
- package/enums.d.ts +8 -1
- package/errors.d.ts +2 -0
- package/errors.js +4 -0
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/metadata/EntitySchema.js +3 -0
- package/metadata/MetadataDiscovery.d.ts +11 -0
- package/metadata/MetadataDiscovery.js +135 -10
- package/metadata/MetadataValidator.d.ts +26 -0
- package/metadata/MetadataValidator.js +236 -0
- package/metadata/Routine.d.ts +74 -0
- package/metadata/Routine.js +140 -0
- package/metadata/index.d.ts +1 -0
- package/metadata/index.js +1 -0
- package/metadata/types.d.ts +77 -4
- package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
- package/naming-strategy/NamingStrategy.d.ts +1 -1
- package/package.json +1 -1
- package/platforms/Platform.d.ts +7 -0
- package/platforms/Platform.js +15 -1
- package/serialization/EntitySerializer.d.ts +18 -6
- package/serialization/EntitySerializer.js +31 -4
- package/typings.d.ts +389 -18
- package/typings.js +21 -4
- package/unit-of-work/UnitOfWork.js +11 -3
- package/utils/AbstractMigrator.d.ts +26 -3
- package/utils/AbstractMigrator.js +264 -15
- package/utils/AbstractSchemaGenerator.d.ts +1 -1
- package/utils/AbstractSchemaGenerator.js +4 -1
- package/utils/Configuration.d.ts +40 -3
- package/utils/Configuration.js +46 -0
- package/utils/DataloaderUtils.d.ts +10 -1
- package/utils/DataloaderUtils.js +78 -0
- package/utils/QueryHelper.d.ts +16 -0
- package/utils/QueryHelper.js +15 -0
- package/utils/TransactionManager.js +2 -0
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +1 -1
- package/utils/fs-utils.d.ts +2 -0
- package/utils/fs-utils.js +7 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/partition-utils.d.ts +17 -0
- 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
|
-
|
|
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(
|
|
61
|
+
getExecuted(options?: {
|
|
62
|
+
schema?: string;
|
|
63
|
+
}): Promise<MigrationRow[]>;
|
|
61
64
|
/**
|
|
62
65
|
* @inheritDoc
|
|
63
66
|
*/
|
|
64
|
-
getPending(
|
|
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
|
-
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
297
|
-
|
|
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
|
-
|
|
300
|
-
|
|
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,
|
|
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
|
});
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -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
|
|
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.
|