@mikro-orm/core 7.0.2 → 7.0.3-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 (205) hide show
  1. package/EntityManager.d.ts +583 -883
  2. package/EntityManager.js +1869 -1897
  3. package/MikroORM.d.ts +74 -103
  4. package/MikroORM.js +179 -178
  5. package/cache/CacheAdapter.d.ts +36 -36
  6. package/cache/FileCacheAdapter.d.ts +24 -30
  7. package/cache/FileCacheAdapter.js +78 -80
  8. package/cache/GeneratedCacheAdapter.d.ts +20 -18
  9. package/cache/GeneratedCacheAdapter.js +30 -30
  10. package/cache/MemoryCacheAdapter.d.ts +20 -18
  11. package/cache/MemoryCacheAdapter.js +36 -35
  12. package/cache/NullCacheAdapter.d.ts +16 -16
  13. package/cache/NullCacheAdapter.js +24 -24
  14. package/connections/Connection.d.ts +84 -95
  15. package/connections/Connection.js +168 -165
  16. package/drivers/DatabaseDriver.d.ts +80 -186
  17. package/drivers/DatabaseDriver.js +443 -450
  18. package/drivers/IDatabaseDriver.d.ts +301 -440
  19. package/entity/BaseEntity.d.ts +83 -120
  20. package/entity/BaseEntity.js +43 -43
  21. package/entity/Collection.d.ts +179 -212
  22. package/entity/Collection.js +721 -727
  23. package/entity/EntityAssigner.d.ts +77 -88
  24. package/entity/EntityAssigner.js +230 -231
  25. package/entity/EntityFactory.d.ts +54 -66
  26. package/entity/EntityFactory.js +383 -425
  27. package/entity/EntityHelper.d.ts +22 -34
  28. package/entity/EntityHelper.js +267 -280
  29. package/entity/EntityIdentifier.d.ts +4 -4
  30. package/entity/EntityIdentifier.js +10 -10
  31. package/entity/EntityLoader.d.ts +73 -103
  32. package/entity/EntityLoader.js +723 -753
  33. package/entity/EntityRepository.d.ts +201 -316
  34. package/entity/EntityRepository.js +213 -213
  35. package/entity/PolymorphicRef.d.ts +5 -5
  36. package/entity/PolymorphicRef.js +10 -10
  37. package/entity/Reference.d.ts +82 -126
  38. package/entity/Reference.js +274 -278
  39. package/entity/WrappedEntity.d.ts +72 -115
  40. package/entity/WrappedEntity.js +166 -168
  41. package/entity/defineEntity.d.ts +614 -1280
  42. package/entity/defineEntity.js +511 -520
  43. package/entity/utils.d.ts +3 -13
  44. package/entity/utils.js +73 -71
  45. package/entity/validators.js +43 -43
  46. package/entity/wrap.js +8 -8
  47. package/enums.d.ts +253 -258
  48. package/enums.js +252 -251
  49. package/errors.d.ts +72 -114
  50. package/errors.js +253 -350
  51. package/events/EventManager.d.ts +14 -26
  52. package/events/EventManager.js +77 -79
  53. package/events/EventSubscriber.d.ts +29 -29
  54. package/events/TransactionEventBroadcaster.d.ts +8 -15
  55. package/events/TransactionEventBroadcaster.js +14 -14
  56. package/exceptions.d.ts +40 -23
  57. package/exceptions.js +52 -35
  58. package/hydration/Hydrator.d.ts +17 -42
  59. package/hydration/Hydrator.js +43 -43
  60. package/hydration/ObjectHydrator.d.ts +17 -50
  61. package/hydration/ObjectHydrator.js +416 -479
  62. package/index.d.ts +2 -116
  63. package/index.js +1 -10
  64. package/logging/DefaultLogger.d.ts +32 -34
  65. package/logging/DefaultLogger.js +86 -86
  66. package/logging/Logger.d.ts +41 -41
  67. package/logging/SimpleLogger.d.ts +11 -13
  68. package/logging/SimpleLogger.js +22 -22
  69. package/logging/colors.d.ts +6 -6
  70. package/logging/colors.js +10 -11
  71. package/logging/inspect.js +7 -7
  72. package/metadata/EntitySchema.d.ts +127 -211
  73. package/metadata/EntitySchema.js +398 -397
  74. package/metadata/MetadataDiscovery.d.ts +114 -114
  75. package/metadata/MetadataDiscovery.js +1863 -1947
  76. package/metadata/MetadataProvider.d.ts +21 -24
  77. package/metadata/MetadataProvider.js +84 -82
  78. package/metadata/MetadataStorage.d.ts +32 -38
  79. package/metadata/MetadataStorage.js +118 -118
  80. package/metadata/MetadataValidator.d.ts +39 -39
  81. package/metadata/MetadataValidator.js +338 -381
  82. package/metadata/discover-entities.d.ts +2 -5
  83. package/metadata/discover-entities.js +27 -27
  84. package/metadata/types.d.ts +531 -615
  85. package/naming-strategy/AbstractNamingStrategy.d.ts +39 -54
  86. package/naming-strategy/AbstractNamingStrategy.js +85 -90
  87. package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
  88. package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
  89. package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
  90. package/naming-strategy/MongoNamingStrategy.js +18 -18
  91. package/naming-strategy/NamingStrategy.d.ts +99 -109
  92. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  93. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  94. package/not-supported.js +4 -7
  95. package/package.json +1 -1
  96. package/platforms/ExceptionConverter.d.ts +1 -1
  97. package/platforms/ExceptionConverter.js +4 -4
  98. package/platforms/Platform.d.ts +299 -308
  99. package/platforms/Platform.js +636 -659
  100. package/serialization/EntitySerializer.d.ts +26 -48
  101. package/serialization/EntitySerializer.js +218 -224
  102. package/serialization/EntityTransformer.d.ts +6 -10
  103. package/serialization/EntityTransformer.js +217 -219
  104. package/serialization/SerializationContext.d.ts +23 -27
  105. package/serialization/SerializationContext.js +105 -105
  106. package/types/ArrayType.d.ts +8 -8
  107. package/types/ArrayType.js +33 -33
  108. package/types/BigIntType.d.ts +10 -17
  109. package/types/BigIntType.js +37 -37
  110. package/types/BlobType.d.ts +3 -3
  111. package/types/BlobType.js +13 -13
  112. package/types/BooleanType.d.ts +4 -4
  113. package/types/BooleanType.js +12 -12
  114. package/types/CharacterType.d.ts +2 -2
  115. package/types/CharacterType.js +6 -6
  116. package/types/DateTimeType.d.ts +5 -5
  117. package/types/DateTimeType.js +15 -15
  118. package/types/DateType.d.ts +5 -5
  119. package/types/DateType.js +15 -15
  120. package/types/DecimalType.d.ts +7 -7
  121. package/types/DecimalType.js +26 -26
  122. package/types/DoubleType.d.ts +3 -3
  123. package/types/DoubleType.js +12 -12
  124. package/types/EnumArrayType.d.ts +5 -5
  125. package/types/EnumArrayType.js +24 -24
  126. package/types/EnumType.d.ts +3 -3
  127. package/types/EnumType.js +11 -11
  128. package/types/FloatType.d.ts +3 -3
  129. package/types/FloatType.js +9 -9
  130. package/types/IntegerType.d.ts +3 -3
  131. package/types/IntegerType.js +9 -9
  132. package/types/IntervalType.d.ts +4 -4
  133. package/types/IntervalType.js +12 -12
  134. package/types/JsonType.d.ts +8 -8
  135. package/types/JsonType.js +32 -32
  136. package/types/MediumIntType.d.ts +1 -1
  137. package/types/MediumIntType.js +3 -3
  138. package/types/SmallIntType.d.ts +3 -3
  139. package/types/SmallIntType.js +9 -9
  140. package/types/StringType.d.ts +4 -4
  141. package/types/StringType.js +12 -12
  142. package/types/TextType.d.ts +3 -3
  143. package/types/TextType.js +9 -9
  144. package/types/TimeType.d.ts +5 -5
  145. package/types/TimeType.js +17 -17
  146. package/types/TinyIntType.d.ts +3 -3
  147. package/types/TinyIntType.js +10 -10
  148. package/types/Type.d.ts +79 -83
  149. package/types/Type.js +82 -82
  150. package/types/Uint8ArrayType.d.ts +4 -4
  151. package/types/Uint8ArrayType.js +21 -21
  152. package/types/UnknownType.d.ts +4 -4
  153. package/types/UnknownType.js +12 -12
  154. package/types/UuidType.d.ts +5 -5
  155. package/types/UuidType.js +19 -19
  156. package/types/index.d.ts +49 -75
  157. package/types/index.js +26 -52
  158. package/typings.d.ts +729 -1211
  159. package/typings.js +231 -244
  160. package/unit-of-work/ChangeSet.d.ts +26 -26
  161. package/unit-of-work/ChangeSet.js +56 -56
  162. package/unit-of-work/ChangeSetComputer.d.ts +12 -12
  163. package/unit-of-work/ChangeSetComputer.js +170 -178
  164. package/unit-of-work/ChangeSetPersister.d.ts +44 -63
  165. package/unit-of-work/ChangeSetPersister.js +421 -442
  166. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  167. package/unit-of-work/CommitOrderCalculator.js +88 -89
  168. package/unit-of-work/IdentityMap.d.ts +31 -31
  169. package/unit-of-work/IdentityMap.js +105 -105
  170. package/unit-of-work/UnitOfWork.d.ts +141 -181
  171. package/unit-of-work/UnitOfWork.js +1183 -1200
  172. package/utils/AbstractMigrator.d.ts +91 -111
  173. package/utils/AbstractMigrator.js +275 -275
  174. package/utils/AbstractSchemaGenerator.d.ts +34 -43
  175. package/utils/AbstractSchemaGenerator.js +122 -121
  176. package/utils/AsyncContext.d.ts +3 -3
  177. package/utils/AsyncContext.js +35 -34
  178. package/utils/Configuration.d.ts +808 -852
  179. package/utils/Configuration.js +344 -359
  180. package/utils/Cursor.d.ts +22 -40
  181. package/utils/Cursor.js +127 -135
  182. package/utils/DataloaderUtils.d.ts +43 -58
  183. package/utils/DataloaderUtils.js +198 -203
  184. package/utils/EntityComparator.d.ts +81 -98
  185. package/utils/EntityComparator.js +728 -824
  186. package/utils/NullHighlighter.d.ts +1 -1
  187. package/utils/NullHighlighter.js +3 -3
  188. package/utils/QueryHelper.d.ts +51 -79
  189. package/utils/QueryHelper.js +361 -372
  190. package/utils/RawQueryFragment.d.ts +34 -50
  191. package/utils/RawQueryFragment.js +105 -107
  192. package/utils/RequestContext.d.ts +32 -32
  193. package/utils/RequestContext.js +53 -52
  194. package/utils/TransactionContext.d.ts +16 -16
  195. package/utils/TransactionContext.js +27 -27
  196. package/utils/TransactionManager.d.ts +58 -58
  197. package/utils/TransactionManager.js +197 -199
  198. package/utils/Utils.d.ts +145 -204
  199. package/utils/Utils.js +813 -814
  200. package/utils/clone.js +113 -104
  201. package/utils/env-vars.js +88 -90
  202. package/utils/fs-utils.d.ts +15 -15
  203. package/utils/fs-utils.js +181 -180
  204. package/utils/upsert-utils.d.ts +5 -20
  205. 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
  }