@mikro-orm/core 7.0.0-dev.2 → 7.0.0-dev.200

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 (210) hide show
  1. package/EntityManager.d.ts +111 -61
  2. package/EntityManager.js +346 -300
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +103 -143
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +8 -7
  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 +80 -35
  16. package/drivers/IDatabaseDriver.d.ts +47 -17
  17. package/entity/BaseEntity.d.ts +2 -2
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +95 -31
  20. package/entity/Collection.js +444 -102
  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 +88 -54
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +38 -15
  27. package/entity/EntityLoader.d.ts +8 -7
  28. package/entity/EntityLoader.js +134 -80
  29. package/entity/EntityRepository.d.ts +24 -4
  30. package/entity/EntityRepository.js +8 -2
  31. package/entity/Reference.d.ts +9 -12
  32. package/entity/Reference.js +34 -9
  33. package/entity/WrappedEntity.d.ts +2 -7
  34. package/entity/WrappedEntity.js +3 -8
  35. package/entity/defineEntity.d.ts +585 -0
  36. package/entity/defineEntity.js +533 -0
  37. package/entity/index.d.ts +3 -2
  38. package/entity/index.js +3 -2
  39. package/entity/utils.d.ts +7 -0
  40. package/entity/utils.js +16 -4
  41. package/entity/validators.d.ts +11 -0
  42. package/entity/validators.js +65 -0
  43. package/enums.d.ts +22 -6
  44. package/enums.js +15 -1
  45. package/errors.d.ts +23 -9
  46. package/errors.js +59 -21
  47. package/events/EventManager.d.ts +2 -1
  48. package/events/EventManager.js +19 -11
  49. package/events/EventSubscriber.d.ts +3 -1
  50. package/hydration/Hydrator.js +1 -2
  51. package/hydration/ObjectHydrator.d.ts +4 -4
  52. package/hydration/ObjectHydrator.js +53 -33
  53. package/index.d.ts +2 -2
  54. package/index.js +1 -2
  55. package/logging/DefaultLogger.d.ts +1 -1
  56. package/logging/DefaultLogger.js +1 -0
  57. package/logging/SimpleLogger.d.ts +1 -1
  58. package/logging/colors.d.ts +1 -1
  59. package/logging/colors.js +7 -6
  60. package/logging/index.d.ts +1 -0
  61. package/logging/index.js +1 -0
  62. package/logging/inspect.d.ts +2 -0
  63. package/logging/inspect.js +11 -0
  64. package/metadata/EntitySchema.d.ts +26 -26
  65. package/metadata/EntitySchema.js +82 -51
  66. package/metadata/MetadataDiscovery.d.ts +7 -10
  67. package/metadata/MetadataDiscovery.js +408 -335
  68. package/metadata/MetadataProvider.d.ts +11 -2
  69. package/metadata/MetadataProvider.js +46 -2
  70. package/metadata/MetadataStorage.d.ts +13 -11
  71. package/metadata/MetadataStorage.js +70 -37
  72. package/metadata/MetadataValidator.d.ts +17 -9
  73. package/metadata/MetadataValidator.js +100 -42
  74. package/metadata/discover-entities.d.ts +5 -0
  75. package/metadata/discover-entities.js +40 -0
  76. package/metadata/index.d.ts +1 -1
  77. package/metadata/index.js +1 -1
  78. package/metadata/types.d.ts +502 -0
  79. package/metadata/types.js +1 -0
  80. package/naming-strategy/AbstractNamingStrategy.d.ts +12 -4
  81. package/naming-strategy/AbstractNamingStrategy.js +14 -2
  82. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  83. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  84. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  85. package/naming-strategy/MongoNamingStrategy.js +6 -6
  86. package/naming-strategy/NamingStrategy.d.ts +24 -4
  87. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  89. package/not-supported.d.ts +2 -0
  90. package/not-supported.js +4 -0
  91. package/package.json +19 -11
  92. package/platforms/ExceptionConverter.js +1 -1
  93. package/platforms/Platform.d.ts +7 -13
  94. package/platforms/Platform.js +20 -43
  95. package/serialization/EntitySerializer.d.ts +5 -0
  96. package/serialization/EntitySerializer.js +47 -27
  97. package/serialization/EntityTransformer.js +28 -18
  98. package/serialization/SerializationContext.d.ts +6 -6
  99. package/serialization/SerializationContext.js +16 -13
  100. package/types/ArrayType.d.ts +1 -1
  101. package/types/ArrayType.js +2 -3
  102. package/types/BigIntType.d.ts +9 -6
  103. package/types/BigIntType.js +4 -1
  104. package/types/BlobType.d.ts +0 -1
  105. package/types/BlobType.js +0 -3
  106. package/types/BooleanType.d.ts +2 -1
  107. package/types/BooleanType.js +3 -0
  108. package/types/DecimalType.d.ts +6 -4
  109. package/types/DecimalType.js +3 -3
  110. package/types/DoubleType.js +2 -2
  111. package/types/EnumArrayType.js +1 -2
  112. package/types/JsonType.d.ts +1 -1
  113. package/types/JsonType.js +7 -2
  114. package/types/TinyIntType.js +1 -1
  115. package/types/Type.d.ts +2 -4
  116. package/types/Type.js +3 -3
  117. package/types/Uint8ArrayType.d.ts +0 -1
  118. package/types/Uint8ArrayType.js +1 -4
  119. package/types/index.d.ts +1 -1
  120. package/typings.d.ts +300 -140
  121. package/typings.js +62 -44
  122. package/unit-of-work/ChangeSet.d.ts +2 -6
  123. package/unit-of-work/ChangeSet.js +4 -5
  124. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  125. package/unit-of-work/ChangeSetComputer.js +26 -13
  126. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  127. package/unit-of-work/ChangeSetPersister.js +77 -35
  128. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  129. package/unit-of-work/CommitOrderCalculator.js +13 -13
  130. package/unit-of-work/IdentityMap.d.ts +12 -0
  131. package/unit-of-work/IdentityMap.js +39 -1
  132. package/unit-of-work/UnitOfWork.d.ts +23 -3
  133. package/unit-of-work/UnitOfWork.js +199 -106
  134. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  135. package/utils/AbstractSchemaGenerator.js +22 -17
  136. package/utils/AsyncContext.d.ts +6 -0
  137. package/utils/AsyncContext.js +42 -0
  138. package/utils/Configuration.d.ts +779 -207
  139. package/utils/Configuration.js +146 -190
  140. package/utils/ConfigurationLoader.d.ts +1 -54
  141. package/utils/ConfigurationLoader.js +1 -352
  142. package/utils/Cursor.d.ts +3 -6
  143. package/utils/Cursor.js +27 -11
  144. package/utils/DataloaderUtils.d.ts +15 -5
  145. package/utils/DataloaderUtils.js +65 -17
  146. package/utils/EntityComparator.d.ts +13 -9
  147. package/utils/EntityComparator.js +164 -89
  148. package/utils/QueryHelper.d.ts +14 -6
  149. package/utils/QueryHelper.js +88 -26
  150. package/utils/RawQueryFragment.d.ts +48 -25
  151. package/utils/RawQueryFragment.js +67 -66
  152. package/utils/RequestContext.js +2 -2
  153. package/utils/TransactionContext.js +2 -2
  154. package/utils/TransactionManager.d.ts +65 -0
  155. package/utils/TransactionManager.js +223 -0
  156. package/utils/Utils.d.ts +13 -120
  157. package/utils/Utils.js +104 -375
  158. package/utils/clone.js +8 -23
  159. package/utils/env-vars.d.ts +7 -0
  160. package/utils/env-vars.js +97 -0
  161. package/utils/fs-utils.d.ts +32 -0
  162. package/utils/fs-utils.js +178 -0
  163. package/utils/index.d.ts +2 -1
  164. package/utils/index.js +2 -1
  165. package/utils/upsert-utils.d.ts +9 -4
  166. package/utils/upsert-utils.js +55 -4
  167. package/decorators/Check.d.ts +0 -3
  168. package/decorators/Check.js +0 -13
  169. package/decorators/CreateRequestContext.d.ts +0 -3
  170. package/decorators/CreateRequestContext.js +0 -29
  171. package/decorators/Embeddable.d.ts +0 -8
  172. package/decorators/Embeddable.js +0 -11
  173. package/decorators/Embedded.d.ts +0 -18
  174. package/decorators/Embedded.js +0 -18
  175. package/decorators/Entity.d.ts +0 -18
  176. package/decorators/Entity.js +0 -13
  177. package/decorators/Enum.d.ts +0 -9
  178. package/decorators/Enum.js +0 -16
  179. package/decorators/Filter.d.ts +0 -2
  180. package/decorators/Filter.js +0 -8
  181. package/decorators/Formula.d.ts +0 -5
  182. package/decorators/Formula.js +0 -15
  183. package/decorators/Indexed.d.ts +0 -17
  184. package/decorators/Indexed.js +0 -20
  185. package/decorators/ManyToMany.d.ts +0 -40
  186. package/decorators/ManyToMany.js +0 -14
  187. package/decorators/ManyToOne.d.ts +0 -30
  188. package/decorators/ManyToOne.js +0 -14
  189. package/decorators/OneToMany.d.ts +0 -28
  190. package/decorators/OneToMany.js +0 -17
  191. package/decorators/OneToOne.d.ts +0 -24
  192. package/decorators/OneToOne.js +0 -7
  193. package/decorators/PrimaryKey.d.ts +0 -9
  194. package/decorators/PrimaryKey.js +0 -20
  195. package/decorators/Property.d.ts +0 -250
  196. package/decorators/Property.js +0 -32
  197. package/decorators/Transactional.d.ts +0 -13
  198. package/decorators/Transactional.js +0 -28
  199. package/decorators/hooks.d.ts +0 -16
  200. package/decorators/hooks.js +0 -47
  201. package/decorators/index.d.ts +0 -17
  202. package/decorators/index.js +0 -17
  203. package/entity/ArrayCollection.d.ts +0 -116
  204. package/entity/ArrayCollection.js +0 -395
  205. package/entity/EntityValidator.d.ts +0 -19
  206. package/entity/EntityValidator.js +0 -150
  207. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  208. package/metadata/ReflectMetadataProvider.js +0 -44
  209. package/utils/resolveContextProvider.d.ts +0 -10
  210. 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;
