@mikro-orm/core 7.0.4-dev.8 → 7.0.4

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 +884 -583
  2. package/EntityManager.js +1922 -1895
  3. package/MikroORM.d.ts +103 -74
  4. package/MikroORM.js +178 -179
  5. package/README.md +1 -1
  6. package/cache/CacheAdapter.d.ts +36 -36
  7. package/cache/FileCacheAdapter.d.ts +30 -24
  8. package/cache/FileCacheAdapter.js +80 -78
  9. package/cache/GeneratedCacheAdapter.d.ts +18 -20
  10. package/cache/GeneratedCacheAdapter.js +30 -30
  11. package/cache/MemoryCacheAdapter.d.ts +18 -20
  12. package/cache/MemoryCacheAdapter.js +35 -36
  13. package/cache/NullCacheAdapter.d.ts +16 -16
  14. package/cache/NullCacheAdapter.js +24 -24
  15. package/connections/Connection.d.ts +95 -84
  16. package/connections/Connection.js +165 -168
  17. package/drivers/DatabaseDriver.d.ts +186 -80
  18. package/drivers/DatabaseDriver.js +450 -443
  19. package/drivers/IDatabaseDriver.d.ts +440 -301
  20. package/entity/BaseEntity.d.ts +120 -83
  21. package/entity/BaseEntity.js +43 -43
  22. package/entity/Collection.d.ts +212 -179
  23. package/entity/Collection.js +727 -721
  24. package/entity/EntityAssigner.d.ts +88 -77
  25. package/entity/EntityAssigner.js +231 -230
  26. package/entity/EntityFactory.d.ts +66 -54
  27. package/entity/EntityFactory.js +425 -383
  28. package/entity/EntityHelper.d.ts +34 -22
  29. package/entity/EntityHelper.js +280 -267
  30. package/entity/EntityIdentifier.d.ts +4 -4
  31. package/entity/EntityIdentifier.js +10 -10
  32. package/entity/EntityLoader.d.ts +98 -72
  33. package/entity/EntityLoader.js +753 -723
  34. package/entity/EntityRepository.d.ts +316 -201
  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 +126 -82
  39. package/entity/Reference.js +278 -274
  40. package/entity/WrappedEntity.d.ts +115 -72
  41. package/entity/WrappedEntity.js +168 -166
  42. package/entity/defineEntity.d.ts +1315 -636
  43. package/entity/defineEntity.js +527 -518
  44. package/entity/utils.d.ts +13 -3
  45. package/entity/utils.js +71 -73
  46. package/entity/validators.js +43 -43
  47. package/entity/wrap.js +8 -8
  48. package/enums.d.ts +258 -253
  49. package/enums.js +251 -252
  50. package/errors.d.ts +114 -72
  51. package/errors.js +350 -253
  52. package/events/EventManager.d.ts +26 -14
  53. package/events/EventManager.js +79 -77
  54. package/events/EventSubscriber.d.ts +29 -29
  55. package/events/TransactionEventBroadcaster.d.ts +15 -8
  56. package/events/TransactionEventBroadcaster.js +14 -14
  57. package/exceptions.d.ts +23 -40
  58. package/exceptions.js +35 -52
  59. package/hydration/Hydrator.d.ts +42 -17
  60. package/hydration/Hydrator.js +43 -43
  61. package/hydration/ObjectHydrator.d.ts +50 -17
  62. package/hydration/ObjectHydrator.js +481 -416
  63. package/index.d.ts +116 -2
  64. package/index.js +10 -1
  65. package/logging/DefaultLogger.d.ts +34 -32
  66. package/logging/DefaultLogger.js +86 -86
  67. package/logging/Logger.d.ts +41 -41
  68. package/logging/SimpleLogger.d.ts +13 -11
  69. package/logging/SimpleLogger.js +22 -22
  70. package/logging/colors.d.ts +6 -6
  71. package/logging/colors.js +11 -10
  72. package/logging/inspect.js +7 -7
  73. package/metadata/EntitySchema.d.ts +211 -127
  74. package/metadata/EntitySchema.js +397 -398
  75. package/metadata/MetadataDiscovery.d.ts +114 -114
  76. package/metadata/MetadataDiscovery.js +1951 -1863
  77. package/metadata/MetadataProvider.d.ts +24 -21
  78. package/metadata/MetadataProvider.js +82 -84
  79. package/metadata/MetadataStorage.d.ts +38 -32
  80. package/metadata/MetadataStorage.js +118 -118
  81. package/metadata/MetadataValidator.d.ts +39 -39
  82. package/metadata/MetadataValidator.js +381 -338
  83. package/metadata/discover-entities.d.ts +5 -2
  84. package/metadata/discover-entities.js +35 -27
  85. package/metadata/types.d.ts +615 -531
  86. package/naming-strategy/AbstractNamingStrategy.d.ts +54 -39
  87. package/naming-strategy/AbstractNamingStrategy.js +90 -85
  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 +109 -99
  93. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  94. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  95. package/not-supported.js +7 -4
  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 +310 -299
  100. package/platforms/Platform.js +663 -636
  101. package/serialization/EntitySerializer.d.ts +49 -26
  102. package/serialization/EntitySerializer.js +224 -218
  103. package/serialization/EntityTransformer.d.ts +10 -6
  104. package/serialization/EntityTransformer.js +219 -217
  105. package/serialization/SerializationContext.d.ts +27 -23
  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 +17 -10
  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 +83 -79
  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 +75 -49
  158. package/types/index.js +52 -26
  159. package/typings.d.ts +1250 -737
  160. package/typings.js +244 -231
  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 +178 -170
  165. package/unit-of-work/ChangeSetPersister.d.ts +63 -44
  166. package/unit-of-work/ChangeSetPersister.js +442 -421
  167. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  168. package/unit-of-work/CommitOrderCalculator.js +89 -88
  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 +181 -141
  172. package/unit-of-work/UnitOfWork.js +1200 -1183
  173. package/utils/AbstractMigrator.d.ts +111 -91
  174. package/utils/AbstractMigrator.js +275 -275
  175. package/utils/AbstractSchemaGenerator.d.ts +43 -34
  176. package/utils/AbstractSchemaGenerator.js +121 -122
  177. package/utils/AsyncContext.d.ts +3 -3
  178. package/utils/AsyncContext.js +34 -35
  179. package/utils/Configuration.d.ts +852 -808
  180. package/utils/Configuration.js +359 -344
  181. package/utils/Cursor.d.ts +40 -22
  182. package/utils/Cursor.js +135 -127
  183. package/utils/DataloaderUtils.d.ts +58 -43
  184. package/utils/DataloaderUtils.js +203 -198
  185. package/utils/EntityComparator.d.ts +98 -81
  186. package/utils/EntityComparator.js +828 -728
  187. package/utils/NullHighlighter.d.ts +1 -1
  188. package/utils/NullHighlighter.js +3 -3
  189. package/utils/QueryHelper.d.ts +79 -51
  190. package/utils/QueryHelper.js +372 -361
  191. package/utils/RawQueryFragment.d.ts +50 -34
  192. package/utils/RawQueryFragment.js +107 -105
  193. package/utils/RequestContext.d.ts +32 -32
  194. package/utils/RequestContext.js +52 -53
  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 +199 -197
  199. package/utils/Utils.d.ts +204 -145
  200. package/utils/Utils.js +812 -810
  201. package/utils/clone.js +104 -113
  202. package/utils/env-vars.js +90 -88
  203. package/utils/fs-utils.d.ts +15 -15
  204. package/utils/fs-utils.js +180 -181
  205. package/utils/upsert-utils.d.ts +20 -5
  206. package/utils/upsert-utils.js +114 -116
