@mikro-orm/core 7.0.0-dev.33 → 7.0.0-dev.331

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 (218) hide show
  1. package/EntityManager.d.ts +70 -75
  2. package/EntityManager.js +487 -402
  3. package/MikroORM.d.ts +45 -38
  4. package/MikroORM.js +123 -156
  5. package/README.md +7 -4
  6. package/cache/FileCacheAdapter.d.ts +2 -7
  7. package/cache/FileCacheAdapter.js +35 -30
  8. package/cache/GeneratedCacheAdapter.d.ts +1 -2
  9. package/cache/GeneratedCacheAdapter.js +6 -8
  10. package/cache/MemoryCacheAdapter.d.ts +1 -2
  11. package/cache/MemoryCacheAdapter.js +8 -8
  12. package/cache/index.d.ts +1 -2
  13. package/cache/index.js +0 -2
  14. package/connections/Connection.d.ts +12 -5
  15. package/connections/Connection.js +37 -15
  16. package/drivers/DatabaseDriver.d.ts +25 -18
  17. package/drivers/DatabaseDriver.js +144 -45
  18. package/drivers/IDatabaseDriver.d.ts +118 -23
  19. package/entity/BaseEntity.d.ts +63 -4
  20. package/entity/BaseEntity.js +0 -3
  21. package/entity/Collection.d.ts +95 -31
  22. package/entity/Collection.js +487 -139
  23. package/entity/EntityAssigner.js +37 -25
  24. package/entity/EntityFactory.d.ts +8 -9
  25. package/entity/EntityFactory.js +152 -100
  26. package/entity/EntityHelper.d.ts +2 -2
  27. package/entity/EntityHelper.js +69 -27
  28. package/entity/EntityLoader.d.ts +12 -13
  29. package/entity/EntityLoader.js +286 -125
  30. package/entity/EntityRepository.d.ts +28 -8
  31. package/entity/EntityRepository.js +8 -2
  32. package/entity/PolymorphicRef.d.ts +12 -0
  33. package/entity/PolymorphicRef.js +18 -0
  34. package/entity/Reference.d.ts +3 -8
  35. package/entity/Reference.js +62 -29
  36. package/entity/WrappedEntity.d.ts +7 -10
  37. package/entity/WrappedEntity.js +6 -7
  38. package/entity/defineEntity.d.ts +472 -313
  39. package/entity/defineEntity.js +134 -290
  40. package/entity/index.d.ts +2 -2
  41. package/entity/index.js +2 -2
  42. package/entity/utils.d.ts +6 -1
  43. package/entity/utils.js +46 -11
  44. package/entity/validators.d.ts +11 -0
  45. package/entity/validators.js +66 -0
  46. package/enums.d.ts +8 -6
  47. package/enums.js +13 -17
  48. package/errors.d.ts +26 -16
  49. package/errors.js +63 -31
  50. package/events/EventManager.d.ts +3 -5
  51. package/events/EventManager.js +37 -26
  52. package/events/index.d.ts +1 -1
  53. package/events/index.js +0 -1
  54. package/exceptions.js +9 -2
  55. package/hydration/Hydrator.js +1 -2
  56. package/hydration/ObjectHydrator.d.ts +5 -6
  57. package/hydration/ObjectHydrator.js +109 -50
  58. package/index.d.ts +2 -2
  59. package/index.js +1 -2
  60. package/logging/DefaultLogger.d.ts +1 -1
  61. package/logging/DefaultLogger.js +3 -4
  62. package/logging/SimpleLogger.d.ts +1 -1
  63. package/logging/colors.d.ts +1 -1
  64. package/logging/colors.js +4 -6
  65. package/logging/index.d.ts +2 -1
  66. package/logging/index.js +1 -1
  67. package/logging/inspect.d.ts +2 -0
  68. package/logging/inspect.js +11 -0
  69. package/metadata/EntitySchema.d.ts +47 -23
  70. package/metadata/EntitySchema.js +103 -34
  71. package/metadata/MetadataDiscovery.d.ts +65 -18
  72. package/metadata/MetadataDiscovery.js +940 -424
  73. package/metadata/MetadataProvider.d.ts +11 -2
  74. package/metadata/MetadataProvider.js +71 -2
  75. package/metadata/MetadataStorage.d.ts +11 -13
  76. package/metadata/MetadataStorage.js +79 -48
  77. package/metadata/MetadataValidator.d.ts +32 -9
  78. package/metadata/MetadataValidator.js +214 -44
  79. package/metadata/discover-entities.d.ts +5 -0
  80. package/metadata/discover-entities.js +40 -0
  81. package/metadata/index.d.ts +1 -1
  82. package/metadata/index.js +0 -1
  83. package/metadata/types.d.ts +577 -0
  84. package/metadata/types.js +1 -0
  85. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  86. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  87. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/EntityCaseNamingStrategy.js +7 -6
  89. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  90. package/naming-strategy/MongoNamingStrategy.js +6 -6
  91. package/naming-strategy/NamingStrategy.d.ts +28 -4
  92. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  93. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  94. package/naming-strategy/index.d.ts +1 -1
  95. package/naming-strategy/index.js +0 -1
  96. package/not-supported.d.ts +2 -0
  97. package/not-supported.js +8 -0
  98. package/package.json +47 -36
  99. package/platforms/ExceptionConverter.js +1 -1
  100. package/platforms/Platform.d.ts +33 -15
  101. package/platforms/Platform.js +125 -69
  102. package/serialization/EntitySerializer.d.ts +6 -3
  103. package/serialization/EntitySerializer.js +54 -30
  104. package/serialization/EntityTransformer.js +37 -22
  105. package/serialization/SerializationContext.d.ts +10 -14
  106. package/serialization/SerializationContext.js +24 -19
  107. package/types/ArrayType.d.ts +1 -1
  108. package/types/ArrayType.js +2 -3
  109. package/types/BigIntType.js +1 -1
  110. package/types/BlobType.d.ts +0 -1
  111. package/types/BlobType.js +0 -3
  112. package/types/BooleanType.d.ts +1 -0
  113. package/types/BooleanType.js +3 -0
  114. package/types/DecimalType.js +2 -2
  115. package/types/DoubleType.js +1 -1
  116. package/types/EnumArrayType.js +1 -2
  117. package/types/JsonType.d.ts +1 -1
  118. package/types/JsonType.js +7 -2
  119. package/types/TinyIntType.js +1 -1
  120. package/types/Type.d.ts +2 -4
  121. package/types/Type.js +3 -3
  122. package/types/Uint8ArrayType.d.ts +0 -1
  123. package/types/Uint8ArrayType.js +1 -4
  124. package/types/UuidType.d.ts +2 -0
  125. package/types/UuidType.js +14 -2
  126. package/types/index.d.ts +3 -2
  127. package/typings.d.ts +427 -170
  128. package/typings.js +100 -45
  129. package/unit-of-work/ChangeSet.d.ts +4 -6
  130. package/unit-of-work/ChangeSet.js +8 -9
  131. package/unit-of-work/ChangeSetComputer.d.ts +2 -12
  132. package/unit-of-work/ChangeSetComputer.js +61 -38
  133. package/unit-of-work/ChangeSetPersister.d.ts +10 -17
  134. package/unit-of-work/ChangeSetPersister.js +136 -73
  135. package/unit-of-work/CommitOrderCalculator.d.ts +13 -14
  136. package/unit-of-work/CommitOrderCalculator.js +22 -20
  137. package/unit-of-work/IdentityMap.d.ts +12 -3
  138. package/unit-of-work/IdentityMap.js +51 -13
  139. package/unit-of-work/UnitOfWork.d.ts +39 -23
  140. package/unit-of-work/UnitOfWork.js +441 -246
  141. package/utils/AbstractMigrator.d.ts +101 -0
  142. package/utils/AbstractMigrator.js +303 -0
  143. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  144. package/utils/AbstractSchemaGenerator.js +30 -18
  145. package/utils/AsyncContext.d.ts +6 -0
  146. package/utils/AsyncContext.js +42 -0
  147. package/utils/Configuration.d.ts +647 -185
  148. package/utils/Configuration.js +215 -252
  149. package/utils/ConfigurationLoader.d.ts +1 -52
  150. package/utils/ConfigurationLoader.js +1 -330
  151. package/utils/Cursor.d.ts +3 -6
  152. package/utils/Cursor.js +32 -17
  153. package/utils/DataloaderUtils.d.ts +10 -5
  154. package/utils/DataloaderUtils.js +42 -22
  155. package/utils/EntityComparator.d.ts +21 -21
  156. package/utils/EntityComparator.js +224 -118
  157. package/utils/QueryHelper.d.ts +34 -7
  158. package/utils/QueryHelper.js +183 -72
  159. package/utils/RawQueryFragment.d.ts +28 -34
  160. package/utils/RawQueryFragment.js +37 -72
  161. package/utils/RequestContext.js +2 -2
  162. package/utils/TransactionContext.js +2 -2
  163. package/utils/TransactionManager.js +11 -8
  164. package/utils/Utils.d.ts +16 -127
  165. package/utils/Utils.js +104 -402
  166. package/utils/clone.js +13 -23
  167. package/utils/env-vars.d.ts +7 -0
  168. package/utils/env-vars.js +98 -0
  169. package/utils/fs-utils.d.ts +20 -0
  170. package/utils/fs-utils.js +193 -0
  171. package/utils/index.d.ts +1 -3
  172. package/utils/index.js +1 -3
  173. package/utils/upsert-utils.d.ts +9 -4
  174. package/utils/upsert-utils.js +51 -5
  175. package/decorators/Check.d.ts +0 -3
  176. package/decorators/Check.js +0 -13
  177. package/decorators/CreateRequestContext.d.ts +0 -3
  178. package/decorators/CreateRequestContext.js +0 -32
  179. package/decorators/Embeddable.d.ts +0 -8
  180. package/decorators/Embeddable.js +0 -11
  181. package/decorators/Embedded.d.ts +0 -12
  182. package/decorators/Embedded.js +0 -18
  183. package/decorators/Entity.d.ts +0 -33
  184. package/decorators/Entity.js +0 -12
  185. package/decorators/Enum.d.ts +0 -9
  186. package/decorators/Enum.js +0 -16
  187. package/decorators/Filter.d.ts +0 -2
  188. package/decorators/Filter.js +0 -8
  189. package/decorators/Formula.d.ts +0 -4
  190. package/decorators/Formula.js +0 -15
  191. package/decorators/Indexed.d.ts +0 -19
  192. package/decorators/Indexed.js +0 -20
  193. package/decorators/ManyToMany.d.ts +0 -42
  194. package/decorators/ManyToMany.js +0 -14
  195. package/decorators/ManyToOne.d.ts +0 -34
  196. package/decorators/ManyToOne.js +0 -14
  197. package/decorators/OneToMany.d.ts +0 -28
  198. package/decorators/OneToMany.js +0 -17
  199. package/decorators/OneToOne.d.ts +0 -28
  200. package/decorators/OneToOne.js +0 -7
  201. package/decorators/PrimaryKey.d.ts +0 -8
  202. package/decorators/PrimaryKey.js +0 -20
  203. package/decorators/Property.d.ts +0 -250
  204. package/decorators/Property.js +0 -32
  205. package/decorators/Transactional.d.ts +0 -14
  206. package/decorators/Transactional.js +0 -28
  207. package/decorators/hooks.d.ts +0 -16
  208. package/decorators/hooks.js +0 -47
  209. package/decorators/index.d.ts +0 -17
  210. package/decorators/index.js +0 -17
  211. package/entity/ArrayCollection.d.ts +0 -118
  212. package/entity/ArrayCollection.js +0 -407
  213. package/entity/EntityValidator.d.ts +0 -19
  214. package/entity/EntityValidator.js +0 -150
  215. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  216. package/metadata/ReflectMetadataProvider.js +0 -44
  217. package/utils/resolveContextProvider.d.ts +0 -10
  218. package/utils/resolveContextProvider.js +0 -28
