@mikro-orm/core 7.0.0-dev.3 → 7.0.0-dev.300

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