@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321

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 (216) hide show
  1. package/EntityManager.d.ts +71 -63
  2. package/EntityManager.js +365 -283
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +7 -4
  6. package/cache/FileCacheAdapter.d.ts +1 -2
  7. package/cache/FileCacheAdapter.js +19 -14
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +1 -2
  11. package/cache/index.js +0 -2
  12. package/connections/Connection.d.ts +12 -5
  13. package/connections/Connection.js +37 -15
  14. package/drivers/DatabaseDriver.d.ts +25 -18
  15. package/drivers/DatabaseDriver.js +144 -45
  16. package/drivers/IDatabaseDriver.d.ts +118 -23
  17. package/entity/BaseEntity.d.ts +63 -4
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +101 -29
  20. package/entity/Collection.js +473 -115
  21. package/entity/EntityAssigner.js +37 -25
  22. package/entity/EntityFactory.d.ts +7 -1
  23. package/entity/EntityFactory.js +116 -64
  24. package/entity/EntityHelper.d.ts +2 -2
  25. package/entity/EntityHelper.js +69 -27
  26. package/entity/EntityLoader.d.ts +11 -10
  27. package/entity/EntityLoader.js +264 -102
  28. package/entity/EntityRepository.d.ts +28 -8
  29. package/entity/EntityRepository.js +8 -2
  30. package/entity/PolymorphicRef.d.ts +12 -0
  31. package/entity/PolymorphicRef.js +18 -0
  32. package/entity/Reference.d.ts +2 -6
  33. package/entity/Reference.js +52 -19
  34. package/entity/WrappedEntity.d.ts +3 -8
  35. package/entity/WrappedEntity.js +6 -7
  36. package/entity/defineEntity.d.ts +525 -311
  37. package/entity/defineEntity.js +134 -290
  38. package/entity/index.d.ts +2 -2
  39. package/entity/index.js +2 -2
  40. package/entity/utils.d.ts +6 -1
  41. package/entity/utils.js +46 -11
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +66 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +13 -17
  46. package/errors.d.ts +20 -10
  47. package/errors.js +63 -31
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +24 -13
  50. package/events/index.d.ts +1 -1
  51. package/events/index.js +0 -1
  52. package/exceptions.js +9 -2
  53. package/hydration/Hydrator.js +1 -2
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +105 -46
  56. package/index.d.ts +2 -2
  57. package/index.js +1 -2
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/DefaultLogger.js +3 -4
  60. package/logging/SimpleLogger.d.ts +1 -1
  61. package/logging/colors.d.ts +1 -1
  62. package/logging/colors.js +5 -7
  63. package/logging/index.d.ts +2 -1
  64. package/logging/index.js +1 -1
  65. package/logging/inspect.d.ts +2 -0
  66. package/logging/inspect.js +11 -0
  67. package/metadata/EntitySchema.d.ts +47 -23
  68. package/metadata/EntitySchema.js +103 -34
  69. package/metadata/MetadataDiscovery.d.ts +64 -9
  70. package/metadata/MetadataDiscovery.js +867 -354
  71. package/metadata/MetadataProvider.d.ts +11 -2
  72. package/metadata/MetadataProvider.js +71 -2
  73. package/metadata/MetadataStorage.d.ts +13 -11
  74. package/metadata/MetadataStorage.js +72 -41
  75. package/metadata/MetadataValidator.d.ts +32 -9
  76. package/metadata/MetadataValidator.js +214 -44
  77. package/metadata/discover-entities.d.ts +5 -0
  78. package/metadata/discover-entities.js +40 -0
  79. package/metadata/index.d.ts +1 -1
  80. package/metadata/index.js +0 -1
  81. package/metadata/types.d.ts +577 -0
  82. package/metadata/types.js +1 -0
  83. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  84. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  85. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  86. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  87. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/MongoNamingStrategy.js +6 -6
  89. package/naming-strategy/NamingStrategy.d.ts +28 -4
  90. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  91. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  92. package/naming-strategy/index.d.ts +1 -1
  93. package/naming-strategy/index.js +0 -1
  94. package/not-supported.d.ts +2 -0
  95. package/not-supported.js +8 -0
  96. package/package.json +47 -36
  97. package/platforms/ExceptionConverter.js +1 -1
  98. package/platforms/Platform.d.ts +33 -15
  99. package/platforms/Platform.js +125 -69
  100. package/serialization/EntitySerializer.d.ts +6 -3
  101. package/serialization/EntitySerializer.js +53 -29
  102. package/serialization/EntityTransformer.js +33 -21
  103. package/serialization/SerializationContext.d.ts +6 -6
  104. package/serialization/SerializationContext.js +4 -4
  105. package/types/ArrayType.d.ts +1 -1
  106. package/types/ArrayType.js +2 -3
  107. package/types/BigIntType.js +1 -1
  108. package/types/BlobType.d.ts +0 -1
  109. package/types/BlobType.js +0 -3
  110. package/types/BooleanType.d.ts +1 -0
  111. package/types/BooleanType.js +3 -0
  112. package/types/DecimalType.js +2 -2
  113. package/types/DoubleType.js +1 -1
  114. package/types/EnumArrayType.js +1 -2
  115. package/types/JsonType.d.ts +1 -1
  116. package/types/JsonType.js +7 -2
  117. package/types/TinyIntType.js +1 -1
  118. package/types/Type.d.ts +2 -4
  119. package/types/Type.js +3 -3
  120. package/types/Uint8ArrayType.d.ts +0 -1
  121. package/types/Uint8ArrayType.js +1 -4
  122. package/types/UuidType.d.ts +2 -0
  123. package/types/UuidType.js +14 -2
  124. package/types/index.d.ts +3 -2
  125. package/typings.d.ts +427 -170
  126. package/typings.js +100 -45
  127. package/unit-of-work/ChangeSet.d.ts +4 -6
  128. package/unit-of-work/ChangeSet.js +8 -9
  129. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  130. package/unit-of-work/ChangeSetComputer.js +49 -26
  131. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  132. package/unit-of-work/ChangeSetPersister.js +107 -44
  133. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  134. package/unit-of-work/CommitOrderCalculator.js +17 -15
  135. package/unit-of-work/IdentityMap.d.ts +12 -0
  136. package/unit-of-work/IdentityMap.js +39 -1
  137. package/unit-of-work/UnitOfWork.d.ts +34 -4
  138. package/unit-of-work/UnitOfWork.js +294 -107
  139. package/utils/AbstractMigrator.d.ts +101 -0
  140. package/utils/AbstractMigrator.js +303 -0
  141. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  142. package/utils/AbstractSchemaGenerator.js +30 -18
  143. package/utils/AsyncContext.d.ts +6 -0
  144. package/utils/AsyncContext.js +42 -0
  145. package/utils/Configuration.d.ts +796 -211
  146. package/utils/Configuration.js +160 -197
  147. package/utils/ConfigurationLoader.d.ts +1 -52
  148. package/utils/ConfigurationLoader.js +1 -330
  149. package/utils/Cursor.d.ts +0 -3
  150. package/utils/Cursor.js +29 -14
  151. package/utils/DataloaderUtils.d.ts +10 -5
  152. package/utils/DataloaderUtils.js +42 -22
  153. package/utils/EntityComparator.d.ts +16 -9
  154. package/utils/EntityComparator.js +202 -96
  155. package/utils/QueryHelper.d.ts +34 -7
  156. package/utils/QueryHelper.js +183 -72
  157. package/utils/RawQueryFragment.d.ts +28 -34
  158. package/utils/RawQueryFragment.js +37 -72
  159. package/utils/RequestContext.js +2 -2
  160. package/utils/TransactionContext.js +2 -2
  161. package/utils/TransactionManager.js +11 -7
  162. package/utils/Utils.d.ts +16 -127
  163. package/utils/Utils.js +106 -401
  164. package/utils/clone.js +13 -23
  165. package/utils/env-vars.d.ts +7 -0
  166. package/utils/env-vars.js +98 -0
  167. package/utils/fs-utils.d.ts +34 -0
  168. package/utils/fs-utils.js +193 -0
  169. package/utils/index.d.ts +1 -3
  170. package/utils/index.js +1 -3
  171. package/utils/upsert-utils.d.ts +9 -4
  172. package/utils/upsert-utils.js +51 -5
  173. package/decorators/Check.d.ts +0 -3
  174. package/decorators/Check.js +0 -13
  175. package/decorators/CreateRequestContext.d.ts +0 -3
  176. package/decorators/CreateRequestContext.js +0 -32
  177. package/decorators/Embeddable.d.ts +0 -8
  178. package/decorators/Embeddable.js +0 -11
  179. package/decorators/Embedded.d.ts +0 -12
  180. package/decorators/Embedded.js +0 -18
  181. package/decorators/Entity.d.ts +0 -33
  182. package/decorators/Entity.js +0 -12
  183. package/decorators/Enum.d.ts +0 -9
  184. package/decorators/Enum.js +0 -16
  185. package/decorators/Filter.d.ts +0 -2
  186. package/decorators/Filter.js +0 -8
  187. package/decorators/Formula.d.ts +0 -4
  188. package/decorators/Formula.js +0 -15
  189. package/decorators/Indexed.d.ts +0 -19
  190. package/decorators/Indexed.js +0 -20
  191. package/decorators/ManyToMany.d.ts +0 -42
  192. package/decorators/ManyToMany.js +0 -14
  193. package/decorators/ManyToOne.d.ts +0 -34
  194. package/decorators/ManyToOne.js +0 -14
  195. package/decorators/OneToMany.d.ts +0 -28
  196. package/decorators/OneToMany.js +0 -17
  197. package/decorators/OneToOne.d.ts +0 -28
  198. package/decorators/OneToOne.js +0 -7
  199. package/decorators/PrimaryKey.d.ts +0 -8
  200. package/decorators/PrimaryKey.js +0 -20
  201. package/decorators/Property.d.ts +0 -250
  202. package/decorators/Property.js +0 -32
  203. package/decorators/Transactional.d.ts +0 -14
  204. package/decorators/Transactional.js +0 -28
  205. package/decorators/hooks.d.ts +0 -16
  206. package/decorators/hooks.js +0 -47
  207. package/decorators/index.d.ts +0 -17
  208. package/decorators/index.js +0 -17
  209. package/entity/ArrayCollection.d.ts +0 -118
  210. package/entity/ArrayCollection.js +0 -407
  211. package/entity/EntityValidator.d.ts +0 -19
  212. package/entity/EntityValidator.js +0 -150
  213. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  214. package/metadata/ReflectMetadataProvider.js +0 -44
  215. package/utils/resolveContextProvider.d.ts +0 -10
  216. package/utils/resolveContextProvider.js +0 -28