@@ -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
86
  if (exists && meta.discriminatorColumn && !(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 => {
@@ -133,6 +137,10 @@ export class EntityFactory {
133
137
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
134
138
  diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
135
139
  }
140
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) && prop.customType?.ensureComparable(meta, prop) && diff2[key] != null) {
141
+ const converted = prop.customType.convertToJSValue(diff2[key], this.platform, { force: true });
142
+ diff2[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { fromQuery: true });
143
+ }
136
144
  originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
137
145
  helper(entity).__loadedProperties.add(key);
138
146
  });
@@ -143,20 +151,31 @@ export class EntityFactory {
143
151
  // we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
144
152
  data[prop.name]
145
153
  .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
154
+ .forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
147
155
  return;
148
156
  }
149
157
  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
158
+ this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
151
159
  }
152
160
  });
153
- helper(entity).__touched = false;
161
+ this.unitOfWork.normalizeEntityData(meta, originalEntityData);
154
162
  }
155
163
  createReference(entityName, id, options = {}) {
156
164
  options.convertCustomTypes ??= true;
157
- entityName = Utils.className(entityName);
158
165
  const meta = this.metadata.get(entityName);
159
166
  const schema = this.driver.getSchemaName(meta, options);
167
+ // Handle alternate key lookup
168
+ if (options.key) {
169
+ const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
170
+ const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
171
+ if (exists) {
172
+ return exists;
173
+ }
174
+ // Create entity stub - storeByKey will set the alternate key property and store in identity map
175
+ const entity = this.create(entityName, {}, { ...options, initialized: false });
176
+ this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
177
+ return entity;
178
+ }
160
179
  if (meta.simplePK) {
161
180
  const exists = this.unitOfWork.getById(entityName, id, schema);
162
181
  if (exists) {
@@ -168,8 +187,8 @@ export class EntityFactory {
168
187
  if (Array.isArray(id)) {
169
188
  id = Utils.getPrimaryKeyCondFromArray(id, meta);
170
189
  }
171
- const pks = Utils.getOrderedPrimaryKeys(id, meta, this.platform, options.convertCustomTypes);
172
- const exists = this.unitOfWork.getById(entityName, pks, schema);
190
+ const pks = Utils.getOrderedPrimaryKeys(id, meta, this.platform);
191
+ const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
173
192
  if (exists) {
174
193
  return exists;
175
194
  }
@@ -179,7 +198,6 @@ export class EntityFactory {
179
198
  return this.create(entityName, id, { ...options, initialized: false });
180
199
  }
181
200
  createEmbeddable(entityName, data, options = {}) {
182
- entityName = Utils.className(entityName);
183
201
  data = { ...data };
184
202
  const meta = this.metadata.get(entityName);
185
203
  const meta2 = this.processDiscriminatorColumn(meta, data);
@@ -191,7 +209,7 @@ export class EntityFactory {
191
209
  createEntity(data, meta, options) {
192
210
  const schema = this.driver.getSchemaName(meta, options);
193
211
  if (options.newEntity || meta.forceConstructor || meta.virtual) {
194
- if (!meta.class) {
212
+ if (meta.polymorphs) {
195
213
  throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
196
214
  }
197
215
  const params = this.extractConstructorParams(meta, data, options);
@@ -228,28 +246,39 @@ export class EntityFactory {
228
246
  }
229
247
  return entity;
230
248
  }
249
+ assignDefaultValues(entity, meta) {
250
+ for (const prop of meta.props) {
251
+ if (prop.onCreate) {
252
+ entity[prop.name] ??= prop.onCreate(entity, this.em);
253
+ }
254
+ }
255
+ }
231
256
  hydrate(entity, meta, data, options) {
232
257
  if (options.initialized) {
233
- this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
258
+ this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
234
259
  }
235
260
  else {
236
- this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
261
+ this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
237
262
  }
238
263
  Utils.keys(data).forEach(key => {
239
264
  helper(entity)?.__loadedProperties.add(key);
240
265
  helper(entity)?.__serializationContext.fields?.add(key);
241
266
  });
267
+ const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.config.get('processOnCreateHooksEarly');
268
+ if (options.newEntity && processOnCreateHooksEarly) {
269
+ this.assignDefaultValues(entity, meta);
270
+ }
242
271
  }
243
272
  findEntity(data, meta, options) {
244
273
  const schema = this.driver.getSchemaName(meta, options);
245
274
  if (meta.simplePK) {
246
- return this.unitOfWork.getById(meta.className, data[meta.primaryKeys[0]], schema);
275
+ return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
247
276
  }
248
277
  if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
249
278
  return undefined;
250
279
  }
251
- const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform);
252
- return this.unitOfWork.getById(meta.className, pks, schema);
280
+ const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform, options.convertCustomTypes);
281
+ return this.unitOfWork.getById(meta.class, pks, schema);
253
282
  }
254
283
  processDiscriminatorColumn(meta, data) {
255
284
  if (!meta.root.discriminatorColumn) {
@@ -258,60 +287,65 @@ export class EntityFactory {
258
287
  const prop = meta.properties[meta.root.discriminatorColumn];
259
288
  const value = data[prop.name];
260
289
  const type = meta.root.discriminatorMap[value];
261
- meta = type ? this.metadata.find(type) : meta;
290
+ meta = type ? this.metadata.get(type) : meta;
262
291
  return meta;
263
292
  }
264
293
  /**
265
294
  * denormalize PK to value required by driver (e.g. ObjectId)
266
295
  */
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;
296
+ denormalizePrimaryKey(meta, data) {
297
+ const pk = meta.getPrimaryProp();
298
+ const spk = meta.properties[meta.serializedPrimaryKey];
299
+ if (!spk?.serializedPrimaryKey) {
300
+ return;
301
+ }
302
+ if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
303
+ data[pk.name] = this.platform.denormalizePrimaryKey((data[spk.name] || data[pk.name]));
304
+ delete data[spk.name];
276
305
  }
277
306
  }
278
307
  /**
279
308
  * returns parameters for entity constructor, creating references from plain ids
280
309
  */
281
310
  extractConstructorParams(meta, data, options) {
311
+ if (!meta.constructorParams) {
312
+ return [data];
313
+ }
282
314
  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);
315
+ const prop = meta.properties[k];
316
+ const value = data[k];
317
+ if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
318
+ const pk = Reference.unwrapReference(value);
319
+ const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
286
320
  if (entity) {
287
321
  return entity;
288
322
  }
289
- if (Utils.isEntity(data[k])) {
290
- return data[k];
323
+ if (Utils.isEntity(value)) {
324
+ return value;
291
325
  }
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);
326
+ const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
327
+ if (Utils.isObject(value) && !nakedPk) {
328
+ return this.create(prop.targetMeta.class, value, options);
295
329
  }
296
330
  const { newEntity, initialized, ...rest } = options;
297
- const target = this.createReference(meta.properties[k].type, nakedPk, rest);
298
- return Reference.wrapReference(target, meta.properties[k]);
331
+ const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
332
+ return Reference.wrapReference(target, prop);
299
333
  }
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];
334
+ if (prop?.kind === ReferenceKind.EMBEDDED && value) {
335
+ /* v8 ignore next */
336
+ if (Utils.isEntity(value)) {
337
+ return value;
304
338
  }
305
- return this.createEmbeddable(meta.properties[k].type, data[k], options);
339
+ return this.createEmbeddable(prop.targetMeta.class, value, options);
306
340
  }
307
- if (!meta.properties[k]) {
341
+ if (!prop) {
308
342
  const tmp = { ...data };
309
343
  for (const prop of meta.props) {
310
344
  if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
311
345
  continue;
312
346
  }
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);
347
+ 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)) {
348
+ tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
315
349
  }
316
350
  else if (prop.kind === ReferenceKind.SCALAR) {
317
351
  tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.platform);
@@ -319,10 +353,10 @@ export class EntityFactory {
319
353
  }
320
354
  return tmp;
321
355
  }
322
- if (options.convertCustomTypes && meta.properties[k].customType && data[k] != null) {
323
- return meta.properties[k].customType.convertToJSValue(data[k], this.platform);
356
+ if (options.convertCustomTypes && prop.customType && value != null) {
357
+ return prop.customType.convertToJSValue(value, this.platform);
324
358
  }
325
- return data[k];
359
+ return value;
326
360
  });
327
361
  }
