@mikro-orm/core 7.0.15 → 7.0.16-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/EntityManager.d.ts +584 -885
  2. package/EntityManager.js +1911 -1939
  3. package/MikroORM.d.ts +74 -103
  4. package/MikroORM.js +178 -177
  5. package/README.md +1 -1
  6. package/cache/CacheAdapter.d.ts +36 -36
  7. package/cache/FileCacheAdapter.d.ts +24 -30
  8. package/cache/FileCacheAdapter.js +78 -80
  9. package/cache/GeneratedCacheAdapter.d.ts +20 -18
  10. package/cache/GeneratedCacheAdapter.js +30 -30
  11. package/cache/MemoryCacheAdapter.d.ts +20 -18
  12. package/cache/MemoryCacheAdapter.js +36 -35
  13. package/cache/NullCacheAdapter.d.ts +16 -16
  14. package/cache/NullCacheAdapter.js +24 -24
  15. package/connections/Connection.d.ts +84 -95
  16. package/connections/Connection.js +168 -165
  17. package/drivers/DatabaseDriver.d.ts +80 -186
  18. package/drivers/DatabaseDriver.js +443 -450
  19. package/drivers/IDatabaseDriver.d.ts +305 -444
  20. package/entity/BaseEntity.d.ts +83 -120
  21. package/entity/BaseEntity.js +43 -43
  22. package/entity/Collection.d.ts +181 -215
  23. package/entity/Collection.js +724 -730
  24. package/entity/EntityAssigner.d.ts +77 -88
  25. package/entity/EntityAssigner.js +230 -231
  26. package/entity/EntityFactory.d.ts +55 -67
  27. package/entity/EntityFactory.js +413 -454
  28. package/entity/EntityHelper.d.ts +23 -35
  29. package/entity/EntityHelper.js +290 -302
  30. package/entity/EntityIdentifier.d.ts +4 -4
  31. package/entity/EntityIdentifier.js +10 -10
  32. package/entity/EntityLoader.d.ts +72 -98
  33. package/entity/EntityLoader.js +786 -815
  34. package/entity/EntityRepository.d.ts +201 -316
  35. package/entity/EntityRepository.js +213 -213
  36. package/entity/PolymorphicRef.d.ts +5 -5
  37. package/entity/PolymorphicRef.js +10 -10
  38. package/entity/Reference.d.ts +83 -127
  39. package/entity/Reference.js +277 -281
  40. package/entity/WrappedEntity.d.ts +72 -115
  41. package/entity/WrappedEntity.js +166 -168
  42. package/entity/defineEntity.d.ts +659 -1412
  43. package/entity/defineEntity.js +518 -527
  44. package/entity/utils.d.ts +3 -13
  45. package/entity/utils.js +73 -71
  46. package/entity/validators.js +43 -43
  47. package/entity/wrap.js +8 -8
  48. package/enums.d.ts +253 -258
  49. package/enums.js +252 -251
  50. package/errors.d.ts +72 -114
  51. package/errors.js +253 -350
  52. package/events/EventManager.d.ts +14 -26
  53. package/events/EventManager.js +77 -79
  54. package/events/EventSubscriber.d.ts +29 -29
  55. package/events/TransactionEventBroadcaster.d.ts +8 -15
  56. package/events/TransactionEventBroadcaster.js +14 -14
  57. package/exceptions.d.ts +40 -23
  58. package/exceptions.js +52 -35
  59. package/hydration/Hydrator.d.ts +17 -42
  60. package/hydration/Hydrator.js +43 -43
  61. package/hydration/ObjectHydrator.d.ts +17 -50
  62. package/hydration/ObjectHydrator.js +418 -483
  63. package/index.d.ts +2 -118
  64. package/index.js +1 -10
  65. package/logging/DefaultLogger.d.ts +32 -34
  66. package/logging/DefaultLogger.js +86 -86
  67. package/logging/Logger.d.ts +41 -41
  68. package/logging/SimpleLogger.d.ts +11 -13
  69. package/logging/SimpleLogger.js +22 -22
  70. package/logging/colors.d.ts +6 -6
  71. package/logging/colors.js +10 -11
  72. package/logging/inspect.js +7 -7
  73. package/metadata/EntitySchema.d.ts +130 -214
  74. package/metadata/EntitySchema.js +412 -411
  75. package/metadata/MetadataDiscovery.d.ts +115 -115
  76. package/metadata/MetadataDiscovery.js +1931 -2013
  77. package/metadata/MetadataProvider.d.ts +26 -29
  78. package/metadata/MetadataProvider.js +97 -95
  79. package/metadata/MetadataStorage.d.ts +32 -38
  80. package/metadata/MetadataStorage.js +118 -118
  81. package/metadata/MetadataValidator.d.ts +39 -39
  82. package/metadata/MetadataValidator.js +340 -385
  83. package/metadata/discover-entities.d.ts +2 -5
  84. package/metadata/discover-entities.js +37 -35
  85. package/metadata/types.d.ts +531 -615
  86. package/naming-strategy/AbstractNamingStrategy.d.ts +39 -54
  87. package/naming-strategy/AbstractNamingStrategy.js +85 -90
  88. package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
  89. package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
  90. package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
  91. package/naming-strategy/MongoNamingStrategy.js +18 -18
  92. package/naming-strategy/NamingStrategy.d.ts +99 -109
  93. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  94. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  95. package/not-supported.js +4 -7
  96. package/package.json +1 -1
  97. package/platforms/ExceptionConverter.d.ts +1 -1
  98. package/platforms/ExceptionConverter.js +4 -4
  99. package/platforms/Platform.d.ts +303 -312
  100. package/platforms/Platform.js +695 -715
  101. package/serialization/EntitySerializer.d.ts +26 -49
  102. package/serialization/EntitySerializer.js +218 -224
  103. package/serialization/EntityTransformer.d.ts +6 -10
  104. package/serialization/EntityTransformer.js +217 -219
  105. package/serialization/SerializationContext.d.ts +23 -27
  106. package/serialization/SerializationContext.js +105 -105
  107. package/types/ArrayType.d.ts +8 -8
  108. package/types/ArrayType.js +33 -33
  109. package/types/BigIntType.d.ts +10 -17
  110. package/types/BigIntType.js +37 -37
  111. package/types/BlobType.d.ts +3 -3
  112. package/types/BlobType.js +13 -13
  113. package/types/BooleanType.d.ts +4 -4
  114. package/types/BooleanType.js +12 -12
  115. package/types/CharacterType.d.ts +2 -2
  116. package/types/CharacterType.js +6 -6
  117. package/types/DateTimeType.d.ts +5 -5
  118. package/types/DateTimeType.js +15 -15
  119. package/types/DateType.d.ts +5 -5
  120. package/types/DateType.js +15 -15
  121. package/types/DecimalType.d.ts +7 -7
  122. package/types/DecimalType.js +26 -26
  123. package/types/DoubleType.d.ts +3 -3
  124. package/types/DoubleType.js +12 -12
  125. package/types/EnumArrayType.d.ts +5 -5
  126. package/types/EnumArrayType.js +24 -24
  127. package/types/EnumType.d.ts +3 -3
  128. package/types/EnumType.js +11 -11
  129. package/types/FloatType.d.ts +3 -3
  130. package/types/FloatType.js +9 -9
  131. package/types/IntegerType.d.ts +3 -3
  132. package/types/IntegerType.js +9 -9
  133. package/types/IntervalType.d.ts +4 -4
  134. package/types/IntervalType.js +12 -12
  135. package/types/JsonType.d.ts +8 -8
  136. package/types/JsonType.js +32 -32
  137. package/types/MediumIntType.d.ts +1 -1
  138. package/types/MediumIntType.js +3 -3
  139. package/types/SmallIntType.d.ts +3 -3
  140. package/types/SmallIntType.js +9 -9
  141. package/types/StringType.d.ts +4 -4
  142. package/types/StringType.js +12 -12
  143. package/types/TextType.d.ts +3 -3
  144. package/types/TextType.js +9 -9
  145. package/types/TimeType.d.ts +5 -5
  146. package/types/TimeType.js +17 -17
  147. package/types/TinyIntType.d.ts +3 -3
  148. package/types/TinyIntType.js +10 -10
  149. package/types/Type.d.ts +79 -83
  150. package/types/Type.js +82 -82
  151. package/types/Uint8ArrayType.d.ts +4 -4
  152. package/types/Uint8ArrayType.js +21 -21
  153. package/types/UnknownType.d.ts +4 -4
  154. package/types/UnknownType.js +12 -12
  155. package/types/UuidType.d.ts +5 -5
  156. package/types/UuidType.js +19 -19
  157. package/types/index.d.ts +49 -75
  158. package/types/index.js +26 -52
  159. package/typings.d.ts +746 -1268
  160. package/typings.js +233 -246
  161. package/unit-of-work/ChangeSet.d.ts +26 -26
  162. package/unit-of-work/ChangeSet.js +56 -56
  163. package/unit-of-work/ChangeSetComputer.d.ts +12 -12
  164. package/unit-of-work/ChangeSetComputer.js +179 -187
  165. package/unit-of-work/ChangeSetPersister.d.ts +50 -69
  166. package/unit-of-work/ChangeSetPersister.js +446 -469
  167. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  168. package/unit-of-work/CommitOrderCalculator.js +88 -89
  169. package/unit-of-work/IdentityMap.d.ts +31 -31
  170. package/unit-of-work/IdentityMap.js +105 -105
  171. package/unit-of-work/UnitOfWork.d.ts +141 -181
  172. package/unit-of-work/UnitOfWork.js +1226 -1240
  173. package/utils/AbstractMigrator.d.ts +92 -112
  174. package/utils/AbstractMigrator.js +288 -286
  175. package/utils/AbstractSchemaGenerator.d.ts +34 -43
  176. package/utils/AbstractSchemaGenerator.js +122 -121
  177. package/utils/AsyncContext.d.ts +3 -3
  178. package/utils/AsyncContext.js +35 -34
  179. package/utils/Configuration.d.ts +808 -852
  180. package/utils/Configuration.js +344 -359
  181. package/utils/Cursor.d.ts +22 -40
  182. package/utils/Cursor.js +127 -135
  183. package/utils/DataloaderUtils.d.ts +43 -58
  184. package/utils/DataloaderUtils.js +198 -203
  185. package/utils/EntityComparator.d.ts +82 -99
  186. package/utils/EntityComparator.js +744 -836
  187. package/utils/NullHighlighter.d.ts +1 -1
  188. package/utils/NullHighlighter.js +3 -3
  189. package/utils/QueryHelper.d.ts +57 -85
  190. package/utils/QueryHelper.js +392 -404
  191. package/utils/RawQueryFragment.d.ts +34 -50
  192. package/utils/RawQueryFragment.js +121 -122
  193. package/utils/RequestContext.d.ts +32 -32
  194. package/utils/RequestContext.js +53 -52
  195. package/utils/TransactionContext.d.ts +16 -16
  196. package/utils/TransactionContext.js +27 -27
  197. package/utils/TransactionManager.d.ts +58 -58
  198. package/utils/TransactionManager.js +200 -202
  199. package/utils/Utils.d.ts +145 -204
  200. package/utils/Utils.js +826 -826
  201. package/utils/clone.js +114 -105
  202. package/utils/env-vars.js +88 -90
  203. package/utils/fs-utils.d.ts +15 -15
  204. package/utils/fs-utils.js +182 -181
  205. package/utils/upsert-utils.d.ts +5 -20
  206. package/utils/upsert-utils.js +116 -114