@@ -1,12 +1,11 @@
1
- import { inspect } from 'node:util';
2
1
  import { Collection } from './Collection.js';
3
2
  import { Utils } from '../utils/Utils.js';
4
3
  import { Reference } from './Reference.js';
5
4
  import { ReferenceKind, SCALAR_TYPES } from '../enums.js';
6
- import { EntityValidator } from './EntityValidator.js';
5
+ import { validateProperty } from './validators.js';
7
6
  import { helper, wrap } from './wrap.js';
8
7
  import { EntityHelper } from './EntityHelper.js';
9
- const validator = new EntityValidator(false);
8
+ import { ValidationError } from '../errors.js';
10
9
  export class EntityAssigner {
11
10
  static assign(entity, data, options = {}) {
12
11
  let opts = options;
@@ -72,13 +71,16 @@ export class EntityAssigner {
72
71
  value = customType.convertToJSValue(value, options.platform);
73
72
  }
74
73
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop?.kind) && value != null) {
75
- if (options.updateNestedEntities && Object.hasOwn(entity, propName) && Utils.isEntity(entity[propName], true) && Utils.isPlainObject(value)) {
74
+ if (options.updateNestedEntities &&
75
+ Object.hasOwn(entity, propName) &&
76
+ Utils.isEntity(entity[propName], true) &&
77
+ Utils.isPlainObject(value)) {
76
78
  const unwrappedEntity = Reference.unwrapReference(entity[propName]);
77
79
  const wrapped = helper(unwrappedEntity);
78
80
  if (options.updateByPrimaryKey) {
79
81
  const pk = Utils.extractPK(value, prop.targetMeta);
80
82
  if (pk) {
81
- const ref = options.em.getReference(prop.type, pk, options);
83
+ const ref = options.em.getReference(prop.targetMeta.class, pk, options);
82
84
  // if the PK differs, we want to change the target entity, not update it
83
85
  const wrappedChild = helper(ref);
84
86
  const sameTarget = wrappedChild.getSerializedPrimaryKey() === wrapped.getSerializedPrimaryKey();
@@ -94,13 +96,16 @@ export class EntityAssigner {
94
96
  }
95
97
  return EntityAssigner.assignReference(entity, value, prop, options.em, options);
96
98
  }
97
- if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.includes(prop.runtimeType) && (prop.setter || !prop.getter)) {
98
- return entity[prop.name] = validator.validateProperty(prop, value, entity);
99
+ if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && (prop.setter || !prop.getter)) {
100
+ validateProperty(prop, value, entity);
101
+ return (entity[prop.name] = value);
99
102
  }
100
103
  if (prop.kind === ReferenceKind.EMBEDDED && EntityAssigner.validateEM(options.em)) {
101
104
  return EntityAssigner.assignEmbeddable(entity, value, prop, options.em, options);
102
105
  }
103
- if (options.mergeObjectProperties && Utils.isPlainObject(entity[propName]) && Utils.isPlainObject(value)) {
106
+ if (options.mergeObjectProperties &&
107
+ Utils.isPlainObject(entity[propName]) &&
108
+ Utils.isPlainObject(value)) {
104
109
  entity[propName] ??= {};
105
110
  entity[propName] = Utils.merge({}, entity[propName], value);
106
111
  }
@@ -120,7 +125,7 @@ export class EntityAssigner {
120
125
  }
121
126
  const meta2 = helper(ref).__meta;
122
127
  const prop2 = meta2.properties[prop.inversedBy || prop.mappedBy];
123
- /* v8 ignore next 7 */
128
+ /* v8 ignore next */
124
129
  if (prop2 && !ref[prop2.name]) {
125
130
  if (Reference.isReference(ref)) {
126
131
  ref.unwrap()[prop2.name] = Reference.wrapReference(entity, prop2);
@@ -141,13 +146,15 @@ export class EntityAssigner {
141
146
  entity[prop.name] = Reference.wrapReference(value, prop);
142
147
  }
143
148
  else if (Utils.isPrimaryKey(value, true) && EntityAssigner.validateEM(em)) {
144
- entity[prop.name] = prop.mapToPk ? value : Reference.wrapReference(em.getReference(prop.type, value, options), prop);
149
+ entity[prop.name] = prop.mapToPk
150
+ ? value
151
+ : Reference.wrapReference(em.getReference(prop.targetMeta.class, value, options), prop);
145
152
  }
146
153
  else if (Utils.isPlainObject(value) && options.merge && EntityAssigner.validateEM(em)) {
147
- entity[prop.name] = Reference.wrapReference(em.merge(prop.type, value, options), prop);
154
+ entity[prop.name] = Reference.wrapReference(em.merge(prop.targetMeta.class, value, options), prop);
148
155
  }
149
156
  else if (Utils.isPlainObject(value) && EntityAssigner.validateEM(em)) {
150
- entity[prop.name] = Reference.wrapReference(em.create(prop.type, value, options), prop);
157
+ entity[prop.name] = Reference.wrapReference(em.create(prop.targetMeta.class, value, options), prop);
151
158
  }
152
159
  else {
153
160
  const name = entity.constructor.name;
@@ -166,27 +173,31 @@ export class EntityAssigner {
166
173
  if (options.updateNestedEntities && options.updateByPrimaryKey && Utils.isPlainObject(item)) {
167
174
  const pk = Utils.extractPK(item, prop.targetMeta);
168
175
  if (pk && EntityAssigner.validateEM(em)) {
169
- const ref = em.getUnitOfWork().getById(prop.type, pk, options.schema);
176
+ const ref = em.getUnitOfWork().getById(prop.targetMeta.class, pk, options.schema);
170
177
  if (ref) {
171
178
  return EntityAssigner.assign(ref, item, options);
172
179
  }
173
180
  }
174
181
  return this.createCollectionItem(item, em, prop, invalid, options);
175
182
  }
176
- /* v8 ignore next 3 */
177
- if (options.updateNestedEntities && !options.updateByPrimaryKey && collection[idx] && helper(collection[idx])?.isInitialized()) {
183
+ /* v8 ignore next */
184
+ if (options.updateNestedEntities &&
185
+ !options.updateByPrimaryKey &&
186
+ collection[idx] &&
187
+ helper(collection[idx])?.isInitialized()) {
178
188
  return EntityAssigner.assign(collection[idx], item, options);
179
189
  }
180
190
  return this.createCollectionItem(item, em, prop, invalid, options);
181
191
  });
182
192
  if (invalid.length > 0) {
183
193
  const name = entity.constructor.name;
184
- throw new Error(`Invalid collection values provided for '${name}.${prop.name}' in ${name}.assign(): ${inspect(invalid)}`);
194
+ throw ValidationError.invalidCollectionValues(name, prop.name, invalid);
185
195
  }
186
196
  if (Array.isArray(value)) {
187
197
  collection.set(items);
188
198
  }
189
- else { // append to the collection in case of assigning a single value instead of array
199
+ else {
200
+ // append to the collection in case of assigning a single value instead of array
190
201
  collection.add(items);
191
202
  }
192
203
  }
@@ -207,11 +218,12 @@ export class EntityAssigner {
207
218
  entity[propName].push(...Object.values(tmp));
208
219
  });
209
220
  }
210
- const create = () => EntityAssigner.validateEM(em) && em.getEntityFactory().createEmbeddable(prop.type, value, {
211
- convertCustomTypes: options.convertCustomTypes,
212
- newEntity: options.mergeEmbeddedProperties ? !('propName' in entity) : true,
213
- });
214
- entity[propName] = (options.mergeEmbeddedProperties ? (entity[propName] || create()) : create());
221
+ const create = () => EntityAssigner.validateEM(em) &&
222
+ em.getEntityFactory().createEmbeddable(prop.targetMeta.class, value, {
223
+ convertCustomTypes: options.convertCustomTypes,
224
+ newEntity: options.mergeEmbeddedProperties ? !('propName' in entity) : true,
225
+ });
226
+ entity[propName] = (options.mergeEmbeddedProperties ? entity[propName] || create() : create());
215
227
  Object.keys(value).forEach(key => {
216
228
  EntityAssigner.assignProperty(entity[propName], key, prop.embeddedProps, value, options);
217
229
  });
@@ -221,13 +233,13 @@ export class EntityAssigner {
221
233
  return item;
222
234
  }
223
235
  if (Utils.isPrimaryKey(item) && EntityAssigner.validateEM(em)) {
224
- return em.getReference(prop.type, item, options);
236
+ return em.getReference(prop.targetMeta.class, item, options);
225
237
  }
226
238
  if (Utils.isPlainObject(item) && options.merge && EntityAssigner.validateEM(em)) {
227
- return em.merge(prop.type, item, options);
239
+ return em.merge(prop.targetMeta.class, item, options);
228
240
  }
229
241
  if (Utils.isPlainObject(item) && EntityAssigner.validateEM(em)) {
230
- return em.create(prop.type, item, options);
242
+ return em.create(prop.targetMeta.class, item, options);
231
243
  }
232
244
  invalid.push(item);
233
245
  return item;
@@ -15,6 +15,12 @@ export interface FactoryOptions {
15
15
  recomputeSnapshot?: boolean;
16
16
  schema?: string;
17
17
  parentSchema?: string;
18
+ normalizeAccessors?: boolean;
19
+ /**
20
+ * Property name to use for identity map lookup instead of the primary key.
21
+ * This is useful for creating references by unique non-PK properties.
22
+ */
23
+ key?: string;
18
24
  }
19
25
  export declare class EntityFactory {
20
26
  private readonly em;
@@ -28,7 +34,7 @@ export declare class EntityFactory {
28
34
  constructor(em: EntityManager);
29
35
  create<T extends object, P extends string = string>(entityName: EntityName<T>, data: EntityData<T>, options?: FactoryOptions): New<T, P>;
30
36
  mergeData<T extends object>(meta: EntityMetadata<T>, entity: T, data: EntityData<T>, options?: FactoryOptions): void;
31
- createReference<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[] | Record<string, Primary<T>>, options?: Pick<FactoryOptions, 'merge' | 'convertCustomTypes' | 'schema'>): T;
37
+ createReference<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[] | Record<string, Primary<T>>, options?: Pick<FactoryOptions, 'merge' | 'convertCustomTypes' | 'schema' | 'key'>): T;
32
38
  createEmbeddable<T extends object>(entityName: EntityName<T>, data: EntityData<T>, options?: Pick<FactoryOptions, 'newEntity' | 'convertCustomTypes'>): T;
33
39
  getComparator(): EntityComparator;
34
40
  private createEntity;
@@ -4,6 +4,7 @@ import { EventType, ReferenceKind } from '../enums.js';
4
4
  import { Reference } from './Reference.js';
5
5
  import { helper } from './wrap.js';
6
6
  import { EntityHelper } from './EntityHelper.js';
7
+ import { JsonType } from '../types/JsonType.js';
7
8
  export class EntityFactory {
8
9
  em;
9
10
  driver;
@@ -29,7 +30,6 @@ export class EntityFactory {
29
30
  if (data.__entity) {
30
31
  return data;
31
32
  }
32
- entityName = Utils.className(entityName);
33
33
  const meta = this.metadata.get(entityName);
34
34
  if (meta.virtual) {
35
35
  data = { ...data };
@@ -37,8 +37,8 @@ export class EntityFactory {
37
37
  this.hydrate(entity, meta, data, options);
38
38
  return entity;
39
39
  }
40
- if (this.platform.usesDifferentSerializedPrimaryKey()) {
41
- meta.primaryKeys.forEach(pk => this.denormalizePrimaryKey(data, pk, meta.properties[pk]));
40
+ if (meta.serializedPrimaryKey) {
41
+ this.denormalizePrimaryKey(meta, data);
42
42
  }
43
43
  const meta2 = this.processDiscriminatorColumn(meta, data);
44
44
  const exists = this.findEntity(data, meta2, options);
@@ -59,7 +59,7 @@ export class EntityFactory {
59
59
  wrapped.__initialized = options.initialized;
60
60
  if (options.newEntity || meta.forceConstructor || meta.virtual) {
61
61
  const tmp = { ...data };
62
- meta.constructorParams.forEach(prop => delete tmp[prop]);
62
+ meta.constructorParams?.forEach(prop => delete tmp[prop]);
63
63
  this.hydrate(entity, meta2, tmp, options);
64
64
  // since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
65
65
  // we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
@@ -70,10 +70,16 @@ export class EntityFactory {
70
70
  if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
71
71
  continue;
72
72
  }
73
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
73
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
74
+ Utils.isPlainObject(data[prop.name])) {
74
75
  data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
75
76
  }
76
- data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, { key: prop.name, mode: 'hydration' });
77
+ if (prop.customType instanceof JsonType && this.platform.convertsJsonAutomatically()) {
78
+ data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, {
79
+ key: prop.name,
80
+ mode: 'hydration',
81
+ });
82
+ }
77
83
  }
78
84
  }
79
85
  }
@@ -81,8 +87,7 @@ export class EntityFactory {
81
87
  else {
82
88
  this.hydrate(entity, meta2, data, options);
83
89
  }
84
- wrapped.__touched = false;
85
- if (exists && meta.discriminatorColumn && !(entity instanceof meta2.class)) {
90
+ if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
86
91
  Object.setPrototypeOf(entity, meta2.prototype);
87
92
  }
88
93
  if (options.merge && wrapped.hasPrimaryKey()) {
@@ -108,58 +113,90 @@ export class EntityFactory {
108
113
  data = QueryHelper.processParams(data);
109
114
  const existsData = this.comparator.prepareEntity(entity);
110
115
  const originalEntityData = helper(entity).__originalEntityData ?? {};
111
- const diff = this.comparator.diffEntities(meta.className, originalEntityData, existsData);
116
+ const diff = this.comparator.diffEntities(meta.class, originalEntityData, existsData);
112
117
  // version properties are not part of entity snapshots
113
- if (meta.versionProperty && data[meta.versionProperty] && data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
118
+ if (meta.versionProperty &&
119
+ data[meta.versionProperty] &&
120
+ data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
114
121
  diff[meta.versionProperty] = data[meta.versionProperty];
115
122
  }
116
- const diff2 = this.comparator.diffEntities(meta.className, existsData, data, { includeInverseSides: true });
123
+ const diff2 = this.comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
117
124
  // do not override values changed by user
118
125
  Utils.keys(diff).forEach(key => delete diff2[key]);
119
- Utils.keys(diff2).filter(key => {
126
+ Utils.keys(diff2)
127
+ .filter(key => {
120
128
  // ignore null values if there is already present non-null value
121
129
  if (existsData[key] != null) {
122
130
  return diff2[key] == null;
123
131
  }
124
132
  return diff2[key] === undefined;
125
- }).forEach(key => delete diff2[key]);
133
+ })
134
+ .forEach(key => delete diff2[key]);
126
135
  // but always add collection properties and formulas if they are part of the `data`
127
136
  Utils.keys(data)
128
- .filter(key => meta.properties[key]?.formula || [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
129
- .forEach(key => diff2[key] = data[key]);
137
+ .filter(key => meta.properties[key]?.formula ||
138
+ [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
139
+ .forEach(key => (diff2[key] = data[key]));
130
140
  // rehydrated with the new values, skip those changed by user
131
- this.hydrate(entity, meta, diff2, options);
141
+ // use full hydration if the entity is already initialized, even if the caller used `initialized: false`
142
+ // (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
143
+ const initialized = options.initialized || helper(entity).__initialized;
144
+ this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
132
145
  // we need to update the entity data only with keys that were not present before
133
146
  const nullVal = this.config.get('forceUndefined') ? undefined : null;
134
147
  Utils.keys(diff2).forEach(key => {
135
148
  const prop = meta.properties[key];
136
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
149
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
150
+ Utils.isPlainObject(data[prop.name])) {
151
+ // oxfmt-ignore
137
152
  diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
138
153
  }
154
+ if (!options.convertCustomTypes &&
155
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
156
+ prop.customType?.ensureComparable(meta, prop) &&
157
+ diff2[key] != null) {
158
+ const converted = prop.customType.convertToJSValue(diff2[key], this.platform, { force: true });
159
+ diff2[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { fromQuery: true });
160
+ }
139
161
  originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
140
162
  helper(entity).__loadedProperties.add(key);
141
163
  });
142
164
  // in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
143
165
  meta.relations.forEach(prop => {
144
- if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && Array.isArray(data[prop.name])) {
166
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
167
+ Array.isArray(data[prop.name])) {
145
168
  // instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
146
169
  // we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
147
170
  data[prop.name]
148
171
  .filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
149
- .forEach(child => this.create(prop.type, child, options)); // we can ignore the value, we just care about the `mergeData` call
172
+ .forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
150
173
  return;
151
174
  }
152
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name]) && entity[prop.name] && helper(entity[prop.name]).__initialized) {
153
- this.create(prop.type, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
175
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
176
+ Utils.isPlainObject(data[prop.name]) &&
177
+ entity[prop.name] &&
178
+ helper(entity[prop.name]).__initialized) {
179
+ this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
154
180
  }
155
181
  });
156
- helper(entity).__touched = false;
182
+ this.unitOfWork.normalizeEntityData(meta, originalEntityData);
157
183
  }
158
184
  createReference(entityName, id, options = {}) {
159
185
  options.convertCustomTypes ??= true;
160
- entityName = Utils.className(entityName);
161
186
  const meta = this.metadata.get(entityName);
162
187
  const schema = this.driver.getSchemaName(meta, options);
188
+ // Handle alternate key lookup
189
+ if (options.key) {
190
+ const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
191
+ const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
192
+ if (exists) {
193
+ return exists;
194
+ }
195
+ // Create entity stub - storeByKey will set the alternate key property and store in identity map
196
+ const entity = this.create(entityName, {}, { ...options, initialized: false });
197
+ this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
198
+ return entity;
199
+ }
163
200
  if (meta.simplePK) {
164
201
  const exists = this.unitOfWork.getById(entityName, id, schema);
165
202
  if (exists) {
@@ -182,7 +219,6 @@ export class EntityFactory {
182
219
  return this.create(entityName, id, { ...options, initialized: false });
183
220
  }
184
221
  createEmbeddable(entityName, data, options = {}) {
185
- entityName = Utils.className(entityName);
186
222
  data = { ...data };
187
223
  const meta = this.metadata.get(entityName);
188
224
  const meta2 = this.processDiscriminatorColumn(meta, data);
@@ -194,7 +230,7 @@ export class EntityFactory {
194
230
  createEntity(data, meta, options) {
195
231
  const schema = this.driver.getSchemaName(meta, options);
196
232
  if (options.newEntity || meta.forceConstructor || meta.virtual) {
197
- if (!meta.class) {
233
+ if (meta.polymorphs) {
198
234
  throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
199
235
  }
200
236
  const params = this.extractConstructorParams(meta, data, options);
@@ -240,10 +276,10 @@ export class EntityFactory {
240
276
  }
241
277
  hydrate(entity, meta, data, options) {
242
278
  if (options.initialized) {
243
- this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
279
+ this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
244
280
  }
245
281
  else {
246
- this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
282
+ this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
247
283
  }
248
284
  Utils.keys(data).forEach(key => {
249
285
  helper(entity)?.__loadedProperties.add(key);
@@ -257,75 +293,91 @@ export class EntityFactory {
257
293
  findEntity(data, meta, options) {
258
294
  const schema = this.driver.getSchemaName(meta, options);
259
295
  if (meta.simplePK) {
260
- return this.unitOfWork.getById(meta.className, data[meta.primaryKeys[0]], schema);
296
+ return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
261
297
  }
262
298
  if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
263
299
  return undefined;
264
300
  }
265
301
  const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform, options.convertCustomTypes);
266
- return this.unitOfWork.getById(meta.className, pks, schema);
302
+ return this.unitOfWork.getById(meta.class, pks, schema);
267
303
  }
268
304
  processDiscriminatorColumn(meta, data) {
269
- if (!meta.root.discriminatorColumn) {
305
+ // Handle STI discriminator (persisted column)
306
+ if (meta.root.inheritanceType === 'sti') {
307
+ const prop = meta.properties[meta.root.discriminatorColumn];
308
+ const value = data[prop.name];
309
+ const type = meta.root.discriminatorMap[value];
310
+ meta = type ? this.metadata.get(type) : meta;
270
311
  return meta;
271
312
  }
272
- const prop = meta.properties[meta.root.discriminatorColumn];
273
- const value = data[prop.name];
274
- const type = meta.root.discriminatorMap[value];
275
- meta = type ? this.metadata.find(type) : meta;
313
+ // Handle TPT discriminator (computed at query time)
314
+ if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
315
+ const value = data[meta.root.tptDiscriminatorColumn];
316
+ if (value) {
317
+ const type = meta.root.discriminatorMap[value];
318
+ meta = type ? this.metadata.get(type) : meta;
319
+ }
320
+ }
276
321
  return meta;
277
322
  }
278
323
  /**
279
324
  * denormalize PK to value required by driver (e.g. ObjectId)
280
325
  */
281
- denormalizePrimaryKey(data, primaryKey, prop) {
282
- const pk = this.platform.getSerializedPrimaryKeyField(primaryKey);
283
- if (data[pk] != null || data[primaryKey] != null) {
284
- let id = (data[pk] || data[primaryKey]);
285
- if (prop.type.toLowerCase() === 'objectid') {
286
- id = this.platform.denormalizePrimaryKey(id);
287
- }
288
- delete data[pk];
289
- data[primaryKey] = id;
326
+ denormalizePrimaryKey(meta, data) {
327
+ const pk = meta.getPrimaryProp();
328
+ const spk = meta.properties[meta.serializedPrimaryKey];
329
+ if (!spk?.serializedPrimaryKey) {
330
+ return;
331
+ }
332
+ if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
333
+ data[pk.name] = this.platform.denormalizePrimaryKey((data[spk.name] || data[pk.name]));
334
+ delete data[spk.name];
290
335
  }
291
336
  }
292
337
  /**
293
338
  * returns parameters for entity constructor, creating references from plain ids
294
339
  */
295
340
  extractConstructorParams(meta, data, options) {
341
+ if (!meta.constructorParams) {
342
+ return [data];
343
+ }
296
344
  return meta.constructorParams.map(k => {
297
- if (meta.properties[k] && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[k].kind) && data[k]) {
298
- const pk = Reference.unwrapReference(data[k]);
299
- const entity = this.unitOfWork.getById(meta.properties[k].type, pk, options.schema, true);
345
+ const prop = meta.properties[k];
346
+ const value = data[k];
347
+ if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
348
+ const pk = Reference.unwrapReference(value);
349
+ const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
300
350
  if (entity) {
301
351
  return entity;
302
352
  }
303
- if (Utils.isEntity(data[k])) {
304
- return data[k];
353
+ if (Utils.isEntity(value)) {
354
+ return value;
305
355
  }
306
- const nakedPk = Utils.extractPK(data[k], meta.properties[k].targetMeta, true);
307
- if (Utils.isObject(data[k]) && !nakedPk) {
308
- return this.create(meta.properties[k].type, data[k], options);
356
+ const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
357
+ if (Utils.isObject(value) && !nakedPk) {
358
+ return this.create(prop.targetMeta.class, value, options);
309
359
  }
310
360
  const { newEntity, initialized, ...rest } = options;
311
- const target = this.createReference(meta.properties[k].type, nakedPk, rest);
312
- return Reference.wrapReference(target, meta.properties[k]);
361
+ const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
362
+ return Reference.wrapReference(target, prop);
313
363
  }
314
- if (meta.properties[k]?.kind === ReferenceKind.EMBEDDED && data[k]) {
315
- /* v8 ignore next 3 */
316
- if (Utils.isEntity(data[k])) {
317
- return data[k];
364
+ if (prop?.kind === ReferenceKind.EMBEDDED && value) {
365
+ /* v8 ignore next */
366
+ if (Utils.isEntity(value)) {
367
+ return value;
318
368
  }
319
- return this.createEmbeddable(meta.properties[k].type, data[k], options);
369
+ return this.createEmbeddable(prop.targetMeta.class, value, options);
320
370
  }
321
- if (!meta.properties[k]) {
371
+ if (!prop) {
322
372
  const tmp = { ...data };
323
373
  for (const prop of meta.props) {
324
374
  if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
325
375
  continue;
326
376
  }
327
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(tmp[prop.name]) && !Utils.extractPK(tmp[prop.name], meta.properties[prop.name].targetMeta, true)) {
328
- tmp[prop.name] = Reference.wrapReference(this.create(meta.properties[prop.name].type, tmp[prop.name], options), prop);
377
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
378
+ Utils.isPlainObject(tmp[prop.name]) &&
379
+ !Utils.extractPK(tmp[prop.name], prop.targetMeta, true)) {
380
+ tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
329
381
  }
330
382
  else if (prop.kind === ReferenceKind.SCALAR) {
331
383
  tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.platform);
@@ -333,10 +385,10 @@ export class EntityFactory {
333
385
  }
334
386
  return tmp;
335
387
  }
336
- if (options.convertCustomTypes && meta.properties[k].customType && data[k] != null) {
337
- return meta.properties[k].customType.convertToJSValue(data[k], this.platform);
388
+ if (options.convertCustomTypes && prop.customType && value != null) {
389
+ return prop.customType.convertToJSValue(value, this.platform);
338
390
  }
339
- return data[k];
391
+ return value;
340
392
  });
341
393
  }
342
394
  get unitOfWork() {
@@ -6,9 +6,9 @@ import { type EntityMetadata, type EntityProperty, type IHydrator } from '../typ
6
6
  export declare class EntityHelper {
7
7
  static decorate<T extends object>(meta: EntityMetadata<T>, em: EntityManager): void;
8
8
  /**
9
- * As a performance optimization, we create entity state methods in a lazy manner. We first add
9
+ * As a performance optimization, we create entity state methods lazily. We first add
10
10
  * the `null` value to the prototype to reserve space in memory. Then we define a setter on the
11
- * prototype, that will be executed exactly once per entity instance. There we redefine given
11
+ * prototype that will be executed exactly once per entity instance. There we redefine the given
12
12
  * property on the entity instance, so shadowing the prototype setter.
13
13
  */
14
14
  private static defineBaseProperties;