328
362
  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;
@@ -1,4 +1,3 @@
1
- import { inspect } from 'node:util';
2
1
  import { EagerProps, EntityRepositoryType, HiddenProps, OptionalProps, PrimaryKeyProp, } from '../typings.js';
3
2
  import { EntityTransformer } from '../serialization/EntityTransformer.js';
4
3
  import { Reference } from './Reference.js';
@@ -6,6 +5,8 @@ import { Utils } from '../utils/Utils.js';
6
5
  import { WrappedEntity } from './WrappedEntity.js';
7
6
  import { ReferenceKind } from '../enums.js';
8
7
  import { helper } from './wrap.js';
8
+ import { inspect } from '../logging/inspect.js';
9
+ import { getEnv } from '../utils/env-vars.js';
9
10
  /**
10
11
  * @internal
11
12
  */
@@ -32,14 +33,14 @@ export class EntityHelper {
32
33
  const prototype = meta.prototype;
33
34
  if (!prototype.toJSON) { // toJSON can be overridden
34
35
  prototype.toJSON = function (...args) {
35
- return EntityTransformer.toObject(this, ...args.slice(meta.toJsonParams.length));
36
+ return EntityTransformer.toObject(this, ...args);
36
37
  };
37
38
  }
38
39
  }
39
40
  /**
40
- * As a performance optimization, we create entity state methods in a lazy manner. We first add
41
+ * As a performance optimization, we create entity state methods lazily. We first add
41
42
  * the `null` value to the prototype to reserve space in memory. Then we define a setter on the
42
- * prototype, that will be executed exactly once per entity instance. There we redefine given
43
+ * prototype that will be executed exactly once per entity instance. There we redefine the given
43
44
  * property on the entity instance, so shadowing the prototype setter.
44
45
  */
