@mikro-orm/core 7.0.0-dev.225 → 7.0.0-dev.227
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.js +8 -2
- package/cache/FileCacheAdapter.js +9 -1
- package/drivers/DatabaseDriver.js +38 -0
- package/entity/EntityHelper.js +13 -1
- package/entity/EntityLoader.d.ts +1 -0
- package/entity/EntityLoader.js +123 -25
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/defineEntity.d.ts +8 -4
- package/entity/defineEntity.js +8 -0
- package/entity/index.d.ts +1 -0
- package/entity/index.js +1 -0
- package/errors.d.ts +3 -2
- package/errors.js +9 -4
- package/hydration/ObjectHydrator.js +40 -8
- package/metadata/EntitySchema.d.ts +1 -1
- package/metadata/MetadataDiscovery.d.ts +22 -0
- package/metadata/MetadataDiscovery.js +222 -11
- package/metadata/MetadataValidator.d.ts +6 -0
- package/metadata/MetadataValidator.js +76 -3
- package/metadata/types.d.ts +21 -5
- package/naming-strategy/AbstractNamingStrategy.d.ts +4 -0
- package/naming-strategy/AbstractNamingStrategy.js +6 -0
- package/naming-strategy/NamingStrategy.d.ts +4 -0
- package/package.json +1 -1
- package/typings.d.ts +20 -6
- package/typings.js +5 -1
- package/unit-of-work/ChangeSetComputer.js +14 -4
- package/unit-of-work/ChangeSetPersister.js +7 -0
- package/unit-of-work/UnitOfWork.js +13 -2
- package/utils/EntityComparator.d.ts +5 -0
- package/utils/EntityComparator.js +66 -12
- package/utils/QueryHelper.d.ts +4 -0
- package/utils/QueryHelper.js +10 -1
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +1 -1
package/typings.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReferenceKind, } from './enums.js';
|
|
2
2
|
import { Reference } from './entity/Reference.js';
|
|
3
3
|
import { EntityHelper } from './entity/EntityHelper.js';
|
|
4
|
+
import { helper } from './entity/wrap.js';
|
|
4
5
|
import { Utils } from './utils/Utils.js';
|
|
5
6
|
import { EntityComparator } from './utils/EntityComparator.js';
|
|
6
7
|
import { BaseEntity } from './entity/BaseEntity.js';
|
|
@@ -167,7 +168,10 @@ export class EntityMetadata {
|
|
|
167
168
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
168
169
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
169
170
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
170
|
-
|
|
171
|
+
const targetMeta = prop.targetMeta ?? helper(entity)?.__meta;
|
|
172
|
+
if (targetMeta) {
|
|
173
|
+
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, targetMeta, true);
|
|
174
|
+
}
|
|
171
175
|
}
|
|
172
176
|
EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
|
|
173
177
|
},
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Utils } from '../utils/Utils.js';
|
|
2
|
+
import { QueryHelper } from '../utils/QueryHelper.js';
|
|
2
3
|
import { ChangeSet, ChangeSetType } from './ChangeSet.js';
|
|
3
4
|
import { helper } from '../entity/wrap.js';
|
|
4
5
|
import { validateEntity } from '../entity/validators.js';
|
|
6
|
+
import { Reference } from '../entity/Reference.js';
|
|
7
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
5
8
|
import { ReferenceKind } from '../enums.js';
|
|
6
9
|
export class ChangeSetComputer {
|
|
7
10
|
collectionUpdates;
|
|
@@ -130,9 +133,10 @@ export class ChangeSetComputer {
|
|
|
130
133
|
return;
|
|
131
134
|
}
|
|
132
135
|
const targets = Utils.unwrapProperty(changeSet.entity, changeSet.meta, prop);
|
|
133
|
-
targets.forEach(([
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
targets.forEach(([rawTarget, idx]) => {
|
|
137
|
+
const target = Reference.unwrapReference(rawTarget);
|
|
138
|
+
const needsProcessing = target != null && (prop.targetKey != null || !target.__helper.hasPrimaryKey());
|
|
139
|
+
if (needsProcessing) {
|
|
136
140
|
let value = prop.targetKey ? target[prop.targetKey] : target.__helper.__identifier;
|
|
137
141
|
/* v8 ignore next */
|
|
138
142
|
if (prop.targetKey && prop.targetMeta) {
|
|
@@ -141,7 +145,13 @@ export class ChangeSetComputer {
|
|
|
141
145
|
value = targetProp.customType.convertToDatabaseValue(value, this.platform, { mode: 'serialization' });
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
|
-
|
|
148
|
+
if (prop.polymorphic) {
|
|
149
|
+
const discriminator = QueryHelper.findDiscriminatorValue(prop.discriminatorMap, target.constructor);
|
|
150
|
+
Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, new PolymorphicRef(discriminator, value), idx);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, value, idx);
|
|
154
|
+
}
|
|
145
155
|
}
|
|
146
156
|
});
|
|
147
157
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
2
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
2
3
|
import { helper } from '../entity/wrap.js';
|
|
3
4
|
import { ChangeSetType } from './ChangeSet.js';
|
|
4
5
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
@@ -376,6 +377,12 @@ export class ChangeSetPersister {
|
|
|
376
377
|
changeSet.payload[prop.name] = value.getValue();
|
|
377
378
|
return;
|
|
378
379
|
}
|
|
380
|
+
if (value instanceof PolymorphicRef) {
|
|
381
|
+
if (value.id instanceof EntityIdentifier) {
|
|
382
|
+
value.id = value.id.getValue();
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
379
386
|
if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
|
|
380
387
|
changeSet.payload[prop.name] = value.map(item => item.getValue());
|
|
381
388
|
return;
|
|
@@ -77,7 +77,10 @@ export class UnitOfWork {
|
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
79
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
80
|
-
|
|
80
|
+
// Skip polymorphic relations - they use PolymorphicRef wrapper
|
|
81
|
+
if (!prop.polymorphic) {
|
|
82
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
83
|
+
}
|
|
81
84
|
}
|
|
82
85
|
else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
|
|
83
86
|
for (const p of prop.targetMeta.props) {
|
|
@@ -262,6 +265,7 @@ export class UnitOfWork {
|
|
|
262
265
|
if (!cs || this.checkUniqueProps(cs)) {
|
|
263
266
|
return;
|
|
264
267
|
}
|
|
268
|
+
/* v8 ignore next */
|
|
265
269
|
if (type) {
|
|
266
270
|
cs.type = type;
|
|
267
271
|
}
|
|
@@ -1013,7 +1017,14 @@ export class UnitOfWork {
|
|
|
1013
1017
|
set.forEach(meta => calc.addNode(meta._id));
|
|
1014
1018
|
for (const meta of set) {
|
|
1015
1019
|
for (const prop of meta.relations) {
|
|
1016
|
-
|
|
1020
|
+
if (prop.polymorphTargets) {
|
|
1021
|
+
for (const targetMeta of prop.polymorphTargets) {
|
|
1022
|
+
calc.discoverProperty({ ...prop, targetMeta }, meta._id);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
calc.discoverProperty(prop, meta._id);
|
|
1027
|
+
}
|
|
1017
1028
|
}
|
|
1018
1029
|
}
|
|
1019
1030
|
return calc.sort().map(id => this.metadata.getById(id));
|
|
@@ -85,6 +85,11 @@ export declare class EntityComparator {
|
|
|
85
85
|
private getPropertyComparator;
|
|
86
86
|
private wrap;
|
|
87
87
|
private safeKey;
|
|
88
|
+
/**
|
|
89
|
+
* Sets the toArray helper in the context if not already set.
|
|
90
|
+
* Used for converting composite PKs to arrays.
|
|
91
|
+
*/
|
|
92
|
+
private setToArrayHelper;
|
|
88
93
|
/**
|
|
89
94
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
90
95
|
*/
|
|
@@ -4,6 +4,7 @@ import { compareArrays, compareBooleans, compareBuffers, compareObjects, equals,
|
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { Raw } from './RawQueryFragment.js';
|
|
6
6
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
7
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
7
8
|
export class EntityComparator {
|
|
8
9
|
metadata;
|
|
9
10
|
platform;
|
|
@@ -285,6 +286,7 @@ export class EntityComparator {
|
|
|
285
286
|
}
|
|
286
287
|
const lines = [];
|
|
287
288
|
const context = new Map();
|
|
289
|
+
context.set('PolymorphicRef', PolymorphicRef);
|
|
288
290
|
const tz = this.platform.getTimezone();
|
|
289
291
|
const parseDate = (key, value, padding = '') => {
|
|
290
292
|
lines.push(`${padding} if (${value} == null || ${value} instanceof Date) {`);
|
|
@@ -311,12 +313,28 @@ export class EntityComparator {
|
|
|
311
313
|
if (!prop.fieldNames) {
|
|
312
314
|
continue;
|
|
313
315
|
}
|
|
316
|
+
if (prop.polymorphic && prop.fieldNames.length >= 2) {
|
|
317
|
+
const discriminatorField = prop.fieldNames[0];
|
|
318
|
+
const idFields = prop.fieldNames.slice(1);
|
|
319
|
+
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
320
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
321
|
+
if (idFields.length === 1) {
|
|
322
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = new PolymorphicRef(${this.propName(discriminatorField)}, ${this.propName(idFields[0])});`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = new PolymorphicRef(${this.propName(discriminatorField)}, [${idFields.map(f => this.propName(f)).join(', ')}]);`);
|
|
326
|
+
}
|
|
327
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
328
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n${padding} ret${this.wrap(prop.name)} = null;`);
|
|
329
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
314
332
|
if (prop.targetMeta && prop.fieldNames.length > 1) {
|
|
315
333
|
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
316
334
|
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
317
335
|
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.createCompositeKeyArray(prop)};`);
|
|
318
336
|
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
319
|
-
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n ret${this.wrap(prop.name)} = null;`);
|
|
337
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n${padding} ret${this.wrap(prop.name)} = null;`);
|
|
320
338
|
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
321
339
|
continue;
|
|
322
340
|
}
|
|
@@ -517,6 +535,27 @@ export class EntityComparator {
|
|
|
517
535
|
ret += ` ret${dataKey} = entity${entityKey};\n`;
|
|
518
536
|
}
|
|
519
537
|
}
|
|
538
|
+
else if (prop.polymorphic) {
|
|
539
|
+
const discriminatorMapKey = `discriminatorMapReverse_${prop.name}`;
|
|
540
|
+
const reverseMap = new Map();
|
|
541
|
+
for (const [key, value] of Object.entries(prop.discriminatorMap)) {
|
|
542
|
+
reverseMap.set(value, key);
|
|
543
|
+
}
|
|
544
|
+
context.set(discriminatorMapKey, reverseMap);
|
|
545
|
+
this.setToArrayHelper(context);
|
|
546
|
+
context.set('EntityIdentifier', EntityIdentifier);
|
|
547
|
+
context.set('PolymorphicRef', PolymorphicRef);
|
|
548
|
+
ret += ` if (entity${entityKey} === null) {\n`;
|
|
549
|
+
ret += ` ret${dataKey} = null;\n`;
|
|
550
|
+
ret += ` } else if (typeof entity${entityKey} !== 'undefined') {\n`;
|
|
551
|
+
ret += ` const val${level} = entity${entityKey}${unwrap};\n`;
|
|
552
|
+
ret += ` const discriminator = ${discriminatorMapKey}.get(val${level}?.constructor);\n`;
|
|
553
|
+
ret += ` const pk = val${level}?.__helper?.__identifier && !val${level}?.__helper?.hasPrimaryKey()\n`;
|
|
554
|
+
ret += ` ? val${level}.__helper.__identifier\n`;
|
|
555
|
+
ret += ` : toArray(val${level}?.__helper?.getPrimaryKey(true));\n`;
|
|
556
|
+
ret += ` ret${dataKey} = new PolymorphicRef(discriminator, pk);\n`;
|
|
557
|
+
ret += ` }\n`;
|
|
558
|
+
}
|
|
520
559
|
else if (prop.targetKey) {
|
|
521
560
|
// When targetKey is set, extract that property value instead of the PK
|
|
522
561
|
const targetProp = prop.targetMeta?.properties[prop.targetKey];
|
|
@@ -535,13 +574,7 @@ export class EntityComparator {
|
|
|
535
574
|
ret += ` }\n`;
|
|
536
575
|
}
|
|
537
576
|
else {
|
|
538
|
-
|
|
539
|
-
if (Utils.isPlainObject(val)) {
|
|
540
|
-
return Object.values(val).map(v => toArray(v));
|
|
541
|
-
}
|
|
542
|
-
return val;
|
|
543
|
-
};
|
|
544
|
-
context.set('toArray', toArray);
|
|
577
|
+
this.setToArrayHelper(context);
|
|
545
578
|
context.set('EntityIdentifier', EntityIdentifier);
|
|
546
579
|
ret += ` if (entity${entityKey} === null) {\n`;
|
|
547
580
|
ret += ` ret${dataKey} = null;\n`;
|
|
@@ -613,12 +646,17 @@ export class EntityComparator {
|
|
|
613
646
|
getPropertyComparator(prop, context) {
|
|
614
647
|
let type = prop.type.toLowerCase();
|
|
615
648
|
if (prop.kind !== ReferenceKind.SCALAR && prop.kind !== ReferenceKind.EMBEDDED) {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
type = 'array';
|
|
649
|
+
if (prop.polymorphic) {
|
|
650
|
+
type = 'object';
|
|
619
651
|
}
|
|
620
652
|
else {
|
|
621
|
-
|
|
653
|
+
const meta2 = prop.targetMeta;
|
|
654
|
+
if (meta2.primaryKeys.length > 1) {
|
|
655
|
+
type = 'array';
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
type = meta2.getPrimaryProp().type.toLowerCase();
|
|
659
|
+
}
|
|
622
660
|
}
|
|
623
661
|
}
|
|
624
662
|
if (prop.customType) {
|
|
@@ -670,6 +708,22 @@ export class EntityComparator {
|
|
|
670
708
|
safeKey(key) {
|
|
671
709
|
return key.replace(/\W/g, '_');
|
|
672
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Sets the toArray helper in the context if not already set.
|
|
713
|
+
* Used for converting composite PKs to arrays.
|
|
714
|
+
*/
|
|
715
|
+
setToArrayHelper(context) {
|
|
716
|
+
if (context.has('toArray')) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const toArray = (val) => {
|
|
720
|
+
if (Utils.isPlainObject(val)) {
|
|
721
|
+
return Object.values(val).map(v => toArray(v));
|
|
722
|
+
}
|
|
723
|
+
return val;
|
|
724
|
+
};
|
|
725
|
+
context.set('toArray', toArray);
|
|
726
|
+
}
|
|
673
727
|
/**
|
|
674
728
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
675
729
|
*/
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
|
5
5
|
/** @internal */
|
|
6
6
|
export declare class QueryHelper {
|
|
7
7
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
10
|
+
*/
|
|
11
|
+
static findDiscriminatorValue<T>(discriminatorMap: Dictionary<T>, targetClass: T): string | undefined;
|
|
8
12
|
static processParams(params: unknown): any;
|
|
9
13
|
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
10
14
|
/**
|
package/utils/QueryHelper.js
CHANGED
|
@@ -7,6 +7,12 @@ import { isRaw, Raw } from './RawQueryFragment.js';
|
|
|
7
7
|
/** @internal */
|
|
8
8
|
export class QueryHelper {
|
|
9
9
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
10
|
+
/**
|
|
11
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
12
|
+
*/
|
|
13
|
+
static findDiscriminatorValue(discriminatorMap, targetClass) {
|
|
14
|
+
return Object.entries(discriminatorMap).find(([, cls]) => cls === targetClass)?.[0];
|
|
15
|
+
}
|
|
10
16
|
static processParams(params) {
|
|
11
17
|
if (Reference.isReference(params)) {
|
|
12
18
|
params = params.unwrap();
|
|
@@ -62,7 +68,10 @@ export class QueryHelper {
|
|
|
62
68
|
for (const k of keys) {
|
|
63
69
|
const value = where[k];
|
|
64
70
|
const prop = meta.properties[k];
|
|
65
|
-
|
|
71
|
+
// Polymorphic relations use multiple columns (discriminator + FK), so they cannot
|
|
72
|
+
// participate in the standard single-column FK expansion. Query by discriminator
|
|
73
|
+
// column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
|
|
74
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
|
|
66
75
|
continue;
|
|
67
76
|
}
|
|
68
77
|
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
package/utils/Utils.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CompiledFunctions, Dictionary, EntityData, EntityDictionary, EntityKey, EntityMetadata, EntityName, EntityProperty, Primary } from '../typings.js';
|
|
2
2
|
import type { Collection } from '../entity/Collection.js';
|
|
3
3
|
import type { Platform } from '../platforms/Platform.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type ScalarReference } from '../entity/Reference.js';
|
|
5
5
|
import { type RawQueryFragmentSymbol } from './RawQueryFragment.js';
|
|
6
6
|
export declare function compareObjects(a: any, b: any): boolean;
|
|
7
7
|
export declare function compareArrays(a: any[] | string, b: any[] | string): boolean;
|
package/utils/Utils.js
CHANGED
|
@@ -123,7 +123,7 @@ export function parseJsonSafe(value) {
|
|
|
123
123
|
}
|
|
124
124
|
export class Utils {
|
|
125
125
|
static PK_SEPARATOR = '~~~';
|
|
126
|
-
static #ORM_VERSION = '7.0.0-dev.
|
|
126
|
+
static #ORM_VERSION = '7.0.0-dev.227';
|
|
127
127
|
/**
|
|
128
128
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
129
129
|
*/
|