@mikro-orm/core 7.0.0-dev.300 → 7.0.0-dev.301
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 +1 -1
- package/EntityManager.js +94 -43
- package/MikroORM.js +4 -4
- package/cache/FileCacheAdapter.js +1 -3
- package/connections/Connection.js +16 -3
- package/drivers/DatabaseDriver.js +25 -7
- package/entity/Collection.js +43 -17
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.js +32 -12
- package/entity/EntityHelper.js +12 -8
- package/entity/EntityLoader.js +55 -22
- package/entity/Reference.d.ts +1 -1
- package/entity/Reference.js +37 -8
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +5 -7
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.js +12 -17
- package/errors.js +18 -8
- package/events/EventManager.js +1 -1
- package/exceptions.js +7 -2
- package/hydration/ObjectHydrator.js +27 -13
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +3 -5
- package/logging/colors.js +3 -6
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.js +101 -46
- package/metadata/MetadataProvider.js +6 -1
- package/metadata/MetadataStorage.js +1 -3
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +5 -2
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.js +46 -23
- package/serialization/EntitySerializer.js +7 -3
- package/serialization/SerializationContext.js +1 -1
- package/typings.d.ts +23 -23
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.js +8 -6
- package/unit-of-work/ChangeSetPersister.js +13 -8
- package/unit-of-work/CommitOrderCalculator.js +4 -2
- package/unit-of-work/UnitOfWork.d.ts +7 -1
- package/unit-of-work/UnitOfWork.js +51 -22
- package/utils/AbstractMigrator.js +3 -5
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.js +8 -4
- package/utils/Cursor.js +4 -2
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.js +51 -43
- package/utils/QueryHelper.js +38 -26
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +2 -1
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +36 -30
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +6 -3
|
@@ -33,7 +33,9 @@ export class MetadataDiscovery {
|
|
|
33
33
|
async discover(preferTs = true) {
|
|
34
34
|
this.discovered.length = 0;
|
|
35
35
|
const startTime = Date.now();
|
|
36
|
-
const suffix = this.metadataProvider.constructor === MetadataProvider
|
|
36
|
+
const suffix = this.metadataProvider.constructor === MetadataProvider
|
|
37
|
+
? ''
|
|
38
|
+
: `, using ${colors.cyan(this.metadataProvider.constructor.name)}`;
|
|
37
39
|
this.logger.log('discovery', `ORM entity discovery started${suffix}`);
|
|
38
40
|
await this.findEntities(preferTs);
|
|
39
41
|
for (const meta of this.discovered) {
|
|
@@ -51,7 +53,9 @@ export class MetadataDiscovery {
|
|
|
51
53
|
discoverSync() {
|
|
52
54
|
this.discovered.length = 0;
|
|
53
55
|
const startTime = Date.now();
|
|
54
|
-
const suffix = this.metadataProvider.constructor === MetadataProvider
|
|
56
|
+
const suffix = this.metadataProvider.constructor === MetadataProvider
|
|
57
|
+
? ''
|
|
58
|
+
: `, using ${colors.cyan(this.metadataProvider.constructor.name)}`;
|
|
55
59
|
this.logger.log('discovery', `ORM entity discovery started${suffix} in sync mode`);
|
|
56
60
|
const refs = this.config.get('entities');
|
|
57
61
|
this.discoverReferences(refs);
|
|
@@ -127,7 +131,7 @@ export class MetadataDiscovery {
|
|
|
127
131
|
// ignore base entities (not annotated with @Entity)
|
|
128
132
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
129
133
|
// sort so we discover entities first to get around issues with nested embeddables
|
|
130
|
-
filtered.sort((a, b) => !a.embeddable === !b.embeddable ? 0 :
|
|
134
|
+
filtered.sort((a, b) => (!a.embeddable === !b.embeddable ? 0 : a.embeddable ? 1 : -1));
|
|
131
135
|
filtered.forEach(meta => this.initSingleTableInheritance(meta, filtered));
|
|
132
136
|
filtered.forEach(meta => this.initTPTRelationships(meta, filtered));
|
|
133
137
|
filtered.forEach(meta => this.defineBaseEntityProperties(meta));
|
|
@@ -176,7 +180,7 @@ export class MetadataDiscovery {
|
|
|
176
180
|
}
|
|
177
181
|
async findEntities(preferTs) {
|
|
178
182
|
const { entities, entitiesTs, baseDir } = this.config.getAll();
|
|
179
|
-
const targets =
|
|
183
|
+
const targets = preferTs && entitiesTs.length > 0 ? entitiesTs : entities;
|
|
180
184
|
const processed = [];
|
|
181
185
|
const paths = [];
|
|
182
186
|
for (const entity of targets) {
|
|
@@ -189,7 +193,7 @@ export class MetadataDiscovery {
|
|
|
189
193
|
}
|
|
190
194
|
if (paths.length > 0) {
|
|
191
195
|
const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
|
|
192
|
-
processed.push(...await discoverEntities(paths, { baseDir }));
|
|
196
|
+
processed.push(...(await discoverEntities(paths, { baseDir })));
|
|
193
197
|
}
|
|
194
198
|
return this.discoverReferences(processed);
|
|
195
199
|
}
|
|
@@ -210,10 +214,10 @@ export class MetadataDiscovery {
|
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
213
|
-
const target = typeof prop.entity === 'function' && !prop.entity.prototype
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
const target = typeof prop.entity === 'function' && !prop.entity.prototype ? prop.entity() : prop.type;
|
|
218
|
+
if (!unwrap(prop.type)
|
|
219
|
+
.split(/ ?\| ?/)
|
|
220
|
+
.every(type => this.discovered.find(m => m.className === type))) {
|
|
217
221
|
missing.push(...Utils.asArray(target));
|
|
218
222
|
}
|
|
219
223
|
}
|
|
@@ -305,7 +309,8 @@ export class MetadataDiscovery {
|
|
|
305
309
|
}
|
|
306
310
|
getRootEntity(meta) {
|
|
307
311
|
const base = meta.extends && this.metadata.find(meta.extends);
|
|
308
|
-
if (!base || base === meta) {
|
|
312
|
+
if (!base || base === meta) {
|
|
313
|
+
// make sure we do not fall into infinite loop
|
|
309
314
|
return meta;
|
|
310
315
|
}
|
|
311
316
|
const root = this.getRootEntity(base);
|
|
@@ -365,7 +370,10 @@ export class MetadataDiscovery {
|
|
|
365
370
|
initOwnColumns(meta) {
|
|
366
371
|
meta.sync();
|
|
367
372
|
for (const prop of meta.props) {
|
|
368
|
-
if (!prop.joinColumns ||
|
|
373
|
+
if (!prop.joinColumns ||
|
|
374
|
+
!prop.columnTypes ||
|
|
375
|
+
prop.ownColumns ||
|
|
376
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
369
377
|
continue;
|
|
370
378
|
}
|
|
371
379
|
// For polymorphic relations, ownColumns should include all fieldNames
|
|
@@ -553,7 +561,8 @@ export class MetadataDiscovery {
|
|
|
553
561
|
this.initRelation(prop);
|
|
554
562
|
}
|
|
555
563
|
this.initOwnColumns(meta);
|
|
556
|
-
meta.simplePK =
|
|
564
|
+
meta.simplePK =
|
|
565
|
+
pks.length === 1 && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType && pks[0].runtimeType !== 'Date';
|
|
557
566
|
meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
|
|
558
567
|
if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
|
|
559
568
|
meta.properties[meta.serializedPrimaryKey].persist ??= false;
|
|
@@ -685,7 +694,8 @@ export class MetadataDiscovery {
|
|
|
685
694
|
pivotMeta2.compositePK = true;
|
|
686
695
|
}
|
|
687
696
|
// handle self-referenced m:n with same default field names
|
|
688
|
-
if (meta.className === targetType &&
|
|
697
|
+
if (meta.className === targetType &&
|
|
698
|
+
prop.joinColumns.every((joinColumn, idx) => joinColumn === prop.inverseJoinColumns[idx])) {
|
|
689
699
|
// use tableName only when explicitly provided by user, otherwise use className for backwards compatibility
|
|
690
700
|
const baseName = this.isExplicitTableName(meta) ? meta.tableName : meta.className;
|
|
691
701
|
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(baseName + '_1', name, meta.compositePK));
|
|
@@ -775,7 +785,9 @@ export class MetadataDiscovery {
|
|
|
775
785
|
if (isCompositePK) {
|
|
776
786
|
// Create separate properties for each PK column (nullable for other entity types)
|
|
777
787
|
for (let i = 0; i < prop.joinColumns.length; i++) {
|
|
778
|
-
pivotMeta.properties[prop.joinColumns[i]] = this.createPivotScalarProperty(prop.joinColumns[i], [
|
|
788
|
+
pivotMeta.properties[prop.joinColumns[i]] = this.createPivotScalarProperty(prop.joinColumns[i], [
|
|
789
|
+
columnTypes[i],
|
|
790
|
+
]);
|
|
779
791
|
}
|
|
780
792
|
// Virtual property combining all columns (for compatibility)
|
|
781
793
|
pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, columnTypes, [...prop.joinColumns], { type: meta.className, persist: false });
|
|
@@ -915,7 +927,8 @@ export class MetadataDiscovery {
|
|
|
915
927
|
}
|
|
916
928
|
defineBaseEntityProperties(meta) {
|
|
917
929
|
const base = meta.extends && this.metadata.get(meta.extends);
|
|
918
|
-
if (!base || base === meta) {
|
|
930
|
+
if (!base || base === meta) {
|
|
931
|
+
// make sure we do not fall into infinite loop
|
|
919
932
|
return 0;
|
|
920
933
|
}
|
|
921
934
|
let order = this.defineBaseEntityProperties(base);
|
|
@@ -927,10 +940,12 @@ export class MetadataDiscovery {
|
|
|
927
940
|
meta.properties[prop.name] = prop;
|
|
928
941
|
}
|
|
929
942
|
});
|
|
930
|
-
ownProps.forEach(prop => meta.properties[prop.name] = prop);
|
|
943
|
+
ownProps.forEach(prop => (meta.properties[prop.name] = prop));
|
|
931
944
|
meta.filters = { ...base.filters, ...meta.filters };
|
|
932
945
|
if (!meta.discriminatorValue) {
|
|
933
|
-
Object.values(base.properties)
|
|
946
|
+
Object.values(base.properties)
|
|
947
|
+
.filter(prop => !old.includes(prop.name))
|
|
948
|
+
.forEach(prop => {
|
|
934
949
|
meta.properties[prop.name] = { ...prop };
|
|
935
950
|
meta.propertyOrder.set(prop.name, (order += 0.01));
|
|
936
951
|
});
|
|
@@ -938,7 +953,9 @@ export class MetadataDiscovery {
|
|
|
938
953
|
meta.indexes = Utils.unique([...base.indexes, ...meta.indexes]);
|
|
939
954
|
meta.uniques = Utils.unique([...base.uniques, ...meta.uniques]);
|
|
940
955
|
meta.checks = Utils.unique([...base.checks, ...meta.checks]);
|
|
941
|
-
const pks = Object.values(meta.properties)
|
|
956
|
+
const pks = Object.values(meta.properties)
|
|
957
|
+
.filter(p => p.primary)
|
|
958
|
+
.map(p => p.name);
|
|
942
959
|
if (pks.length > 0 && meta.primaryKeys.length === 0) {
|
|
943
960
|
meta.primaryKeys = pks;
|
|
944
961
|
}
|
|
@@ -971,7 +988,7 @@ export class MetadataDiscovery {
|
|
|
971
988
|
properties[prop.name].runtimeType = 'any';
|
|
972
989
|
return properties[prop.name];
|
|
973
990
|
}
|
|
974
|
-
return properties[prop.name] = prop;
|
|
991
|
+
return (properties[prop.name] = prop);
|
|
975
992
|
});
|
|
976
993
|
};
|
|
977
994
|
const processExtensions = (meta) => {
|
|
@@ -989,7 +1006,10 @@ export class MetadataDiscovery {
|
|
|
989
1006
|
inlineProperties(meta);
|
|
990
1007
|
processExtensions(meta);
|
|
991
1008
|
});
|
|
992
|
-
const name = polymorphs
|
|
1009
|
+
const name = polymorphs
|
|
1010
|
+
.map(t => t.className)
|
|
1011
|
+
.sort()
|
|
1012
|
+
.join(' | ');
|
|
993
1013
|
embeddable = new EntityMetadata({
|
|
994
1014
|
name,
|
|
995
1015
|
className: name,
|
|
@@ -1001,7 +1021,7 @@ export class MetadataDiscovery {
|
|
|
1001
1021
|
});
|
|
1002
1022
|
embeddable.sync();
|
|
1003
1023
|
discovered.push(embeddable);
|
|
1004
|
-
polymorphs.forEach(meta => meta.root = embeddable);
|
|
1024
|
+
polymorphs.forEach(meta => (meta.root = embeddable));
|
|
1005
1025
|
}
|
|
1006
1026
|
}
|
|
1007
1027
|
initPolymorphicRelation(meta, prop, discovered) {
|
|
@@ -1171,7 +1191,7 @@ export class MetadataDiscovery {
|
|
|
1171
1191
|
const map = meta.root.discriminatorMap;
|
|
1172
1192
|
Object.keys(map)
|
|
1173
1193
|
.filter(key => typeof map[key] === 'string')
|
|
1174
|
-
.forEach(key => map[key] = this.metadata.getByClassName(map[key]).class);
|
|
1194
|
+
.forEach(key => (map[key] = this.metadata.getByClassName(map[key]).class));
|
|
1175
1195
|
}
|
|
1176
1196
|
else {
|
|
1177
1197
|
meta.root.discriminatorMap = {};
|
|
@@ -1195,7 +1215,9 @@ export class MetadataDiscovery {
|
|
|
1195
1215
|
Object.values(meta.properties).forEach(prop => {
|
|
1196
1216
|
const newProp = { ...prop };
|
|
1197
1217
|
const rootProp = meta.root.properties[prop.name];
|
|
1198
|
-
if (rootProp &&
|
|
1218
|
+
if (rootProp &&
|
|
1219
|
+
(rootProp.type !== prop.type ||
|
|
1220
|
+
(rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
1199
1221
|
const name = newProp.name;
|
|
1200
1222
|
this.initFieldName(newProp, newProp.object);
|
|
1201
1223
|
newProp.renamedFrom = name;
|
|
@@ -1210,7 +1232,8 @@ export class MetadataDiscovery {
|
|
|
1210
1232
|
// Find which discriminator owns the original fieldNames
|
|
1211
1233
|
for (const [discValue, childClass] of Object.entries(meta.root.discriminatorMap)) {
|
|
1212
1234
|
const childMeta = this.metadata.find(childClass);
|
|
1213
|
-
if (childMeta?.properties[prop.name]?.fieldNames &&
|
|
1235
|
+
if (childMeta?.properties[prop.name]?.fieldNames &&
|
|
1236
|
+
compareArrays(childMeta.properties[prop.name].fieldNames, rootProp.fieldNames)) {
|
|
1214
1237
|
rootProp.stiFieldNameMap[discValue] = rootProp.fieldNames[0];
|
|
1215
1238
|
break;
|
|
1216
1239
|
}
|
|
@@ -1442,7 +1465,10 @@ export class MetadataDiscovery {
|
|
|
1442
1465
|
}
|
|
1443
1466
|
if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
|
|
1444
1467
|
for (const prop of meta.props) {
|
|
1445
|
-
if (prop.enum &&
|
|
1468
|
+
if (prop.enum &&
|
|
1469
|
+
prop.persist !== false &&
|
|
1470
|
+
!prop.nativeEnumName &&
|
|
1471
|
+
prop.items?.every(item => typeof item === 'string')) {
|
|
1446
1472
|
this.initFieldName(prop);
|
|
1447
1473
|
meta.checks.push({
|
|
1448
1474
|
name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
|
|
@@ -1495,7 +1521,11 @@ export class MetadataDiscovery {
|
|
|
1495
1521
|
const entity1 = new meta.class();
|
|
1496
1522
|
const entity2 = new meta.class();
|
|
1497
1523
|
// we compare the two values by reference, this will discard things like `new Date()` or `Date.now()`
|
|
1498
|
-
if (this.config.get('discovery').inferDefaultValues &&
|
|
1524
|
+
if (this.config.get('discovery').inferDefaultValues &&
|
|
1525
|
+
prop.default === undefined &&
|
|
1526
|
+
entity1[prop.name] != null &&
|
|
1527
|
+
entity1[prop.name] === entity2[prop.name] &&
|
|
1528
|
+
entity1[prop.name] !== now) {
|
|
1499
1529
|
prop.default ??= entity1[prop.name];
|
|
1500
1530
|
}
|
|
1501
1531
|
// if the default value is null, infer nullability
|
|
@@ -1562,7 +1592,9 @@ export class MetadataDiscovery {
|
|
|
1562
1592
|
prop.type = prop.customType.constructor.name;
|
|
1563
1593
|
}
|
|
1564
1594
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1565
|
-
if (typeof prop.type === 'function' &&
|
|
1595
|
+
if (typeof prop.type === 'function' &&
|
|
1596
|
+
Type.isMappedType(prop.type.prototype) &&
|
|
1597
|
+
!prop.customType) {
|
|
1566
1598
|
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1567
1599
|
// we use just the type name, to have more performant hydration code
|
|
1568
1600
|
const type = Utils.keys(t).find(type => {
|
|
@@ -1582,7 +1614,10 @@ export class MetadataDiscovery {
|
|
|
1582
1614
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1583
1615
|
prop.customType = new t.json();
|
|
1584
1616
|
}
|
|
1585
|
-
if (prop.kind === ReferenceKind.SCALAR &&
|
|
1617
|
+
if (prop.kind === ReferenceKind.SCALAR &&
|
|
1618
|
+
!prop.customType &&
|
|
1619
|
+
prop.columnTypes &&
|
|
1620
|
+
['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1586
1621
|
prop.customType = new t.json();
|
|
1587
1622
|
}
|
|
1588
1623
|
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
@@ -1613,10 +1648,12 @@ export class MetadataDiscovery {
|
|
|
1613
1648
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1614
1649
|
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1615
1650
|
.filter(type => mappedType instanceof type)
|
|
1616
|
-
.forEach((type) => prop.customType = new type());
|
|
1651
|
+
.forEach((type) => (prop.customType = new type()));
|
|
1617
1652
|
}
|
|
1618
1653
|
if (prop.customType && !prop.columnTypes) {
|
|
1619
|
-
const mappedType = this.getMappedType({
|
|
1654
|
+
const mappedType = this.getMappedType({
|
|
1655
|
+
columnTypes: [prop.customType.getColumnType(prop, this.platform)],
|
|
1656
|
+
});
|
|
1620
1657
|
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1621
1658
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1622
1659
|
}
|
|
@@ -1635,23 +1672,33 @@ export class MetadataDiscovery {
|
|
|
1635
1672
|
prop.customType.meta = meta;
|
|
1636
1673
|
prop.customType.prop = prop;
|
|
1637
1674
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1638
|
-
prop.hasConvertToJSValueSQL =
|
|
1639
|
-
|
|
1640
|
-
|
|
1675
|
+
prop.hasConvertToJSValueSQL =
|
|
1676
|
+
!!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1677
|
+
prop.hasConvertToDatabaseValueSQL =
|
|
1678
|
+
!!prop.customType.convertToDatabaseValueSQL &&
|
|
1679
|
+
prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1680
|
+
if (prop.customType instanceof t.bigint &&
|
|
1681
|
+
['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1641
1682
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1642
1683
|
}
|
|
1643
1684
|
}
|
|
1644
1685
|
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1645
1686
|
prop.type = prop.customType.name;
|
|
1646
1687
|
}
|
|
1647
|
-
if (!prop.customType &&
|
|
1688
|
+
if (!prop.customType &&
|
|
1689
|
+
[ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
1690
|
+
!prop.polymorphic &&
|
|
1691
|
+
prop.targetMeta.compositePK) {
|
|
1648
1692
|
prop.customTypes = [];
|
|
1649
1693
|
for (const pk of prop.targetMeta.getPrimaryProps()) {
|
|
1650
1694
|
if (pk.customType) {
|
|
1651
1695
|
prop.customTypes.push(pk.customType);
|
|
1652
|
-
prop.hasConvertToJSValueSQL ||=
|
|
1696
|
+
prop.hasConvertToJSValueSQL ||=
|
|
1697
|
+
!!pk.customType.convertToJSValueSQL && pk.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1653
1698
|
/* v8 ignore next */
|
|
1654
|
-
prop.hasConvertToDatabaseValueSQL ||=
|
|
1699
|
+
prop.hasConvertToDatabaseValueSQL ||=
|
|
1700
|
+
!!pk.customType.convertToDatabaseValueSQL &&
|
|
1701
|
+
pk.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1655
1702
|
}
|
|
1656
1703
|
else {
|
|
1657
1704
|
prop.customTypes.push(undefined);
|
|
@@ -1659,7 +1706,11 @@ export class MetadataDiscovery {
|
|
|
1659
1706
|
}
|
|
1660
1707
|
}
|
|
1661
1708
|
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1662
|
-
if (!prop.columnTypes &&
|
|
1709
|
+
if (!prop.columnTypes &&
|
|
1710
|
+
prop.nativeEnumName &&
|
|
1711
|
+
meta.schema !== this.platform.getDefaultSchemaName() &&
|
|
1712
|
+
meta.schema &&
|
|
1713
|
+
!prop.nativeEnumName.includes('.')) {
|
|
1663
1714
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1664
1715
|
}
|
|
1665
1716
|
else {
|
|
@@ -1686,7 +1737,10 @@ export class MetadataDiscovery {
|
|
|
1686
1737
|
}
|
|
1687
1738
|
// Auto-generate formula for persist: false relations, but only for single-column FKs
|
|
1688
1739
|
// Composite FK relations need standard JOIN conditions, not formula-based
|
|
1689
|
-
if (!prop.formula &&
|
|
1740
|
+
if (!prop.formula &&
|
|
1741
|
+
prop.persist === false &&
|
|
1742
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
1743
|
+
!prop.embedded) {
|
|
1690
1744
|
this.initFieldName(prop);
|
|
1691
1745
|
if (prop.fieldNames?.length === 1) {
|
|
1692
1746
|
prop.formula = table => `${table}.${this.platform.quoteIdentifier(prop.fieldNames[0])}`;
|
|
@@ -1697,7 +1751,9 @@ export class MetadataDiscovery {
|
|
|
1697
1751
|
this.initUnsigned(prop);
|
|
1698
1752
|
// Get the target properties for FK relations - use targetKey property if specified, otherwise PKs
|
|
1699
1753
|
const targetProps = prop.targetMeta
|
|
1700
|
-
?
|
|
1754
|
+
? prop.targetKey
|
|
1755
|
+
? [prop.targetMeta.properties[prop.targetKey]]
|
|
1756
|
+
: prop.targetMeta.getPrimaryProps()
|
|
1701
1757
|
: [];
|
|
1702
1758
|
targetProps.map(targetProp => {
|
|
1703
1759
|
prop.length ??= targetProp.length;
|
|
@@ -1715,11 +1771,11 @@ export class MetadataDiscovery {
|
|
|
1715
1771
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1716
1772
|
const mappedType = this.getMappedType(prop);
|
|
1717
1773
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1718
|
-
if (mappedType instanceof t.unknown
|
|
1774
|
+
if (mappedType instanceof t.unknown &&
|
|
1719
1775
|
// it could be a runtime type from reflect-metadata
|
|
1720
|
-
|
|
1776
|
+
!SCALAR_TYPES.includes(prop.type) &&
|
|
1721
1777
|
// or it might be inferred via ts-morph to some generic type alias
|
|
1722
|
-
|
|
1778
|
+
!prop.type.match(/[<>:"';{}]/)) {
|
|
1723
1779
|
const type = prop.length != null && !prop.type.endsWith(`(${prop.length})`) ? `${prop.type}(${prop.length})` : prop.type;
|
|
1724
1780
|
prop.columnTypes = [type];
|
|
1725
1781
|
}
|
|
@@ -1736,9 +1792,7 @@ export class MetadataDiscovery {
|
|
|
1736
1792
|
const targetMeta = prop.targetMeta;
|
|
1737
1793
|
prop.columnTypes = [];
|
|
1738
1794
|
// Use targetKey property if specified, otherwise use primary key properties
|
|
1739
|
-
const referencedProps = prop.targetKey
|
|
1740
|
-
? [targetMeta.properties[prop.targetKey]]
|
|
1741
|
-
: targetMeta.getPrimaryProps();
|
|
1795
|
+
const referencedProps = prop.targetKey ? [targetMeta.properties[prop.targetKey]] : targetMeta.getPrimaryProps();
|
|
1742
1796
|
if (prop.polymorphic && prop.polymorphTargets) {
|
|
1743
1797
|
prop.columnTypes.push(this.platform.getVarcharTypeDeclarationSQL(prop));
|
|
1744
1798
|
}
|
|
@@ -1796,7 +1850,8 @@ export class MetadataDiscovery {
|
|
|
1796
1850
|
});
|
|
1797
1851
|
return;
|
|
1798
1852
|
}
|
|
1799
|
-
prop.unsigned ??=
|
|
1853
|
+
prop.unsigned ??=
|
|
1854
|
+
(prop.primary || prop.unsigned) && this.platform.isNumericProperty(prop) && this.platform.supportsUnsigned();
|
|
1800
1855
|
}
|
|
1801
1856
|
initIndexes(meta, prop) {
|
|
1802
1857
|
const hasIndex = meta.indexes.some(idx => idx.properties?.length === 1 && idx.properties[0] === prop.name);
|
|
@@ -13,7 +13,12 @@ export class MetadataProvider {
|
|
|
13
13
|
}
|
|
14
14
|
else if (prop.entity) {
|
|
15
15
|
const tmp = prop.entity();
|
|
16
|
-
prop.type = Array.isArray(tmp)
|
|
16
|
+
prop.type = Array.isArray(tmp)
|
|
17
|
+
? tmp
|
|
18
|
+
.map(t => Utils.className(t))
|
|
19
|
+
.sort()
|
|
20
|
+
.join(' | ')
|
|
21
|
+
: Utils.className(tmp);
|
|
17
22
|
prop.target = tmp instanceof EntitySchema ? tmp.meta.class : tmp;
|
|
18
23
|
}
|
|
19
24
|
else if (!prop.type && !((prop.enum || prop.array) && (prop.items?.length ?? 0) > 0)) {
|
|
@@ -90,9 +90,7 @@ export class MetadataStorage {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
decorate(em) {
|
|
93
|
-
[...this.metadata.values()]
|
|
94
|
-
.filter(meta => meta.prototype)
|
|
95
|
-
.forEach(meta => EntityHelper.decorate(meta, em));
|
|
93
|
+
[...this.metadata.values()].filter(meta => meta.prototype).forEach(meta => EntityHelper.decorate(meta, em));
|
|
96
94
|
}
|
|
97
95
|
*[Symbol.iterator]() {
|
|
98
96
|
for (const meta of this.metadata.values()) {
|
|
@@ -63,7 +63,11 @@ export class MetadataValidator {
|
|
|
63
63
|
}
|
|
64
64
|
// Validate no mixing of STI and TPT in the same hierarchy
|
|
65
65
|
this.validateInheritanceStrategies(discovered);
|
|
66
|
-
const tableNames = discovered.filter(meta => !meta.abstract &&
|
|
66
|
+
const tableNames = discovered.filter(meta => !meta.abstract &&
|
|
67
|
+
!meta.embeddable &&
|
|
68
|
+
meta === meta.root &&
|
|
69
|
+
(meta.tableName || meta.collection) &&
|
|
70
|
+
meta.schema !== '*');
|
|
67
71
|
const duplicateTableNames = Utils.findDuplicates(tableNames.map(meta => {
|
|
68
72
|
const tableName = meta.tableName || meta.collection;
|
|
69
73
|
return (meta.schema ? '.' + meta.schema : '') + tableName;
|
|
@@ -88,7 +92,10 @@ export class MetadataValidator {
|
|
|
88
92
|
const pivotProps = new Map();
|
|
89
93
|
// check for not discovered entities
|
|
90
94
|
discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
91
|
-
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
95
|
+
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
96
|
+
!unwrap(prop.type)
|
|
97
|
+
.split(/ ?\| ?/)
|
|
98
|
+
.every(type => discovered.find(m => m.className === type))) {
|
|
92
99
|
throw MetadataError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
|
|
93
100
|
}
|
|
94
101
|
if (prop.pivotEntity) {
|
|
@@ -122,7 +129,10 @@ export class MetadataValidator {
|
|
|
122
129
|
if (targetMeta.abstract && !targetMeta.root?.inheritanceType && !targetMeta.embeddable) {
|
|
123
130
|
throw MetadataError.targetIsAbstract(meta, prop);
|
|
124
131
|
}
|
|
125
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
132
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
133
|
+
prop.persist === false &&
|
|
134
|
+
targetMeta.compositePK &&
|
|
135
|
+
options.checkNonPersistentCompositeProps) {
|
|
126
136
|
throw MetadataError.nonPersistentCompositeProp(meta, prop);
|
|
127
137
|
}
|
|
128
138
|
this.validateTargetKey(meta, prop, targetMeta);
|
|
@@ -248,7 +258,9 @@ export class MetadataValidator {
|
|
|
248
258
|
// has correct `mappedBy` reference type
|
|
249
259
|
// For polymorphic relations, check if this entity is one of the polymorphic targets
|
|
250
260
|
const isValidPolymorphicInverse = owner.polymorphic && owner.polymorphTargets?.some(target => target.class === meta.root.class);
|
|
251
|
-
if (!isValidPolymorphicInverse &&
|
|
261
|
+
if (!isValidPolymorphicInverse &&
|
|
262
|
+
owner.type !== meta.className &&
|
|
263
|
+
owner.targetMeta?.root.class !== meta.root.class) {
|
|
252
264
|
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
|
|
253
265
|
}
|
|
254
266
|
// owning side is not defined as inverse
|
|
@@ -280,7 +292,10 @@ export class MetadataValidator {
|
|
|
280
292
|
}
|
|
281
293
|
validateDuplicateFieldNames(meta, options) {
|
|
282
294
|
const candidates = Object.values(meta.properties)
|
|
283
|
-
.filter(prop => prop.persist !== false &&
|
|
295
|
+
.filter(prop => prop.persist !== false &&
|
|
296
|
+
!prop.inherited &&
|
|
297
|
+
prop.fieldNames?.length === 1 &&
|
|
298
|
+
(prop.kind !== ReferenceKind.EMBEDDED || prop.object))
|
|
284
299
|
.map(prop => prop.fieldNames[0]);
|
|
285
300
|
const duplicates = Utils.findDuplicates(candidates);
|
|
286
301
|
if (duplicates.length > 0 && options.checkDuplicateFieldNames) {
|
package/metadata/types.d.ts
CHANGED
|
@@ -526,7 +526,7 @@ export interface IndexColumnOptions {
|
|
|
526
526
|
}
|
|
527
527
|
interface BaseOptions<T, H extends string> {
|
|
528
528
|
name?: string;
|
|
529
|
-
properties?:
|
|
529
|
+
properties?: T extends EntityClass<infer P> ? Properties<P, H> : Properties<T, H>;
|
|
530
530
|
options?: Dictionary;
|
|
531
531
|
expression?: string | (T extends EntityClass<infer P> ? IndexCallback<P> : IndexCallback<T>);
|
|
532
532
|
/**
|
|
@@ -540,7 +540,7 @@ interface BaseOptions<T, H extends string> {
|
|
|
540
540
|
* Columns to include in the index but not as part of the key (PostgreSQL, MSSQL).
|
|
541
541
|
* These columns are stored in the leaf level of the index but not used for searching.
|
|
542
542
|
*/
|
|
543
|
-
include?:
|
|
543
|
+
include?: T extends EntityClass<infer P> ? Properties<P, H> : Properties<T, H>;
|
|
544
544
|
/** Fill factor for the index as a percentage 0-100 (PostgreSQL, MSSQL). */
|
|
545
545
|
fillFactor?: number;
|
|
546
546
|
}
|
|
@@ -35,10 +35,13 @@ export class AbstractNamingStrategy {
|
|
|
35
35
|
*/
|
|
36
36
|
getEntityName(tableName, schemaName) {
|
|
37
37
|
const name = tableName.match(/^[^$_\p{ID_Start}]/u) ? `E_${tableName}` : tableName;
|
|
38
|
-
return this.getClassName(name.replaceAll(/[^\u200C\u200D\p{ID_Continue}]+/
|
|
38
|
+
return this.getClassName(name.replaceAll(/[^\u200C\u200D\p{ID_Continue}]+/gu, r => r
|
|
39
|
+
.split('')
|
|
40
|
+
.map(c => `$${c.codePointAt(0)}`)
|
|
41
|
+
.join('')), '_');
|
|
39
42
|
}
|
|
40
43
|
columnNameToProperty(columnName) {
|
|
41
|
-
const propName = columnName.replace(/[_\- ]+(\w)/
|
|
44
|
+
const propName = columnName.replace(/[_\- ]+(\w)/gu, (_, p1) => p1.toUpperCase());
|
|
42
45
|
if (populatePathMembers.includes(propName.replace(/^\${2,}/u, '$$').replace(/^\$\*$/u, '*'))) {
|
|
43
46
|
return `$${propName}`;
|
|
44
47
|
}
|
package/not-supported.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export function discoverEntities() {
|
|
2
2
|
throw new Error('Folder-based discovery is not supported in this environment.');
|
|
3
3
|
}
|
|
4
|
-
export const fs = new Proxy({}, {
|
|
4
|
+
export const fs = new Proxy({}, {
|
|
5
|
+
get: () => {
|
|
6
|
+
throw new Error('File system is not supported in this environment.');
|
|
7
|
+
},
|
|
8
|
+
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"
|
|
4
|
-
"version": "7.0.0-dev.300",
|
|
3
|
+
"version": "7.0.0-dev.301",
|
|
5
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"data-mapper",
|
|
7
|
+
"ddd",
|
|
8
|
+
"entity",
|
|
9
|
+
"identity-map",
|
|
10
|
+
"javascript",
|
|
11
|
+
"js",
|
|
12
|
+
"mariadb",
|
|
13
|
+
"mikro-orm",
|
|
14
|
+
"mongo",
|
|
15
|
+
"mongodb",
|
|
16
|
+
"mysql",
|
|
17
|
+
"orm",
|
|
18
|
+
"postgresql",
|
|
19
|
+
"sqlite",
|
|
20
|
+
"sqlite3",
|
|
21
|
+
"ts",
|
|
22
|
+
"typescript",
|
|
23
|
+
"unit-of-work"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://mikro-orm.io",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/mikro-orm/mikro-orm/issues"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"author": "Martin Adámek",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+ssh://git@github.com/mikro-orm/mikro-orm.git"
|
|
34
|
+
},
|
|
35
|
+
"funding": "https://github.com/sponsors/b4nan",
|
|
36
|
+
"type": "module",
|
|
6
37
|
"exports": {
|
|
7
38
|
"./package.json": "./package.json",
|
|
8
39
|
".": "./index.js",
|
|
@@ -18,39 +49,8 @@
|
|
|
18
49
|
"./schema": "./utils/AbstractSchemaGenerator.js",
|
|
19
50
|
"./dataloader": "./utils/DataloaderUtils.js"
|
|
20
51
|
},
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"url": "git+ssh://git@github.com/mikro-orm/mikro-orm.git"
|
|
24
|
-
},
|
|
25
|
-
"funding": "https://github.com/sponsors/b4nan",
|
|
26
|
-
"keywords": [
|
|
27
|
-
"orm",
|
|
28
|
-
"mongo",
|
|
29
|
-
"mongodb",
|
|
30
|
-
"mysql",
|
|
31
|
-
"mariadb",
|
|
32
|
-
"postgresql",
|
|
33
|
-
"sqlite",
|
|
34
|
-
"sqlite3",
|
|
35
|
-
"ts",
|
|
36
|
-
"typescript",
|
|
37
|
-
"js",
|
|
38
|
-
"javascript",
|
|
39
|
-
"entity",
|
|
40
|
-
"ddd",
|
|
41
|
-
"mikro-orm",
|
|
42
|
-
"unit-of-work",
|
|
43
|
-
"data-mapper",
|
|
44
|
-
"identity-map"
|
|
45
|
-
],
|
|
46
|
-
"author": "Martin Adámek",
|
|
47
|
-
"license": "MIT",
|
|
48
|
-
"bugs": {
|
|
49
|
-
"url": "https://github.com/mikro-orm/mikro-orm/issues"
|
|
50
|
-
},
|
|
51
|
-
"homepage": "https://mikro-orm.io",
|
|
52
|
-
"engines": {
|
|
53
|
-
"node": ">= 22.17.0"
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "yarn compile && yarn copy",
|
|
@@ -58,9 +58,6 @@
|
|
|
58
58
|
"compile": "yarn run -T tsc -p tsconfig.build.json",
|
|
59
59
|
"copy": "node ../../scripts/copy.mjs"
|
|
60
60
|
},
|
|
61
|
-
"publishConfig": {
|
|
62
|
-
"access": "public"
|
|
63
|
-
},
|
|
64
61
|
"peerDependencies": {
|
|
65
62
|
"dataloader": "2.2.3"
|
|
66
63
|
},
|
|
@@ -68,5 +65,8 @@
|
|
|
68
65
|
"dataloader": {
|
|
69
66
|
"optional": true
|
|
70
67
|
}
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">= 22.17.0"
|
|
71
71
|
}
|
|
72
72
|
}
|