@@ -1,23 +1,33 @@
1
1
  import { Utils } from '../utils/Utils.js';
2
2
  import { MetadataError } from '../errors.js';
3
3
  import { ReferenceKind } from '../enums.js';
4
+ /**
5
+ * List of property names that could lead to prototype pollution vulnerabilities.
6
+ * These names should never be used as entity property names because they could
7
+ * allow malicious code to modify object prototypes when property values are assigned.
8
+ *
9
+ * - `__proto__`: Could modify the prototype chain
10
+ * - `constructor`: Could modify the constructor property
11
+ * - `prototype`: Could modify the prototype object
12
+ *
13
+ * @internal
14
+ */
15
+ const DANGEROUS_PROPERTY_NAMES = ['__proto__', 'constructor', 'prototype'];
4
16
  /**
5
17
  * @internal
6
18
  */
7
19
  export class MetadataValidator {
8
- /**
9
- * Validate there is only one property decorator. This disallows using `@Property()` together with e.g. `@ManyToOne()`
10
- * on the same property. One should use only `@ManyToOne()` in such case.
11
- * We allow the existence of the property in metadata if the reference type is the same, this should allow things like HMR to work.
12
- */
13
- static validateSingleDecorator(meta, propertyName, reference) {
14
- if (meta.properties[propertyName] && meta.properties[propertyName].kind !== reference) {
15
- throw MetadataError.multipleDecorators(meta.className, propertyName);
16
- }
17
- }
18
20
  validateEntityDefinition(metadata, name, options) {
19
21
  const meta = metadata.get(name);
20
- if (meta.virtual || meta.expression) {
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)) {
21
31
  for (const prop of Utils.values(meta.properties)) {
22
32
  if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED, ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
23
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}`);
@@ -36,13 +46,14 @@ export class MetadataValidator {
36
46
  this.validateDuplicateFieldNames(meta, options);
37
47
  this.validateIndexes(meta, meta.indexes ?? [], 'index');
38
48
  this.validateIndexes(meta, meta.uniques ?? [], 'unique');
49
+ this.validatePropertyNames(meta);
39
50
  for (const prop of Utils.values(meta.properties)) {
40
51
  if (prop.kind !== ReferenceKind.SCALAR) {
41
- this.validateReference(meta, prop, metadata, options);
42
- this.validateBidirectional(meta, prop, metadata);
52
+ this.validateReference(meta, prop, options);
53
+ this.validateBidirectional(meta, prop);
43
54
  }
44
- else if (metadata.has(prop.type)) {
45
- throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.get(prop.type));
55
+ else if (metadata.getByClassName(prop.type, false)) {
56
+ throw MetadataError.propertyTargetsEntityType(meta, prop, metadata.getByClassName(prop.type));
46
57
  }
47
58
  }
48
59
  }
@@ -50,17 +61,19 @@ export class MetadataValidator {
50
61
  if (discovered.length === 0 && options.warnWhenNoEntities) {
51
62
  throw MetadataError.noEntityDiscovered();
52
63
  }
53
- const duplicates = Utils.findDuplicates(discovered.map(meta => meta.className));
54
- if (duplicates.length > 0 && options.checkDuplicateEntities) {
55
- throw MetadataError.duplicateEntityDiscovered(duplicates);
56
- }
57
- const tableNames = discovered.filter(meta => !meta.abstract && meta === meta.root && (meta.tableName || meta.collection) && meta.schema !== '*');
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 !== '*');
58
71
  const duplicateTableNames = Utils.findDuplicates(tableNames.map(meta => {
59
72
  const tableName = meta.tableName || meta.collection;
60
73
  return (meta.schema ? '.' + meta.schema : '') + tableName;
61
74
  }));
62
- if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames && options.checkDuplicateEntities) {
63
- throw MetadataError.duplicateEntityDiscovered(duplicateTableNames, 'table names');
75
+ if (duplicateTableNames.length > 0 && options.checkDuplicateTableNames) {
76
+ throw MetadataError.duplicateEntityDiscovered(duplicateTableNames);
64
77
  }
65
78
  // validate we found at least one entity (not just abstract/base entities)
66
79
  if (discovered.filter(meta => meta.name).length === 0 && options.warnWhenNoEntities) {
@@ -71,7 +84,7 @@ export class MetadataValidator {
71
84
  .replace(/\[]$/, '') // remove array suffix
72
85
  .replace(/\((.*)\)/, '$1'); // unwrap union types
73
86
  const name = (p) => {
74
- if (typeof p === 'function') {
87
+ if (typeof p === 'function' && !p.prototype) {
75
88
  return Utils.className(p());
76
89
  }
77
90
  return Utils.className(p);
@@ -79,7 +92,10 @@ export class MetadataValidator {
79
92
  const pivotProps = new Map();
80
93
  // check for not discovered entities
81
94
  discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
82
- if (prop.kind !== ReferenceKind.SCALAR && !unwrap(prop.type).split(/ ?\| ?/).every(type => discovered.find(m => m.className === type))) {
95
+ if (prop.kind !== ReferenceKind.SCALAR &&
96
+ !unwrap(prop.type)
97
+ .split(/ ?\| ?/)
98
+ .every(type => discovered.find(m => m.className === type))) {
83
99
  throw MetadataError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
84
100
  }
85
101
  if (prop.pivotEntity) {
@@ -95,47 +111,137 @@ export class MetadataValidator {
95
111
  }
96
112
  });
97
113
  }
98
- validateReference(meta, prop, metadata, options) {
114
+ validateReference(meta, prop, options) {
99
115
  // references do have types
100
116
  if (!prop.type) {
101
117
  throw MetadataError.fromWrongTypeDefinition(meta, prop);
102
118
  }
103
- const targetMeta = metadata.find(prop.type);
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;
104
125
  // references do have type of known entity
105
126
  if (!targetMeta) {
106
127
  throw MetadataError.fromWrongTypeDefinition(meta, prop);
107
128
  }
108
- if (targetMeta.abstract && !targetMeta.discriminatorColumn && !targetMeta.embeddable) {
129
+ if (targetMeta.abstract && !targetMeta.root?.inheritanceType && !targetMeta.embeddable) {
109
130
  throw MetadataError.targetIsAbstract(meta, prop);
110
131
  }
111
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false && targetMeta.compositePK && options.checkNonPersistentCompositeProps) {
132
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
133
+ prop.persist === false &&
134
+ targetMeta.compositePK &&
135
+ options.checkNonPersistentCompositeProps) {
112
136
  throw MetadataError.nonPersistentCompositeProp(meta, prop);
113
137
  }
138
+ this.validateTargetKey(meta, prop, targetMeta);
139
+ }
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];
150
+ if (!targetProp) {
151
+ throw MetadataError.targetKeyNotFound(meta, prop);
152
+ }
153
+ // targetKey must point to a unique property (composite unique is not sufficient)
154
+ if (!this.isPropertyUnique(targetProp, targetMeta)) {
155
+ throw MetadataError.targetKeyNotUnique(meta, prop);
156
+ }
157
+ }
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
+ }
114
202
  }
115
- validateBidirectional(meta, prop, metadata) {
203
+ validateBidirectional(meta, prop) {
116
204
  if (prop.inversedBy) {
117
- const inverse = metadata.get(prop.type).properties[prop.inversedBy];
118
- this.validateOwningSide(meta, prop, inverse, metadata);
205
+ this.validateOwningSide(meta, prop);
119
206
  }
120
207
  else if (prop.mappedBy) {
121
- const inverse = metadata.get(prop.type).properties[prop.mappedBy];
122
- this.validateInverseSide(meta, prop, inverse, metadata);
208
+ this.validateInverseSide(meta, prop);
123
209
  }
124
- else {
210
+ else if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
125
211
  // 1:m property has `mappedBy`
126
- if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
127
- throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
128
- }
212
+ throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
129
213
  }
130
214
  }
131
- validateOwningSide(meta, prop, inverse, metadata) {
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];
132
238
  // has correct `inversedBy` on owning side
133
239
  if (!inverse) {
134
240
  throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
135
241
  }
136
- const targetClassName = metadata.find(inverse.type)?.root.className;
242
+ const targetClass = inverse.targetMeta?.root.class;
137
243
  // has correct `inversedBy` reference type
138
- if (inverse.type !== meta.className && targetClassName !== meta.root.className) {
244
+ if (inverse.type !== meta.className && targetClass !== meta.root.class) {
139
245
  throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
140
246
  }
141
247
  // inverse side is not defined as owner
@@ -143,13 +249,18 @@ export class MetadataValidator {
143
249
  throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
144
250
  }
145
251
  }
146
- validateInverseSide(meta, prop, owner, metadata) {
252
+ validateInverseSide(meta, prop) {
253
+ const owner = prop.targetMeta.properties[prop.mappedBy];
147
254
  // has correct `mappedBy` on inverse side
148
255
  if (prop.mappedBy && !owner) {
149
256
  throw MetadataError.fromWrongReference(meta, prop, 'mappedBy');
150
257
  }
151
258
  // has correct `mappedBy` reference type
152
- if (owner.type !== meta.className && metadata.find(owner.type)?.root.className !== meta.root.className) {
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) {
153
264
  throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
154
265
  }
155
266
  // owning side is not defined as inverse
@@ -181,7 +292,10 @@ export class MetadataValidator {
181
292
  }
182
293
  validateDuplicateFieldNames(meta, options) {
183
294
  const candidates = Object.values(meta.properties)
184
- .filter(prop => prop.persist !== false && !prop.inherited && prop.fieldNames?.length === 1 && (prop.kind !== ReferenceKind.EMBEDDED || prop.object))
295
+ .filter(prop => prop.persist !== false &&
296
+ !prop.inherited &&
297
+ prop.fieldNames?.length === 1 &&
298
+ (prop.kind !== ReferenceKind.EMBEDDED || prop.object))
185
299
  .map(prop => prop.fieldNames[0]);
186
300
  const duplicates = Utils.findDuplicates(candidates);
187
301
  if (duplicates.length > 0 && options.checkDuplicateFieldNames) {
@@ -192,7 +306,7 @@ export class MetadataValidator {
192
306
  return [prop.embedded ? prop.embedded.join('.') : prop.name, prop.fieldNames[0]];
193
307
  });
194
308
  });
195
- throw MetadataError.duplicateFieldName(meta.className, pairs);
309
+ throw MetadataError.duplicateFieldName(meta.class, pairs);
196
310
  }
197
311
  }
198
312
  validateVersionField(meta) {
@@ -209,4 +323,60 @@ export class MetadataValidator {
209
323
  throw MetadataError.invalidVersionFieldType(meta);
210
324
  }
211
325
  }
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
+ }
340
+ }
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
+ }
381
+ }
212
382
  }
@@ -0,0 +1,5 @@
1
+ import { type Constructor } from '../typings.js';
2
+ import { EntitySchema } from './EntitySchema.js';
3
+ export declare function discoverEntities(paths: string | string[], options?: {
4
+ baseDir?: string;
5
+ }): Promise<Iterable<EntitySchema | Constructor>>;
@@ -0,0 +1,40 @@
1
+ import { basename } from 'node:path';
2
+ import { fs } from '../utils/fs-utils.js';
3
+ import { Utils } from '../utils/Utils.js';
4
+ import { MetadataStorage } from './MetadataStorage.js';
5
+ import { EntitySchema } from './EntitySchema.js';
6
+ async function getEntityClassOrSchema(filepath, allTargets, baseDir) {
7
+ const path = fs.normalizePath(baseDir, filepath);
8
+ const exports = await fs.dynamicImport(path);
9
+ const targets = Object.values(exports);
10
+ // ignore class implementations that are linked from an EntitySchema
11
+ for (const item of targets) {
12
+ if (item instanceof EntitySchema) {
13
+ for (const item2 of targets) {
14
+ if (item.meta.class === item2) {
15
+ targets.splice(targets.indexOf(item2), 1);
16
+ }
17
+ }
18
+ }
19
+ }
20
+ for (const item of targets) {
21
+ const validTarget = item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name));
22
+ if (validTarget && !allTargets.has(item)) {
23
+ allTargets.set(item, path);
24
+ }
25
+ }
26
+ }
27
+ export async function discoverEntities(paths, options) {
28
+ paths = Utils.asArray(paths).map(path => fs.normalizePath(path));
29
+ const baseDir = fs.absolutePath(options?.baseDir ?? process.cwd());
30
+ const files = fs.glob(paths, fs.normalizePath(baseDir));
31
+ const found = new Map();
32
+ for (const filepath of files) {
33
+ const filename = basename(filepath);
34
+ if (!/\.[cm]?[jt]s$/.exec(filename) || /\.d\.[cm]?ts/.exec(filename)) {
35
+ continue;
36
+ }
37
+ await getEntityClassOrSchema(filepath, found, baseDir);
38
+ }
39
+ return found.keys();
40
+ }
@@ -1,6 +1,6 @@
1
+ export type * from './types.js';
1
2
  export * from './EntitySchema.js';
2
3
  export * from './MetadataDiscovery.js';
3
4
  export * from './MetadataStorage.js';
4
5
  export * from './MetadataProvider.js';
5
6
  export * from './MetadataValidator.js';
6
- export * from './ReflectMetadataProvider.js';
package/metadata/index.js CHANGED
@@ -3,4 +3,3 @@ export * from './MetadataDiscovery.js';
3
3
  export * from './MetadataStorage.js';
4
4
  export * from './MetadataProvider.js';
5
5
  export * from './MetadataValidator.js';
6
- export * from './ReflectMetadataProvider.js';