@mikro-orm/core 7.0.4 → 7.0.5-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/EntityManager.d.ts +583 -884
  2. package/EntityManager.js +1895 -1922
  3. package/MikroORM.d.ts +74 -103
  4. package/MikroORM.js +179 -178
  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 +301 -440
  20. package/entity/BaseEntity.d.ts +83 -120
  21. package/entity/BaseEntity.js +43 -43
  22. package/entity/Collection.d.ts +179 -212
  23. package/entity/Collection.js +721 -727
  24. package/entity/EntityAssigner.d.ts +77 -88
  25. package/entity/EntityAssigner.js +230 -231
  26. package/entity/EntityFactory.d.ts +54 -66
  27. package/entity/EntityFactory.js +383 -425
  28. package/entity/EntityHelper.d.ts +22 -34
  29. package/entity/EntityHelper.js +267 -280
  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 +723 -753
  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 +82 -126
  39. package/entity/Reference.js +274 -278
  40. package/entity/WrappedEntity.d.ts +72 -115
  41. package/entity/WrappedEntity.js +166 -168
  42. package/entity/defineEntity.d.ts +636 -1315
  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 +416 -481
  63. package/index.d.ts +2 -116
  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 +127 -211
  74. package/metadata/EntitySchema.js +398 -397
  75. package/metadata/MetadataDiscovery.d.ts +114 -114
  76. package/metadata/MetadataDiscovery.js +1870 -1951
  77. package/metadata/MetadataProvider.d.ts +21 -24
  78. package/metadata/MetadataProvider.js +84 -82
  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 +338 -381
  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 +301 -310
  100. package/platforms/Platform.js +640 -663
  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 +737 -1250
  160. package/typings.js +231 -244
  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 +170 -178
  165. package/unit-of-work/ChangeSetPersister.d.ts +44 -63
  166. package/unit-of-work/ChangeSetPersister.js +421 -442
  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 +1183 -1200
  173. package/utils/AbstractMigrator.d.ts +91 -111
  174. package/utils/AbstractMigrator.js +275 -275
  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 +81 -98
  186. package/utils/EntityComparator.js +732 -828
  187. package/utils/NullHighlighter.d.ts +1 -1
  188. package/utils/NullHighlighter.js +3 -3
  189. package/utils/QueryHelper.d.ts +51 -79
  190. package/utils/QueryHelper.js +361 -372
  191. package/utils/RawQueryFragment.d.ts +34 -50
  192. package/utils/RawQueryFragment.js +105 -107
  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 +197 -199
  199. package/utils/Utils.d.ts +145 -204
  200. package/utils/Utils.js +812 -812
  201. package/utils/clone.js +113 -104
  202. package/utils/env-vars.js +88 -90
  203. package/utils/fs-utils.d.ts +15 -15
  204. package/utils/fs-utils.js +181 -180
  205. package/utils/upsert-utils.d.ts +5 -20
  206. 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
- 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) {
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) {
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.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
- validateVersionField(meta) {
353
- if (!meta.versionProperty) {
354
- 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
+ }
355
282
  }
356
- const props = Object.values(meta.properties).filter(p => p.version);
357
- if (props.length > 1) {
358
- throw MetadataError.multipleVersionFields(
359
- meta,
360
- props.map(p => p.name),
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
- const prop = meta.properties[meta.versionProperty];
364
- const type = prop.runtimeType ?? prop.columnTypes?.[0] ?? prop.type;
365
- if (type !== 'number' && type !== 'Date' && !type.startsWith('timestamp') && !type.startsWith('datetime')) {
366
- throw MetadataError.invalidVersionFieldType(meta);
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
- * Validates that entity properties do not use dangerous names that could lead to
371
- * prototype pollution vulnerabilities. This validation ensures that property names
372
- * cannot be exploited to modify object prototypes when values are assigned during
373
- * entity hydration or persistence operations.
374
- *
375
- * @internal
376
- */
377
- validatePropertyNames(meta) {
378
- for (const prop of Utils.values(meta.properties)) {
379
- if (DANGEROUS_PROPERTY_NAMES.includes(prop.name)) {
380
- throw MetadataError.dangerousPropertyName(meta, prop);
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
- * Validates view entity configuration.
386
- * View entities must have an expression.
387
- */
388
- validateViewEntity(meta) {
389
- // View entities must have an expression
390
- if (!meta.expression) {
391
- throw MetadataError.viewEntityWithoutExpression(meta);
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
- // Validate indexes if present
394
- this.validateIndexes(meta, meta.indexes ?? [], 'index');
395
- this.validateIndexes(meta, meta.uniques ?? [], 'unique');
396
- // Validate property names
397
- this.validatePropertyNames(meta);
398
- }
399
- /**
400
- * Validates that STI and TPT are not mixed in the same inheritance hierarchy.
401
- * An entity hierarchy can use either STI (discriminatorColumn) or TPT (inheritance: 'tpt'),
402
- * but not both.
403
- *
404
- * Note: This validation runs before `initTablePerTypeInheritance` sets `inheritanceType`,
405
- * so we check the raw `inheritance` option from the decorator/schema.
406
- */
407
- validateInheritanceStrategies(discovered) {
408
- const checkedRoots = new Set();
409
- for (const meta of discovered) {
410
- if (meta.embeddable) {
411
- continue;
412
- }
413
- const root = meta.root;
414
- if (checkedRoots.has(root)) {
415
- continue;
416
- }
417
- checkedRoots.add(root);
418
- const hasSTI = !!root.discriminatorColumn;
419
- const hasTPT = root.inheritanceType === 'tpt' || root.inheritance === 'tpt';
420
- if (hasSTI && hasTPT) {
421
- throw MetadataError.mixedInheritanceStrategies(root, meta);
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
  }