@@ -17,413 +17,368 @@ const DANGEROUS_PROPERTY_NAMES = ['__proto__', 'constructor', 'prototype'];
17
17
  * @internal
18
18
  */
19
19
  export class MetadataValidator {
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;
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
- if (prop.primary) {
42
- throw new MetadataError(
43
- `Virtual entity ${meta.className} cannot have primary key ${meta.className}.${prop.name}`,
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
- * Checks if a property has a unique constraint (either via `unique: true` or single-property `@Unique` decorator).
184
- * Composite unique constraints are not sufficient for targetKey.
185
- */
186
- isPropertyUnique(prop, meta) {
187
- if (prop.unique || (prop.primary && meta.primaryKeys.length === 1)) {
188
- return true;
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
- // Check for single-property unique constraint via @Unique decorator
191
- return !!meta.uniques?.some(u => {
192
- const props = Utils.asArray(u.properties);
193
- return props.length === 1 && props[0] === prop.name && !u.options;
194
- });
195
- }
196
- validatePolymorphicTargets(meta, prop) {
197
- const targets = prop.polymorphTargets;
198
- // Validate targetKey exists and is compatible across all targets
199
- if (prop.targetKey) {
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
- throw MetadataError.targetKeyNotFound(meta, prop, target);
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, target)) {
207
- throw MetadataError.targetKeyNotUnique(meta, prop, target);
154
+ if (!this.isPropertyUnique(targetProp, targetMeta)) {
155
+ throw MetadataError.targetKeyNotUnique(meta, prop);
208
156
  }
209
- }
210
157
  }
211
- const firstPKs = targets[0].getPrimaryProps();
212
- for (let i = 1; i < targets.length; i++) {
213
- const target = targets[i];
214
- const targetPKs = target.getPrimaryProps();
215
- if (targetPKs.length !== firstPKs.length) {
216
- throw MetadataError.incompatiblePolymorphicTargets(
217
- meta,
218
- prop,
219
- targets[0],
220
- target,
221
- 'different number of primary keys',
222
- );
223
- }
224
- for (let j = 0; j < firstPKs.length; j++) {
225
- const firstPK = firstPKs[j];
226
- const targetPK = targetPKs[j];
227
- if (firstPK.runtimeType !== targetPK.runtimeType) {
228
- throw MetadataError.incompatiblePolymorphicTargets(
229
- meta,
230
- prop,
231
- targets[0],
232
- target,
233
- `incompatible primary key types: ${firstPK.name} (${firstPK.runtimeType}) vs ${targetPK.name} (${targetPK.runtimeType})`,
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 || (prop.primary && meta.primaryKeys.length === 1)) {
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
- validateBidirectional(meta, prop) {
240
- if (prop.inversedBy) {
241
- this.validateOwningSide(meta, prop);
242
- } else if (prop.mappedBy) {
243
- this.validateInverseSide(meta, prop);
244
- } else if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
245
- // 1:m property has `mappedBy`
246
- throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
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
- validateOwningSide(meta, prop) {
250
- // For polymorphic relations, inversedBy may point to multiple entity types
251
- if (prop.polymorphic && prop.polymorphTargets?.length) {
252
- // For polymorphic relations, validate inversedBy against each target
253
- // The inverse property should exist on the target entities and reference back to this property
254
- for (const targetMeta of prop.polymorphTargets) {
255
- const inverse = targetMeta.properties[prop.inversedBy];
256
- // The inverse property is optional - some targets may not have it
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
- continue;
240
+ throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
259
241
  }
260
- // Validate the inverse property
261
- if (inverse.targetMeta?.root.class !== meta.root.class) {
262
- throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
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
- throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
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.properties[propName] ?? meta.root.properties[propName];
324
- if (
325
- !prop &&
326
- !Object.values(meta.properties).some(p => propName.startsWith(p.name + '.')) &&
327
- !Object.values(meta.root.properties).some(p => propName.startsWith(p.name + '.'))
328
- ) {
329
- throw MetadataError.unknownIndexProperty(meta, propName, type);
330
- }
331
- }
332
- }
333
- }
334
- validateDuplicateFieldNames(meta, options) {
335
- const candidates = Object.values(meta.properties)
336
- .filter(
337
- prop =>
338
- prop.persist !== false &&
339
- !prop.inherited &&
340
- prop.fieldNames?.length === 1 &&
341
- (prop.kind !== ReferenceKind.EMBEDDED || prop.object),
342
- )
343
- .map(prop => prop.fieldNames[0]);
344
- const duplicates = Utils.findDuplicates(candidates);
345
- if (duplicates.length > 0 && options.checkDuplicateFieldNames) {
346
- const pairs = duplicates.flatMap(name => {
347
- return Object.values(meta.properties)
348
- .filter(p => p.fieldNames?.[0] === name)
349
- .map(prop => {
350
- return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
351
- });
352
- });
353
- throw MetadataError.duplicateFieldName(meta.class, pairs);
354
251
  }
355
- }
356
- validateVersionField(meta) {
357
- if (!meta.versionProperty) {
358
- return;
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
+ }
359
282
  }
360
- const props = Object.values(meta.properties).filter(p => p.version);
361
- if (props.length > 1) {
362
- throw MetadataError.multipleVersionFields(
363
- meta,
364
- props.map(p => p.name),
365
- );
283
+ validateIndexes(meta, indexes, type) {
284
+ for (const index of indexes) {
285
+ for (const propName of Utils.asArray(index.properties)) {
286
+ const prop = meta.properties[propName] ?? meta.root.properties[propName];
287
+ if (!prop &&
288
+ !Object.values(meta.properties).some(p => propName.startsWith(p.name + '.')) &&
289
+ !Object.values(meta.root.properties).some(p => propName.startsWith(p.name + '.'))) {
290
+ throw MetadataError.unknownIndexProperty(meta, propName, type);
291
+ }
292
+ }
293
+ }
366
294
  }
367
- const prop = meta.properties[meta.versionProperty];
368
- const type = prop.runtimeType ?? prop.columnTypes?.[0] ?? prop.type;
369
- if (type !== 'number' && type !== 'Date' && !type.startsWith('timestamp') && !type.startsWith('datetime')) {
370
- throw MetadataError.invalidVersionFieldType(meta);
295
+ validateDuplicateFieldNames(meta, options) {
296
+ const candidates = Object.values(meta.properties)
297
+ .filter(prop => prop.persist !== false &&
298
+ !prop.inherited &&
299
+ prop.fieldNames?.length === 1 &&
300
+ (prop.kind !== ReferenceKind.EMBEDDED || prop.object))
301
+ .map(prop => prop.fieldNames[0]);
302
+ const duplicates = Utils.findDuplicates(candidates);
303
+ if (duplicates.length > 0 && options.checkDuplicateFieldNames) {
304
+ const pairs = duplicates.flatMap(name => {
305
+ return Object.values(meta.properties)
306
+ .filter(p => p.fieldNames?.[0] === name)
307
+ .map(prop => {
308
+ return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
309
+ });
310
+ });
311
+ throw MetadataError.duplicateFieldName(meta.class, pairs);
312
+ }
371
313
  }
372
- }
373
- /**
374
- * Validates that entity properties do not use dangerous names that could lead to
375
- * prototype pollution vulnerabilities. This validation ensures that property names
376
- * cannot be exploited to modify object prototypes when values are assigned during
377
- * entity hydration or persistence operations.
378
- *
379
- * @internal
380
- */
381
- validatePropertyNames(meta) {
382
- for (const prop of Utils.values(meta.properties)) {
383
- if (DANGEROUS_PROPERTY_NAMES.includes(prop.name)) {
384
- throw MetadataError.dangerousPropertyName(meta, prop);
385
- }
314
+ validateVersionField(meta) {
315
+ if (!meta.versionProperty) {
316
+ return;
317
+ }
318
+ const props = Object.values(meta.properties).filter(p => p.version);
319
+ if (props.length > 1) {
320
+ throw MetadataError.multipleVersionFields(meta, props.map(p => p.name));
321
+ }
322
+ const prop = meta.properties[meta.versionProperty];
323
+ const type = prop.runtimeType ?? prop.columnTypes?.[0] ?? prop.type;
324
+ if (type !== 'number' && type !== 'Date' && !type.startsWith('timestamp') && !type.startsWith('datetime')) {
325
+ throw MetadataError.invalidVersionFieldType(meta);
326
+ }
386
327
  }
387
- }
388
- /**
389
- * Validates view entity configuration.
390
- * View entities must have an expression.
391
- */
392
- validateViewEntity(meta) {
393
- // View entities must have an expression
394
- if (!meta.expression) {
395
- throw MetadataError.viewEntityWithoutExpression(meta);
328
+ /**
329
+ * Validates that entity properties do not use dangerous names that could lead to
330
+ * prototype pollution vulnerabilities. This validation ensures that property names
331
+ * cannot be exploited to modify object prototypes when values are assigned during
332
+ * entity hydration or persistence operations.
333
+ *
334
+ * @internal
335
+ */
336
+ validatePropertyNames(meta) {
337
+ for (const prop of Utils.values(meta.properties)) {
338
+ if (DANGEROUS_PROPERTY_NAMES.includes(prop.name)) {
339
+ throw MetadataError.dangerousPropertyName(meta, prop);
340
+ }
341
+ }
396
342
  }
397
- // Validate indexes if present
398
- this.validateIndexes(meta, meta.indexes ?? [], 'index');
399
- this.validateIndexes(meta, meta.uniques ?? [], 'unique');
400
- // Validate property names
401
- this.validatePropertyNames(meta);
402
- }
403
- /**
404
- * Validates that STI and TPT are not mixed in the same inheritance hierarchy.
405
- * An entity hierarchy can use either STI (discriminatorColumn) or TPT (inheritance: 'tpt'),
406
- * but not both.
407
- *
408
- * Note: This validation runs before `initTablePerTypeInheritance` sets `inheritanceType`,
409
- * so we check the raw `inheritance` option from the decorator/schema.
410
- */
411
- validateInheritanceStrategies(discovered) {
412
- const checkedRoots = new Set();
413
- for (const meta of discovered) {
414
- if (meta.embeddable) {
415
- continue;
416
- }
417
- const root = meta.root;
418
- if (checkedRoots.has(root)) {
419
- continue;
420
- }
421
- checkedRoots.add(root);
422
- const hasSTI = !!root.discriminatorColumn;
423
- const hasTPT = root.inheritanceType === 'tpt' || root.inheritance === 'tpt';
424
- if (hasSTI && hasTPT) {
425
- throw MetadataError.mixedInheritanceStrategies(root, meta);
426
- }
343
+ /**
344
+ * Validates view entity configuration.
345
+ * View entities must have an expression.
346
+ */
347
+ validateViewEntity(meta) {
348
+ // View entities must have an expression
349
+ if (!meta.expression) {
350
+ throw MetadataError.viewEntityWithoutExpression(meta);
351
+ }
352
+ // Validate indexes if present
353
+ this.validateIndexes(meta, meta.indexes ?? [], 'index');
354
+ this.validateIndexes(meta, meta.uniques ?? [], 'unique');
355
+ // Validate property names
356
+ this.validatePropertyNames(meta);
357
+ }
358
+ /**
359
+ * Validates that STI and TPT are not mixed in the same inheritance hierarchy.
360
+ * An entity hierarchy can use either STI (discriminatorColumn) or TPT (inheritance: 'tpt'),
361
+ * but not both.
362
+ *
363
+ * Note: This validation runs before `initTablePerTypeInheritance` sets `inheritanceType`,
364
+ * so we check the raw `inheritance` option from the decorator/schema.
365
+ */
366
+ validateInheritanceStrategies(discovered) {
367
+ const checkedRoots = new Set();
368
+ for (const meta of discovered) {
369
+ if (meta.embeddable) {
370
+ continue;
371
+ }
372
+ const root = meta.root;
373
+ if (checkedRoots.has(root)) {
374
+ continue;
375
+ }
376
+ checkedRoots.add(root);
377
+ const hasSTI = !!root.discriminatorColumn;
378
+ const hasTPT = root.inheritanceType === 'tpt' || root.inheritance === 'tpt';
379
+ if (hasSTI && hasTPT) {
380
+ throw MetadataError.mixedInheritanceStrategies(root, meta);
381
+ }
382
+ }
427
383
  }
428
- }
429
384
  }