@@ -17,366 +17,409 @@ 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 (![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
- }
58
- }
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;
59
27
  }
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;
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
+ );
123
40
  }
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);
41
+ if (prop.primary) {
42
+ throw new MetadataError(
43
+ `Virtual entity ${meta.className} cannot have primary key ${meta.className}.${prop.name}`,
44
+ );
137
45
  }
138
- this.validateTargetKey(meta, prop, targetMeta);
46
+ }
47
+ return;
139
48
  }
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];
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
+ }
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;
189
+ }
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];
150
202
  if (!targetProp) {
151
- throw MetadataError.targetKeyNotFound(meta, prop);
203
+ throw MetadataError.targetKeyNotFound(meta, prop, target);
152
204
  }
153
205
  // targetKey must point to a unique property (composite unique is not sufficient)
154
- if (!this.isPropertyUnique(targetProp, targetMeta)) {
155
- throw MetadataError.targetKeyNotUnique(meta, prop);
206
+ if (!this.isPropertyUnique(targetProp, target)) {
207
+ throw MetadataError.targetKeyNotUnique(meta, prop, target);
156
208
  }
209
+ }
157
210
  }
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
- }
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
+ }
202
237
  }
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
- }
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');
214
247
  }
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
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
239
257
  if (!inverse) {
240
- throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
258
+ continue;
241
259
  }
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);
260
+ // Validate the inverse property
261
+ if (inverse.targetMeta?.root.class !== meta.root.class) {
262
+ throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
246
263
  }
247
264
  // inverse side is not defined as owner
248
265
  if (inverse.inversedBy || inverse.owner) {
249
- throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
266
+ throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
250
267
  }
268
+ }
269
+ return;
251
270
  }
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
- }
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');
282
275
  }
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
- }
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);
292
280
  }
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
- }
281
+ // inverse side is not defined as owner
282
+ if (inverse.inversedBy || inverse.owner) {
283
+ throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
311
284
  }
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
- }
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');
325
291
  }
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
- }
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);
340
302
  }
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
- }
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
+ }
351
+ }
352
+ validateVersionField(meta) {
353
+ if (!meta.versionProperty) {
354
+ return;
355
+ }
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
+ );
362
+ }
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);
367
+ }
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
+ }
382
+ }
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);
392
+ }
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
+ }
381
423
  }
424
+ }
382
425
  }