45
46
  static defineBaseProperties(meta, prototype, em) {
@@ -87,7 +88,7 @@ export class EntityHelper {
87
88
  });
88
89
  return;
89
90
  }
90
- if (prop.inherited || prop.primary || prop.persist === false || prop.trackChanges === false || prop.embedded || isCollection) {
91
+ if (prop.inherited || prop.primary || prop.accessor || prop.persist === false || prop.embedded || isCollection) {
91
92
  return;
92
93
  }
93
94
  Object.defineProperty(meta.prototype, prop.name, {
@@ -98,13 +99,11 @@ export class EntityHelper {
98
99
  },
99
100
  set(val) {
100
101
  this.__helper.__data[prop.name] = val;
101
- this.__helper.__touched = !this.__helper.hydrator.isRunning();
102
102
  },
103
103
  enumerable: true,
104
104
  configurable: true,
105
105
  });
106
106
  this.__helper.__data[prop.name] = val;
107
- this.__helper.__touched = !this.__helper.hydrator.isRunning();
108
107
  },
109
108
  configurable: true,
110
109
  });
@@ -112,16 +111,27 @@ export class EntityHelper {
112
111
  }
113
112
  static defineCustomInspect(meta) {
114
113
  // @ts-ignore
115
- meta.prototype[inspect.custom] ??= function (depth = 2) {
116
- const object = { ...this };
114
+ meta.prototype[Symbol.for('nodejs.util.inspect.custom')] ??= function (depth = 2) {
115
+ const object = {};
116
+ const keys = new Set(Utils.keys(this));
117
+ for (const prop of meta.props) {
118
+ if (keys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
119
+ object[prop.name] = this[prop.name];
120
+ }
121
+ }
122
+ for (const key of keys) {
123
+ if (!meta.properties[key]) {
124
+ object[key] = this[key];
125
+ }
126
+ }
117
127
  // ensure we dont have internal symbols in the POJO
118
128
  [OptionalProps, EntityRepositoryType, PrimaryKeyProp, EagerProps, HiddenProps].forEach(sym => delete object[sym]);
119
129
  meta.props
120
130
  .filter(prop => object[prop.name] === undefined)
121
131
  .forEach(prop => delete object[prop.name]);
122
132
  const ret = inspect(object, { depth });
123
- let name = (this).constructor.name;
124
- const showEM = ['true', 't', '1'].includes(process.env.MIKRO_ORM_LOG_EM_ID?.toString().toLowerCase() ?? '');
133
+ let name = this.constructor.name;
134
+ const showEM = ['true', 't', '1'].includes(getEnv('MIKRO_ORM_LOG_EM_ID')?.toLowerCase() ?? '');
125
135
  if (showEM) {
126
136
  if (helper(this).__em) {
127
137
  name += ` [managed by ${helper(this).__em.id}]`;
@@ -146,13 +156,13 @@ export class EntityHelper {
146
156
  set(val) {
147
157
  const entity = Reference.unwrapReference(val ?? wrapped.__data[prop.name]);
148
158
  const old = Reference.unwrapReference(wrapped.__data[prop.name]);
159
+ if (old && old !== entity && prop.kind === ReferenceKind.MANY_TO_ONE && prop.inversedBy && old[prop.inversedBy]) {
160
+ old[prop.inversedBy].removeWithoutPropagation(this);
161
+ }
149
162
  wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
150
163
  // when propagation from inside hydration, we set the FK to the entity data immediately
151
164
  if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
152
- wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta.primaryKeys, true);
153
- }
154
- else {
155
- wrapped.__touched = !hydrator.isRunning();
165
+ wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta, true);
156
166
  }
157
167
  EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
158
168
  },
@@ -169,6 +179,13 @@ export class EntityHelper {
169
179
  continue;
170
180
  }
171
181
  const inverse = value?.[prop2.name];
182
+ if (prop.ref && owner[prop.name]) {
183
+ // eslint-disable-next-line dot-notation
184
+ owner[prop.name]['property'] = prop;
185
+ }
186
+ if (Utils.isCollection(inverse) && inverse.isPartial()) {
187
+ continue;
188
+ }
172
189
  if (prop.kind === ReferenceKind.MANY_TO_ONE && Utils.isCollection(inverse) && inverse.isInitialized()) {
173
190
  inverse.addWithoutPropagation(owner);
174
191
  helper(owner).__em?.getUnitOfWork().cancelOrphanRemoval(owner);
@@ -196,6 +213,11 @@ export class EntityHelper {
196
213
  helper(other).__em?.getUnitOfWork().scheduleOrphanRemoval(other);
197
214
  }
198
215
  }
216
+ // Skip setting the inverse side to null if it's a primary key - the entity will be removed via orphan removal
217
+ // Setting a primary key to null would corrupt the entity and cause validation errors
218
+ if (value == null && prop.orphanRemoval && prop2.primary) {
219
+ return;
220
+ }
199
221
  if (value == null) {
200
222
  entity[prop2.name] = value;
201
223
  }
@@ -207,6 +229,7 @@ export class EntityHelper {
207
229
  }
208
230
  if (old?.[prop2.name] != null) {
209
231
  delete helper(old).__data[prop2.name];
232
+ old[prop2.name] = null;
210
233
  }
211
234
  }
212
235
  static ensurePropagation(entity) {