@mikro-orm/core 7.0.4 → 7.0.5-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.
- package/EntityManager.d.ts +583 -884
- package/EntityManager.js +1895 -1922
- package/MikroORM.d.ts +74 -103
- package/MikroORM.js +179 -178
- package/README.md +1 -1
- package/cache/CacheAdapter.d.ts +36 -36
- package/cache/FileCacheAdapter.d.ts +24 -30
- package/cache/FileCacheAdapter.js +78 -80
- package/cache/GeneratedCacheAdapter.d.ts +20 -18
- package/cache/GeneratedCacheAdapter.js +30 -30
- package/cache/MemoryCacheAdapter.d.ts +20 -18
- package/cache/MemoryCacheAdapter.js +36 -35
- package/cache/NullCacheAdapter.d.ts +16 -16
- package/cache/NullCacheAdapter.js +24 -24
- package/connections/Connection.d.ts +84 -95
- package/connections/Connection.js +168 -165
- package/drivers/DatabaseDriver.d.ts +80 -186
- package/drivers/DatabaseDriver.js +443 -450
- package/drivers/IDatabaseDriver.d.ts +301 -440
- package/entity/BaseEntity.d.ts +83 -120
- package/entity/BaseEntity.js +43 -43
- package/entity/Collection.d.ts +179 -212
- package/entity/Collection.js +721 -727
- package/entity/EntityAssigner.d.ts +77 -88
- package/entity/EntityAssigner.js +230 -231
- package/entity/EntityFactory.d.ts +54 -66
- package/entity/EntityFactory.js +383 -425
- package/entity/EntityHelper.d.ts +22 -34
- package/entity/EntityHelper.js +267 -280
- package/entity/EntityIdentifier.d.ts +4 -4
- package/entity/EntityIdentifier.js +10 -10
- package/entity/EntityLoader.d.ts +72 -98
- package/entity/EntityLoader.js +723 -753
- package/entity/EntityRepository.d.ts +201 -316
- package/entity/EntityRepository.js +213 -213
- package/entity/PolymorphicRef.d.ts +5 -5
- package/entity/PolymorphicRef.js +10 -10
- package/entity/Reference.d.ts +82 -126
- package/entity/Reference.js +274 -278
- package/entity/WrappedEntity.d.ts +72 -115
- package/entity/WrappedEntity.js +166 -168
- package/entity/defineEntity.d.ts +636 -1315
- package/entity/defineEntity.js +518 -527
- package/entity/utils.d.ts +3 -13
- package/entity/utils.js +73 -71
- package/entity/validators.js +43 -43
- package/entity/wrap.js +8 -8
- package/enums.d.ts +253 -258
- package/enums.js +252 -251
- package/errors.d.ts +72 -114
- package/errors.js +253 -350
- package/events/EventManager.d.ts +14 -26
- package/events/EventManager.js +77 -79
- package/events/EventSubscriber.d.ts +29 -29
- package/events/TransactionEventBroadcaster.d.ts +8 -15
- package/events/TransactionEventBroadcaster.js +14 -14
- package/exceptions.d.ts +40 -23
- package/exceptions.js +52 -35
- package/hydration/Hydrator.d.ts +17 -42
- package/hydration/Hydrator.js +43 -43
- package/hydration/ObjectHydrator.d.ts +17 -50
- package/hydration/ObjectHydrator.js +416 -481
- package/index.d.ts +2 -116
- package/index.js +1 -10
- package/logging/DefaultLogger.d.ts +32 -34
- package/logging/DefaultLogger.js +86 -86
- package/logging/Logger.d.ts +41 -41
- package/logging/SimpleLogger.d.ts +11 -13
- package/logging/SimpleLogger.js +22 -22
- package/logging/colors.d.ts +6 -6
- package/logging/colors.js +10 -11
- package/logging/inspect.js +7 -7
- package/metadata/EntitySchema.d.ts +127 -211
- package/metadata/EntitySchema.js +398 -397
- package/metadata/MetadataDiscovery.d.ts +114 -114
- package/metadata/MetadataDiscovery.js +1870 -1951
- package/metadata/MetadataProvider.d.ts +21 -24
- package/metadata/MetadataProvider.js +84 -82
- package/metadata/MetadataStorage.d.ts +32 -38
- package/metadata/MetadataStorage.js +118 -118
- package/metadata/MetadataValidator.d.ts +39 -39
- package/metadata/MetadataValidator.js +338 -381
- package/metadata/discover-entities.d.ts +2 -5
- package/metadata/discover-entities.js +37 -35
- package/metadata/types.d.ts +531 -615
- package/naming-strategy/AbstractNamingStrategy.d.ts +39 -54
- package/naming-strategy/AbstractNamingStrategy.js +85 -90
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
- package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
- package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
- package/naming-strategy/MongoNamingStrategy.js +18 -18
- package/naming-strategy/NamingStrategy.d.ts +99 -109
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
- package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
- package/not-supported.js +4 -7
- package/package.json +1 -1
- package/platforms/ExceptionConverter.d.ts +1 -1
- package/platforms/ExceptionConverter.js +4 -4
- package/platforms/Platform.d.ts +301 -310
- package/platforms/Platform.js +640 -663
- package/serialization/EntitySerializer.d.ts +26 -49
- package/serialization/EntitySerializer.js +218 -224
- package/serialization/EntityTransformer.d.ts +6 -10
- package/serialization/EntityTransformer.js +217 -219
- package/serialization/SerializationContext.d.ts +23 -27
- package/serialization/SerializationContext.js +105 -105
- package/types/ArrayType.d.ts +8 -8
- package/types/ArrayType.js +33 -33
- package/types/BigIntType.d.ts +10 -17
- package/types/BigIntType.js +37 -37
- package/types/BlobType.d.ts +3 -3
- package/types/BlobType.js +13 -13
- package/types/BooleanType.d.ts +4 -4
- package/types/BooleanType.js +12 -12
- package/types/CharacterType.d.ts +2 -2
- package/types/CharacterType.js +6 -6
- package/types/DateTimeType.d.ts +5 -5
- package/types/DateTimeType.js +15 -15
- package/types/DateType.d.ts +5 -5
- package/types/DateType.js +15 -15
- package/types/DecimalType.d.ts +7 -7
- package/types/DecimalType.js +26 -26
- package/types/DoubleType.d.ts +3 -3
- package/types/DoubleType.js +12 -12
- package/types/EnumArrayType.d.ts +5 -5
- package/types/EnumArrayType.js +24 -24
- package/types/EnumType.d.ts +3 -3
- package/types/EnumType.js +11 -11
- package/types/FloatType.d.ts +3 -3
- package/types/FloatType.js +9 -9
- package/types/IntegerType.d.ts +3 -3
- package/types/IntegerType.js +9 -9
- package/types/IntervalType.d.ts +4 -4
- package/types/IntervalType.js +12 -12
- package/types/JsonType.d.ts +8 -8
- package/types/JsonType.js +32 -32
- package/types/MediumIntType.d.ts +1 -1
- package/types/MediumIntType.js +3 -3
- package/types/SmallIntType.d.ts +3 -3
- package/types/SmallIntType.js +9 -9
- package/types/StringType.d.ts +4 -4
- package/types/StringType.js +12 -12
- package/types/TextType.d.ts +3 -3
- package/types/TextType.js +9 -9
- package/types/TimeType.d.ts +5 -5
- package/types/TimeType.js +17 -17
- package/types/TinyIntType.d.ts +3 -3
- package/types/TinyIntType.js +10 -10
- package/types/Type.d.ts +79 -83
- package/types/Type.js +82 -82
- package/types/Uint8ArrayType.d.ts +4 -4
- package/types/Uint8ArrayType.js +21 -21
- package/types/UnknownType.d.ts +4 -4
- package/types/UnknownType.js +12 -12
- package/types/UuidType.d.ts +5 -5
- package/types/UuidType.js +19 -19
- package/types/index.d.ts +49 -75
- package/types/index.js +26 -52
- package/typings.d.ts +737 -1250
- package/typings.js +231 -244
- package/unit-of-work/ChangeSet.d.ts +26 -26
- package/unit-of-work/ChangeSet.js +56 -56
- package/unit-of-work/ChangeSetComputer.d.ts +12 -12
- package/unit-of-work/ChangeSetComputer.js +170 -178
- package/unit-of-work/ChangeSetPersister.d.ts +44 -63
- package/unit-of-work/ChangeSetPersister.js +421 -442
- package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
- package/unit-of-work/CommitOrderCalculator.js +88 -89
- package/unit-of-work/IdentityMap.d.ts +31 -31
- package/unit-of-work/IdentityMap.js +105 -105
- package/unit-of-work/UnitOfWork.d.ts +141 -181
- package/unit-of-work/UnitOfWork.js +1183 -1200
- package/utils/AbstractMigrator.d.ts +91 -111
- package/utils/AbstractMigrator.js +275 -275
- package/utils/AbstractSchemaGenerator.d.ts +34 -43
- package/utils/AbstractSchemaGenerator.js +122 -121
- package/utils/AsyncContext.d.ts +3 -3
- package/utils/AsyncContext.js +35 -34
- package/utils/Configuration.d.ts +808 -852
- package/utils/Configuration.js +344 -359
- package/utils/Cursor.d.ts +22 -40
- package/utils/Cursor.js +127 -135
- package/utils/DataloaderUtils.d.ts +43 -58
- package/utils/DataloaderUtils.js +198 -203
- package/utils/EntityComparator.d.ts +81 -98
- package/utils/EntityComparator.js +732 -828
- package/utils/NullHighlighter.d.ts +1 -1
- package/utils/NullHighlighter.js +3 -3
- package/utils/QueryHelper.d.ts +51 -79
- package/utils/QueryHelper.js +361 -372
- package/utils/RawQueryFragment.d.ts +34 -50
- package/utils/RawQueryFragment.js +105 -107
- package/utils/RequestContext.d.ts +32 -32
- package/utils/RequestContext.js +53 -52
- package/utils/TransactionContext.d.ts +16 -16
- package/utils/TransactionContext.js +27 -27
- package/utils/TransactionManager.d.ts +58 -58
- package/utils/TransactionManager.js +197 -199
- package/utils/Utils.d.ts +145 -204
- package/utils/Utils.js +812 -812
- package/utils/clone.js +113 -104
- package/utils/env-vars.js +88 -90
- package/utils/fs-utils.d.ts +15 -15
- package/utils/fs-utils.js +181 -180
- package/utils/upsert-utils.d.ts +5 -20
- package/utils/upsert-utils.js +116 -114
|
@@ -17,409 +17,366 @@ const DANGEROUS_PROPERTY_NAMES = ['__proto__', 'constructor', 'prototype'];
|
|
|
17
17
|
* @internal
|
|
18
18
|
*/
|
|
19
19
|
export class MetadataValidator {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
// Virtual entities (expression without view flag) have restrictions - no PKs, limited relation types
|
|
29
|
-
// Note: meta.virtual is set later in sync(), so we check for expression && !view here
|
|
30
|
-
if (meta.virtual || (meta.expression && !meta.view)) {
|
|
31
|
-
for (const prop of Utils.values(meta.properties)) {
|
|
32
|
-
if (
|
|
33
|
-
![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(
|
|
34
|
-
prop.kind,
|
|
35
|
-
)
|
|
36
|
-
) {
|
|
37
|
-
throw new MetadataError(
|
|
38
|
-
`Only scalars, embedded properties and to-many relations are allowed inside virtual entity. Found '${prop.kind}' in ${meta.className}.${prop.name}`,
|
|
39
|
-
);
|
|
20
|
+
validateEntityDefinition(metadata, name, options) {
|
|
21
|
+
const meta = metadata.get(name);
|
|
22
|
+
// View entities (expression with view flag) behave like regular tables but are read-only
|
|
23
|
+
// They can have primary keys and are created as actual database views
|
|
24
|
+
if (meta.view) {
|
|
25
|
+
this.validateViewEntity(meta);
|
|
26
|
+
return;
|
|
40
27
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
// Virtual entities (expression without view flag) have restrictions - no PKs, limited relation types
|
|
29
|
+
// Note: meta.virtual is set later in sync(), so we check for expression && !view here
|
|
30
|
+
if (meta.virtual || (meta.expression && !meta.view)) {
|
|
31
|
+
for (const prop of Utils.values(meta.properties)) {
|
|
32
|
+
if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
33
|
+
throw new MetadataError(`Only scalars, embedded properties and to-many relations are allowed inside virtual entity. Found '${prop.kind}' in ${meta.className}.${prop.name}`);
|
|
34
|
+
}
|
|
35
|
+
if (prop.primary) {
|
|
36
|
+
throw new MetadataError(`Virtual entity ${meta.className} cannot have primary key ${meta.className}.${prop.name}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// entities have PK
|
|
42
|
+
if (!meta.embeddable && (!meta.primaryKeys || meta.primaryKeys.length === 0)) {
|
|
43
|
+
throw MetadataError.fromMissingPrimaryKey(meta);
|
|
44
|
+
}
|
|
45
|
+
this.validateVersionField(meta);
|
|
46
|
+
this.validateDuplicateFieldNames(meta, options);
|
|
47
|
+
this.validateIndexes(meta, meta.indexes ?? [], 'index');
|
|
48
|
+
this.validateIndexes(meta, meta.uniques ?? [], 'unique');
|
|
49
|
+
this.validatePropertyNames(meta);
|
|
50
|
+
for (const prop of Utils.values(meta.properties)) {
|
|
51
|
+
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
52
|
+
this.validateReference(meta, prop, options);
|
|
53
|
+
this.validateBidirectional(meta, prop);
|
|
54
|
+
}
|
|
55
|
+
else if (metadata.getByClassName(prop.type, false)) {
|
|
56
|
+
throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.getByClassName(prop.type));
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
|
-
}
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
// entities have PK
|
|
50
|
-
if (!meta.embeddable && (!meta.primaryKeys || meta.primaryKeys.length === 0)) {
|
|
51
|
-
throw MetadataError.fromMissingPrimaryKey(meta);
|
|
52
|
-
}
|
|
53
|
-
this.validateVersionField(meta);
|
|
54
|
-
this.validateDuplicateFieldNames(meta, options);
|
|
55
|
-
this.validateIndexes(meta, meta.indexes ?? [], 'index');
|
|
56
|
-
this.validateIndexes(meta, meta.uniques ?? [], 'unique');
|
|
57
|
-
this.validatePropertyNames(meta);
|
|
58
|
-
for (const prop of Utils.values(meta.properties)) {
|
|
59
|
-
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
60
|
-
this.validateReference(meta, prop, options);
|
|
61
|
-
this.validateBidirectional(meta, prop);
|
|
62
|
-
} else if (metadata.getByClassName(prop.type, false)) {
|
|
63
|
-
throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.getByClassName(prop.type));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
validateDiscovered(discovered, options) {
|
|
68
|
-
if (discovered.length === 0 && options.warnWhenNoEntities) {
|
|
69
|
-
throw MetadataError.noEntityDiscovered();
|
|
70
|
-
}
|
|
71
|
-
// Validate no mixing of STI and TPT in the same hierarchy
|
|
72
|
-
this.validateInheritanceStrategies(discovered);
|
|
73
|
-
const tableNames = discovered.filter(
|
|
74
|
-
meta =>
|
|
75
|
-
!meta.abstract &&
|
|
76
|
-
!meta.embeddable &&
|
|
77
|
-
meta === meta.root &&
|
|
78
|
-
(meta.tableName || meta.collection) &&
|
|
79
|
-
meta.schema !== '*',
|
|
80
|
-
);
|
|
81
|
-
const duplicateTableNames = Utils.findDuplicates(
|
|
82
|
-
tableNames.map(meta => {
|
|
83
|
-
const tableName = meta.tableName || meta.collection;
|
|
84
|
-
return (meta.schema ? '.' + meta.schema : '') + tableName;
|
|
85
|
-
}),
|
|
86
|
-
);
|
|
87
|
-
if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames) {
|
|
88
|
-
throw MetadataError.duplicateEntityDiscovered(duplicateTableNames);
|
|
89
|
-
}
|
|
90
|
-
// validate we found at least one entity (not just abstract/base entities)
|
|
91
|
-
if (discovered.filter(meta => meta.name).length === 0 && options.warnWhenNoEntities) {
|
|
92
|
-
throw MetadataError.onlyAbstractEntitiesDiscovered();
|
|
93
|
-
}
|
|
94
|
-
const unwrap = type =>
|
|
95
|
-
type
|
|
96
|
-
.replace(/Array<(.*)>/, '$1') // unwrap array
|
|
97
|
-
.replace(/\[]$/, '') // remove array suffix
|
|
98
|
-
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
99
|
-
const name = p => {
|
|
100
|
-
if (typeof p === 'function' && !p.prototype) {
|
|
101
|
-
return Utils.className(p());
|
|
102
|
-
}
|
|
103
|
-
return Utils.className(p);
|
|
104
|
-
};
|
|
105
|
-
const pivotProps = new Map();
|
|
106
|
-
// check for not discovered entities
|
|
107
|
-
discovered.forEach(meta =>
|
|
108
|
-
Object.values(meta.properties).forEach(prop => {
|
|
109
|
-
if (
|
|
110
|
-
prop.kind !== ReferenceKind.SCALAR &&
|
|
111
|
-
!unwrap(prop.type)
|
|
112
|
-
.split(/ ?\| ?/)
|
|
113
|
-
.every(type => discovered.find(m => m.className === type))
|
|
114
|
-
) {
|
|
115
|
-
throw MetadataError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
|
|
116
|
-
}
|
|
117
|
-
if (prop.pivotEntity) {
|
|
118
|
-
const props = pivotProps.get(name(prop.pivotEntity)) ?? [];
|
|
119
|
-
props.push({ meta, prop });
|
|
120
|
-
pivotProps.set(name(prop.pivotEntity), props);
|
|
121
|
-
}
|
|
122
|
-
}),
|
|
123
|
-
);
|
|
124
|
-
pivotProps.forEach(props => {
|
|
125
|
-
// if the pivot entity is used in more than one property, check if they are linked
|
|
126
|
-
if (props.length > 1 && props.every(p => !p.prop.mappedBy && !p.prop.inversedBy)) {
|
|
127
|
-
throw MetadataError.invalidManyToManyWithPivotEntity(
|
|
128
|
-
props[0].meta,
|
|
129
|
-
props[0].prop,
|
|
130
|
-
props[1].meta,
|
|
131
|
-
props[1].prop,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
validateReference(meta, prop, options) {
|
|
137
|
-
// references do have types
|
|
138
|
-
if (!prop.type) {
|
|
139
|
-
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
140
|
-
}
|
|
141
|
-
// Polymorphic relations have multiple targets, validate PK compatibility
|
|
142
|
-
if (prop.polymorphic && prop.polymorphTargets) {
|
|
143
|
-
this.validatePolymorphicTargets(meta, prop);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const targetMeta = prop.targetMeta;
|
|
147
|
-
// references do have type of known entity
|
|
148
|
-
if (!targetMeta) {
|
|
149
|
-
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
150
|
-
}
|
|
151
|
-
if (targetMeta.abstract && !targetMeta.root?.inheritanceType && !targetMeta.embeddable) {
|
|
152
|
-
throw MetadataError.targetIsAbstract(meta, prop);
|
|
153
|
-
}
|
|
154
|
-
if (
|
|
155
|
-
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
156
|
-
prop.persist === false &&
|
|
157
|
-
targetMeta.compositePK &&
|
|
158
|
-
options.checkNonPersistentCompositeProps
|
|
159
|
-
) {
|
|
160
|
-
throw MetadataError.nonPersistentCompositeProp(meta, prop);
|
|
161
|
-
}
|
|
162
|
-
this.validateTargetKey(meta, prop, targetMeta);
|
|
163
|
-
}
|
|
164
|
-
validateTargetKey(meta, prop, targetMeta) {
|
|
165
|
-
if (!prop.targetKey) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
// targetKey is not supported for ManyToMany relations
|
|
169
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
170
|
-
throw MetadataError.targetKeyOnManyToMany(meta, prop);
|
|
171
|
-
}
|
|
172
|
-
// targetKey must point to an existing property
|
|
173
|
-
const targetProp = targetMeta.properties[prop.targetKey];
|
|
174
|
-
if (!targetProp) {
|
|
175
|
-
throw MetadataError.targetKeyNotFound(meta, prop);
|
|
176
|
-
}
|
|
177
|
-
// targetKey must point to a unique property (composite unique is not sufficient)
|
|
178
|
-
if (!this.isPropertyUnique(targetProp, targetMeta)) {
|
|
179
|
-
throw MetadataError.targetKeyNotUnique(meta, prop);
|
|
180
59
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
60
|
+
validateDiscovered(discovered, options) {
|
|
61
|
+
if (discovered.length === 0 && options.warnWhenNoEntities) {
|
|
62
|
+
throw MetadataError.noEntityDiscovered();
|
|
63
|
+
}
|
|
64
|
+
// Validate no mixing of STI and TPT in the same hierarchy
|
|
65
|
+
this.validateInheritanceStrategies(discovered);
|
|
66
|
+
const tableNames = discovered.filter(meta => !meta.abstract &&
|
|
67
|
+
!meta.embeddable &&
|
|
68
|
+
meta === meta.root &&
|
|
69
|
+
(meta.tableName || meta.collection) &&
|
|
70
|
+
meta.schema !== '*');
|
|
71
|
+
const duplicateTableNames = Utils.findDuplicates(tableNames.map(meta => {
|
|
72
|
+
const tableName = meta.tableName || meta.collection;
|
|
73
|
+
return (meta.schema ? '.' + meta.schema : '') + tableName;
|
|
74
|
+
}));
|
|
75
|
+
if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames) {
|
|
76
|
+
throw MetadataError.duplicateEntityDiscovered(duplicateTableNames);
|
|
77
|
+
}
|
|
78
|
+
// validate we found at least one entity (not just abstract/base entities)
|
|
79
|
+
if (discovered.filter(meta => meta.name).length === 0 && options.warnWhenNoEntities) {
|
|
80
|
+
throw MetadataError.onlyAbstractEntitiesDiscovered();
|
|
81
|
+
}
|
|
82
|
+
const unwrap = (type) => type
|
|
83
|
+
.replace(/Array<(.*)>/, '$1') // unwrap array
|
|
84
|
+
.replace(/\[]$/, '') // remove array suffix
|
|
85
|
+
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
86
|
+
const name = (p) => {
|
|
87
|
+
if (typeof p === 'function' && !p.prototype) {
|
|
88
|
+
return Utils.className(p());
|
|
89
|
+
}
|
|
90
|
+
return Utils.className(p);
|
|
91
|
+
};
|
|
92
|
+
const pivotProps = new Map();
|
|
93
|
+
// check for not discovered entities
|
|
94
|
+
discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
95
|
+
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
96
|
+
!unwrap(prop.type)
|
|
97
|
+
.split(/ ?\| ?/)
|
|
98
|
+
.every(type => discovered.find(m => m.className === type))) {
|
|
99
|
+
throw MetadataError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
|
|
100
|
+
}
|
|
101
|
+
if (prop.pivotEntity) {
|
|
102
|
+
const props = pivotProps.get(name(prop.pivotEntity)) ?? [];
|
|
103
|
+
props.push({ meta, prop });
|
|
104
|
+
pivotProps.set(name(prop.pivotEntity), props);
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
pivotProps.forEach(props => {
|
|
108
|
+
// if the pivot entity is used in more than one property, check if they are linked
|
|
109
|
+
if (props.length > 1 && props.every(p => !p.prop.mappedBy && !p.prop.inversedBy)) {
|
|
110
|
+
throw MetadataError.invalidManyToManyWithPivotEntity(props[0].meta, props[0].prop, props[1].meta, props[1].prop);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
validateReference(meta, prop, options) {
|
|
115
|
+
// references do have types
|
|
116
|
+
if (!prop.type) {
|
|
117
|
+
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
118
|
+
}
|
|
119
|
+
// Polymorphic relations have multiple targets, validate PK compatibility
|
|
120
|
+
if (prop.polymorphic && prop.polymorphTargets) {
|
|
121
|
+
this.validatePolymorphicTargets(meta, prop);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const targetMeta = prop.targetMeta;
|
|
125
|
+
// references do have type of known entity
|
|
126
|
+
if (!targetMeta) {
|
|
127
|
+
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
128
|
+
}
|
|
129
|
+
if (targetMeta.abstract && !targetMeta.root?.inheritanceType && !targetMeta.embeddable) {
|
|
130
|
+
throw MetadataError.targetIsAbstract(meta, prop);
|
|
131
|
+
}
|
|
132
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
133
|
+
prop.persist === false &&
|
|
134
|
+
targetMeta.compositePK &&
|
|
135
|
+
options.checkNonPersistentCompositeProps) {
|
|
136
|
+
throw MetadataError.nonPersistentCompositeProp(meta, prop);
|
|
137
|
+
}
|
|
138
|
+
this.validateTargetKey(meta, prop, targetMeta);
|
|
189
139
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
for (const target of targets) {
|
|
201
|
-
const targetProp = target.properties[prop.targetKey];
|
|
140
|
+
validateTargetKey(meta, prop, targetMeta) {
|
|
141
|
+
if (!prop.targetKey) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// targetKey is not supported for ManyToMany relations
|
|
145
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
146
|
+
throw MetadataError.targetKeyOnManyToMany(meta, prop);
|
|
147
|
+
}
|
|
148
|
+
// targetKey must point to an existing property
|
|
149
|
+
const targetProp = targetMeta.properties[prop.targetKey];
|
|
202
150
|
if (!targetProp) {
|
|
203
|
-
|
|
151
|
+
throw MetadataError.targetKeyNotFound(meta, prop);
|
|
204
152
|
}
|
|
205
153
|
// targetKey must point to a unique property (composite unique is not sufficient)
|
|
206
|
-
if (!this.isPropertyUnique(targetProp,
|
|
207
|
-
|
|
154
|
+
if (!this.isPropertyUnique(targetProp, targetMeta)) {
|
|
155
|
+
throw MetadataError.targetKeyNotUnique(meta, prop);
|
|
208
156
|
}
|
|
209
|
-
}
|
|
210
157
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Checks if a property has a unique constraint (either via `unique: true` or single-property `@Unique` decorator).
|
|
160
|
+
* Composite unique constraints are not sufficient for targetKey.
|
|
161
|
+
*/
|
|
162
|
+
isPropertyUnique(prop, meta) {
|
|
163
|
+
if (prop.unique) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
// Check for single-property unique constraint via @Unique decorator
|
|
167
|
+
return !!meta.uniques?.some(u => {
|
|
168
|
+
const props = Utils.asArray(u.properties);
|
|
169
|
+
return props.length === 1 && props[0] === prop.name && !u.options;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
validatePolymorphicTargets(meta, prop) {
|
|
173
|
+
const targets = prop.polymorphTargets;
|
|
174
|
+
// Validate targetKey exists and is compatible across all targets
|
|
175
|
+
if (prop.targetKey) {
|
|
176
|
+
for (const target of targets) {
|
|
177
|
+
const targetProp = target.properties[prop.targetKey];
|
|
178
|
+
if (!targetProp) {
|
|
179
|
+
throw MetadataError.targetKeyNotFound(meta, prop, target);
|
|
180
|
+
}
|
|
181
|
+
// targetKey must point to a unique property (composite unique is not sufficient)
|
|
182
|
+
if (!this.isPropertyUnique(targetProp, target)) {
|
|
183
|
+
throw MetadataError.targetKeyNotUnique(meta, prop, target);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const firstPKs = targets[0].getPrimaryProps();
|
|
188
|
+
for (let i = 1; i < targets.length; i++) {
|
|
189
|
+
const target = targets[i];
|
|
190
|
+
const targetPKs = target.getPrimaryProps();
|
|
191
|
+
if (targetPKs.length !== firstPKs.length) {
|
|
192
|
+
throw MetadataError.incompatiblePolymorphicTargets(meta, prop, targets[0], target, 'different number of primary keys');
|
|
193
|
+
}
|
|
194
|
+
for (let j = 0; j < firstPKs.length; j++) {
|
|
195
|
+
const firstPK = firstPKs[j];
|
|
196
|
+
const targetPK = targetPKs[j];
|
|
197
|
+
if (firstPK.runtimeType !== targetPK.runtimeType) {
|
|
198
|
+
throw MetadataError.incompatiblePolymorphicTargets(meta, prop, targets[0], target, `incompatible primary key types: ${firstPK.name} (${firstPK.runtimeType}) vs ${targetPK.name} (${targetPK.runtimeType})`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
237
202
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
203
|
+
validateBidirectional(meta, prop) {
|
|
204
|
+
if (prop.inversedBy) {
|
|
205
|
+
this.validateOwningSide(meta, prop);
|
|
206
|
+
}
|
|
207
|
+
else if (prop.mappedBy) {
|
|
208
|
+
this.validateInverseSide(meta, prop);
|
|
209
|
+
}
|
|
210
|
+
else if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
|
|
211
|
+
// 1:m property has `mappedBy`
|
|
212
|
+
throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
|
|
213
|
+
}
|
|
247
214
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
215
|
+
validateOwningSide(meta, prop) {
|
|
216
|
+
// For polymorphic relations, inversedBy may point to multiple entity types
|
|
217
|
+
if (prop.polymorphic && prop.polymorphTargets?.length) {
|
|
218
|
+
// For polymorphic relations, validate inversedBy against each target
|
|
219
|
+
// The inverse property should exist on the target entities and reference back to this property
|
|
220
|
+
for (const targetMeta of prop.polymorphTargets) {
|
|
221
|
+
const inverse = targetMeta.properties[prop.inversedBy];
|
|
222
|
+
// The inverse property is optional - some targets may not have it
|
|
223
|
+
if (!inverse) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
// Validate the inverse property
|
|
227
|
+
if (inverse.targetMeta?.root.class !== meta.root.class) {
|
|
228
|
+
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
|
|
229
|
+
}
|
|
230
|
+
// inverse side is not defined as owner
|
|
231
|
+
if (inverse.inversedBy || inverse.owner) {
|
|
232
|
+
throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const inverse = prop.targetMeta.properties[prop.inversedBy];
|
|
238
|
+
// has correct `inversedBy` on owning side
|
|
257
239
|
if (!inverse) {
|
|
258
|
-
|
|
240
|
+
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
|
|
259
241
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
242
|
+
const targetClass = inverse.targetMeta?.root.class;
|
|
243
|
+
// has correct `inversedBy` reference type
|
|
244
|
+
if (inverse.type !== meta.className && targetClass !== meta.root.class) {
|
|
245
|
+
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
|
|
263
246
|
}
|
|
264
247
|
// inverse side is not defined as owner
|
|
265
248
|
if (inverse.inversedBy || inverse.owner) {
|
|
266
|
-
|
|
249
|
+
throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
|
|
267
250
|
}
|
|
268
|
-
}
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const inverse = prop.targetMeta.properties[prop.inversedBy];
|
|
272
|
-
// has correct `inversedBy` on owning side
|
|
273
|
-
if (!inverse) {
|
|
274
|
-
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
|
|
275
|
-
}
|
|
276
|
-
const targetClass = inverse.targetMeta?.root.class;
|
|
277
|
-
// has correct `inversedBy` reference type
|
|
278
|
-
if (inverse.type !== meta.className && targetClass !== meta.root.class) {
|
|
279
|
-
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
|
|
280
|
-
}
|
|
281
|
-
// inverse side is not defined as owner
|
|
282
|
-
if (inverse.inversedBy || inverse.owner) {
|
|
283
|
-
throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
validateInverseSide(meta, prop) {
|
|
287
|
-
const owner = prop.targetMeta.properties[prop.mappedBy];
|
|
288
|
-
// has correct `mappedBy` on inverse side
|
|
289
|
-
if (prop.mappedBy && !owner) {
|
|
290
|
-
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy');
|
|
291
|
-
}
|
|
292
|
-
// has correct `mappedBy` reference type
|
|
293
|
-
// For polymorphic relations, check if this entity is one of the polymorphic targets
|
|
294
|
-
const isValidPolymorphicInverse =
|
|
295
|
-
owner.polymorphic && owner.polymorphTargets?.some(target => target.class === meta.root.class);
|
|
296
|
-
if (
|
|
297
|
-
!isValidPolymorphicInverse &&
|
|
298
|
-
owner.type !== meta.className &&
|
|
299
|
-
owner.targetMeta?.root.class !== meta.root.class
|
|
300
|
-
) {
|
|
301
|
-
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
|
|
302
|
-
}
|
|
303
|
-
// owning side is not defined as inverse
|
|
304
|
-
if (owner.mappedBy) {
|
|
305
|
-
throw MetadataError.fromWrongOwnership(meta, prop, 'mappedBy');
|
|
306
|
-
}
|
|
307
|
-
// owning side is not defined as inverse
|
|
308
|
-
const valid = [
|
|
309
|
-
{ owner: ReferenceKind.MANY_TO_ONE, inverse: ReferenceKind.ONE_TO_MANY },
|
|
310
|
-
{ owner: ReferenceKind.MANY_TO_MANY, inverse: ReferenceKind.MANY_TO_MANY },
|
|
311
|
-
{ owner: ReferenceKind.ONE_TO_ONE, inverse: ReferenceKind.ONE_TO_ONE },
|
|
312
|
-
];
|
|
313
|
-
if (!valid.find(spec => spec.owner === owner.kind && spec.inverse === prop.kind)) {
|
|
314
|
-
throw MetadataError.fromWrongReferenceKind(meta, owner, prop);
|
|
315
|
-
}
|
|
316
|
-
if (prop.primary) {
|
|
317
|
-
throw MetadataError.fromInversideSidePrimary(meta, owner, prop);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
validateIndexes(meta, indexes, type) {
|
|
321
|
-
for (const index of indexes) {
|
|
322
|
-
for (const propName of Utils.asArray(index.properties)) {
|
|
323
|
-
const prop = meta.root.properties[propName];
|
|
324
|
-
if (!prop && !Object.values(meta.root.properties).some(p => propName.startsWith(p.name + '.'))) {
|
|
325
|
-
throw MetadataError.unknownIndexProperty(meta, propName, type);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
validateDuplicateFieldNames(meta, options) {
|
|
331
|
-
const candidates = Object.values(meta.properties)
|
|
332
|
-
.filter(
|
|
333
|
-
prop =>
|
|
334
|
-
prop.persist !== false &&
|
|
335
|
-
!prop.inherited &&
|
|
336
|
-
prop.fieldNames?.length === 1 &&
|
|
337
|
-
(prop.kind !== ReferenceKind.EMBEDDED || prop.object),
|
|
338
|
-
)
|
|
339
|
-
.map(prop => prop.fieldNames[0]);
|
|
340
|
-
const duplicates = Utils.findDuplicates(candidates);
|
|
341
|
-
if (duplicates.length > 0 && options.checkDuplicateFieldNames) {
|
|
342
|
-
const pairs = duplicates.flatMap(name => {
|
|
343
|
-
return Object.values(meta.properties)
|
|
344
|
-
.filter(p => p.fieldNames?.[0] === name)
|
|
345
|
-
.map(prop => {
|
|
346
|
-
return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
throw MetadataError.duplicateFieldName(meta.class, pairs);
|
|
350
251
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
252
|
+
validateInverseSide(meta, prop) {
|
|
253
|
+
const owner = prop.targetMeta.properties[prop.mappedBy];
|
|
254
|
+
// has correct `mappedBy` on inverse side
|
|
255
|
+
if (prop.mappedBy && !owner) {
|
|
256
|
+
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy');
|
|
257
|
+
}
|
|
258
|
+
// has correct `mappedBy` reference type
|
|
259
|
+
// For polymorphic relations, check if this entity is one of the polymorphic targets
|
|
260
|
+
const isValidPolymorphicInverse = owner.polymorphic && owner.polymorphTargets?.some(target => target.class === meta.root.class);
|
|
261
|
+
if (!isValidPolymorphicInverse &&
|
|
262
|
+
owner.type !== meta.className &&
|
|
263
|
+
owner.targetMeta?.root.class !== meta.root.class) {
|
|
264
|
+
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
|
|
265
|
+
}
|
|
266
|
+
// owning side is not defined as inverse
|
|
267
|
+
if (owner.mappedBy) {
|
|
268
|
+
throw MetadataError.fromWrongOwnership(meta, prop, 'mappedBy');
|
|
269
|
+
}
|
|
270
|
+
// owning side is not defined as inverse
|
|
271
|
+
const valid = [
|
|
272
|
+
{ owner: ReferenceKind.MANY_TO_ONE, inverse: ReferenceKind.ONE_TO_MANY },
|
|
273
|
+
{ owner: ReferenceKind.MANY_TO_MANY, inverse: ReferenceKind.MANY_TO_MANY },
|
|
274
|
+
{ owner: ReferenceKind.ONE_TO_ONE, inverse: ReferenceKind.ONE_TO_ONE },
|
|
275
|
+
];
|
|
276
|
+
if (!valid.find(spec => spec.owner === owner.kind && spec.inverse === prop.kind)) {
|
|
277
|
+
throw MetadataError.fromWrongReferenceKind(meta, owner, prop);
|
|
278
|
+
}
|
|
279
|
+
if (prop.primary) {
|
|
280
|
+
throw MetadataError.fromInversideSidePrimary(meta, owner, prop);
|
|
281
|
+
}
|
|
355
282
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
283
|
+
validateIndexes(meta, indexes, type) {
|
|
284
|
+
for (const index of indexes) {
|
|
285
|
+
for (const propName of Utils.asArray(index.properties)) {
|
|
286
|
+
const prop = meta.root.properties[propName];
|
|
287
|
+
if (!prop && !Object.values(meta.root.properties).some(p => propName.startsWith(p.name + '.'))) {
|
|
288
|
+
throw MetadataError.unknownIndexProperty(meta, propName, type);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
362
292
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
293
|
+
validateDuplicateFieldNames(meta, options) {
|
|
294
|
+
const candidates = Object.values(meta.properties)
|
|
295
|
+
.filter(prop => prop.persist !== false &&
|
|
296
|
+
!prop.inherited &&
|
|
297
|
+
prop.fieldNames?.length === 1 &&
|
|
298
|
+
(prop.kind !== ReferenceKind.EMBEDDED || prop.object))
|
|
299
|
+
.map(prop => prop.fieldNames[0]);
|
|
300
|
+
const duplicates = Utils.findDuplicates(candidates);
|
|
301
|
+
if (duplicates.length > 0 && options.checkDuplicateFieldNames) {
|
|
302
|
+
const pairs = duplicates.flatMap(name => {
|
|
303
|
+
return Object.values(meta.properties)
|
|
304
|
+
.filter(p => p.fieldNames?.[0] === name)
|
|
305
|
+
.map(prop => {
|
|
306
|
+
return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
throw MetadataError.duplicateFieldName(meta.class, pairs);
|
|
310
|
+
}
|
|
367
311
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
312
|
+
validateVersionField(meta) {
|
|
313
|
+
if (!meta.versionProperty) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const props = Object.values(meta.properties).filter(p => p.version);
|
|
317
|
+
if (props.length > 1) {
|
|
318
|
+
throw MetadataError.multipleVersionFields(meta, props.map(p => p.name));
|
|
319
|
+
}
|
|
320
|
+
const prop = meta.properties[meta.versionProperty];
|
|
321
|
+
const type = prop.runtimeType ?? prop.columnTypes?.[0] ?? prop.type;
|
|
322
|
+
if (type !== 'number' && type !== 'Date' && !type.startsWith('timestamp') && !type.startsWith('datetime')) {
|
|
323
|
+
throw MetadataError.invalidVersionFieldType(meta);
|
|
324
|
+
}
|
|
382
325
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Validates that entity properties do not use dangerous names that could lead to
|
|
328
|
+
* prototype pollution vulnerabilities. This validation ensures that property names
|
|
329
|
+
* cannot be exploited to modify object prototypes when values are assigned during
|
|
330
|
+
* entity hydration or persistence operations.
|
|
331
|
+
*
|
|
332
|
+
* @internal
|
|
333
|
+
*/
|
|
334
|
+
validatePropertyNames(meta) {
|
|
335
|
+
for (const prop of Utils.values(meta.properties)) {
|
|
336
|
+
if (DANGEROUS_PROPERTY_NAMES.includes(prop.name)) {
|
|
337
|
+
throw MetadataError.dangerousPropertyName(meta, prop);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
392
340
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
341
|
+
/**
|
|
342
|
+
* Validates view entity configuration.
|
|
343
|
+
* View entities must have an expression.
|
|
344
|
+
*/
|
|
345
|
+
validateViewEntity(meta) {
|
|
346
|
+
// View entities must have an expression
|
|
347
|
+
if (!meta.expression) {
|
|
348
|
+
throw MetadataError.viewEntityWithoutExpression(meta);
|
|
349
|
+
}
|
|
350
|
+
// Validate indexes if present
|
|
351
|
+
this.validateIndexes(meta, meta.indexes ?? [], 'index');
|
|
352
|
+
this.validateIndexes(meta, meta.uniques ?? [], 'unique');
|
|
353
|
+
// Validate property names
|
|
354
|
+
this.validatePropertyNames(meta);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Validates that STI and TPT are not mixed in the same inheritance hierarchy.
|
|
358
|
+
* An entity hierarchy can use either STI (discriminatorColumn) or TPT (inheritance: 'tpt'),
|
|
359
|
+
* but not both.
|
|
360
|
+
*
|
|
361
|
+
* Note: This validation runs before `initTablePerTypeInheritance` sets `inheritanceType`,
|
|
362
|
+
* so we check the raw `inheritance` option from the decorator/schema.
|
|
363
|
+
*/
|
|
364
|
+
validateInheritanceStrategies(discovered) {
|
|
365
|
+
const checkedRoots = new Set();
|
|
366
|
+
for (const meta of discovered) {
|
|
367
|
+
if (meta.embeddable) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const root = meta.root;
|
|
371
|
+
if (checkedRoots.has(root)) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
checkedRoots.add(root);
|
|
375
|
+
const hasSTI = !!root.discriminatorColumn;
|
|
376
|
+
const hasTPT = root.inheritanceType === 'tpt' || root.inheritance === 'tpt';
|
|
377
|
+
if (hasSTI && hasTPT) {
|
|
378
|
+
throw MetadataError.mixedInheritanceStrategies(root, meta);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
423
381
|
}
|
|
424
|
-
}
|
|
425
382
|
}
|