@mikro-orm/core 7.0.2-dev.9 → 7.0.2

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 (207) hide show
  1. package/EntityManager.d.ts +883 -579
  2. package/EntityManager.js +1897 -1865
  3. package/MikroORM.d.ts +103 -72
  4. package/MikroORM.js +178 -177
  5. package/README.md +128 -294
  6. package/cache/CacheAdapter.d.ts +38 -36
  7. package/cache/FileCacheAdapter.d.ts +30 -24
  8. package/cache/FileCacheAdapter.js +80 -78
  9. package/cache/GeneratedCacheAdapter.d.ts +19 -20
  10. package/cache/GeneratedCacheAdapter.js +31 -30
  11. package/cache/MemoryCacheAdapter.d.ts +19 -20
  12. package/cache/MemoryCacheAdapter.js +36 -36
  13. package/cache/NullCacheAdapter.d.ts +17 -16
  14. package/cache/NullCacheAdapter.js +25 -24
  15. package/connections/Connection.d.ts +99 -75
  16. package/connections/Connection.js +166 -160
  17. package/drivers/DatabaseDriver.d.ts +187 -69
  18. package/drivers/DatabaseDriver.js +451 -432
  19. package/drivers/IDatabaseDriver.d.ts +464 -281
  20. package/drivers/IDatabaseDriver.js +1 -0
  21. package/entity/BaseEntity.d.ts +121 -73
  22. package/entity/BaseEntity.js +44 -33
  23. package/entity/Collection.d.ts +216 -157
  24. package/entity/Collection.js +728 -707
  25. package/entity/EntityAssigner.d.ts +90 -76
  26. package/entity/EntityAssigner.js +232 -229
  27. package/entity/EntityFactory.d.ts +68 -40
  28. package/entity/EntityFactory.js +427 -366
  29. package/entity/EntityHelper.d.ts +34 -22
  30. package/entity/EntityHelper.js +280 -267
  31. package/entity/EntityIdentifier.d.ts +4 -4
  32. package/entity/EntityIdentifier.js +10 -10
  33. package/entity/EntityLoader.d.ts +105 -56
  34. package/entity/EntityLoader.js +754 -722
  35. package/entity/EntityRepository.d.ts +317 -200
  36. package/entity/EntityRepository.js +214 -212
  37. package/entity/PolymorphicRef.d.ts +5 -5
  38. package/entity/PolymorphicRef.js +10 -10
  39. package/entity/Reference.d.ts +130 -66
  40. package/entity/Reference.js +280 -260
  41. package/entity/WrappedEntity.d.ts +116 -53
  42. package/entity/WrappedEntity.js +169 -147
  43. package/entity/defineEntity.d.ts +1290 -614
  44. package/entity/defineEntity.js +521 -511
  45. package/entity/utils.d.ts +13 -3
  46. package/entity/utils.js +71 -73
  47. package/entity/validators.js +43 -43
  48. package/entity/wrap.js +8 -8
  49. package/enums.d.ts +275 -138
  50. package/enums.js +268 -137
  51. package/errors.d.ts +120 -72
  52. package/errors.js +356 -253
  53. package/events/EventManager.d.ts +27 -10
  54. package/events/EventManager.js +80 -73
  55. package/events/EventSubscriber.d.ts +33 -29
  56. package/events/TransactionEventBroadcaster.d.ts +16 -7
  57. package/events/TransactionEventBroadcaster.js +15 -13
  58. package/exceptions.d.ts +23 -40
  59. package/exceptions.js +35 -52
  60. package/hydration/Hydrator.d.ts +43 -16
  61. package/hydration/Hydrator.js +44 -42
  62. package/hydration/ObjectHydrator.d.ts +51 -17
  63. package/hydration/ObjectHydrator.js +480 -416
  64. package/index.d.ts +116 -2
  65. package/index.js +10 -1
  66. package/logging/DefaultLogger.d.ts +35 -30
  67. package/logging/DefaultLogger.js +87 -84
  68. package/logging/Logger.d.ts +45 -40
  69. package/logging/SimpleLogger.d.ts +13 -11
  70. package/logging/SimpleLogger.js +22 -22
  71. package/logging/colors.d.ts +6 -6
  72. package/logging/colors.js +11 -10
  73. package/logging/inspect.js +7 -7
  74. package/metadata/EntitySchema.d.ts +214 -108
  75. package/metadata/EntitySchema.js +398 -379
  76. package/metadata/MetadataDiscovery.d.ts +115 -111
  77. package/metadata/MetadataDiscovery.js +1948 -1857
  78. package/metadata/MetadataProvider.d.ts +25 -14
  79. package/metadata/MetadataProvider.js +83 -77
  80. package/metadata/MetadataStorage.d.ts +39 -19
  81. package/metadata/MetadataStorage.js +119 -106
  82. package/metadata/MetadataValidator.d.ts +39 -39
  83. package/metadata/MetadataValidator.js +381 -338
  84. package/metadata/discover-entities.d.ts +5 -2
  85. package/metadata/discover-entities.js +27 -27
  86. package/metadata/types.d.ts +615 -531
  87. package/naming-strategy/AbstractNamingStrategy.d.ts +55 -39
  88. package/naming-strategy/AbstractNamingStrategy.js +91 -85
  89. package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
  90. package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
  91. package/naming-strategy/MongoNamingStrategy.d.ts +7 -6
  92. package/naming-strategy/MongoNamingStrategy.js +19 -18
  93. package/naming-strategy/NamingStrategy.d.ts +109 -99
  94. package/naming-strategy/UnderscoreNamingStrategy.d.ts +8 -7
  95. package/naming-strategy/UnderscoreNamingStrategy.js +22 -21
  96. package/not-supported.js +7 -4
  97. package/package.json +1 -1
  98. package/platforms/ExceptionConverter.d.ts +2 -1
  99. package/platforms/ExceptionConverter.js +5 -4
  100. package/platforms/Platform.d.ts +310 -236
  101. package/platforms/Platform.js +661 -573
  102. package/serialization/EntitySerializer.d.ts +49 -25
  103. package/serialization/EntitySerializer.js +224 -216
  104. package/serialization/EntityTransformer.d.ts +11 -5
  105. package/serialization/EntityTransformer.js +220 -216
  106. package/serialization/SerializationContext.d.ts +27 -18
  107. package/serialization/SerializationContext.js +105 -100
  108. package/types/ArrayType.d.ts +9 -8
  109. package/types/ArrayType.js +34 -33
  110. package/types/BigIntType.d.ts +17 -10
  111. package/types/BigIntType.js +37 -37
  112. package/types/BlobType.d.ts +4 -3
  113. package/types/BlobType.js +14 -13
  114. package/types/BooleanType.d.ts +5 -4
  115. package/types/BooleanType.js +13 -12
  116. package/types/CharacterType.d.ts +3 -2
  117. package/types/CharacterType.js +7 -6
  118. package/types/DateTimeType.d.ts +6 -5
  119. package/types/DateTimeType.js +16 -15
  120. package/types/DateType.d.ts +6 -5
  121. package/types/DateType.js +16 -15
  122. package/types/DecimalType.d.ts +7 -7
  123. package/types/DecimalType.js +26 -26
  124. package/types/DoubleType.d.ts +3 -3
  125. package/types/DoubleType.js +12 -12
  126. package/types/EnumArrayType.d.ts +6 -5
  127. package/types/EnumArrayType.js +25 -24
  128. package/types/EnumType.d.ts +4 -3
  129. package/types/EnumType.js +12 -11
  130. package/types/FloatType.d.ts +4 -3
  131. package/types/FloatType.js +10 -9
  132. package/types/IntegerType.d.ts +4 -3
  133. package/types/IntegerType.js +10 -9
  134. package/types/IntervalType.d.ts +5 -4
  135. package/types/IntervalType.js +13 -12
  136. package/types/JsonType.d.ts +9 -8
  137. package/types/JsonType.js +33 -32
  138. package/types/MediumIntType.d.ts +2 -1
  139. package/types/MediumIntType.js +4 -3
  140. package/types/SmallIntType.d.ts +4 -3
  141. package/types/SmallIntType.js +10 -9
  142. package/types/StringType.d.ts +5 -4
  143. package/types/StringType.js +13 -12
  144. package/types/TextType.d.ts +4 -3
  145. package/types/TextType.js +10 -9
  146. package/types/TimeType.d.ts +6 -5
  147. package/types/TimeType.js +18 -17
  148. package/types/TinyIntType.d.ts +4 -3
  149. package/types/TinyIntType.js +11 -10
  150. package/types/Type.d.ts +88 -73
  151. package/types/Type.js +85 -74
  152. package/types/Uint8ArrayType.d.ts +5 -4
  153. package/types/Uint8ArrayType.js +22 -21
  154. package/types/UnknownType.d.ts +5 -4
  155. package/types/UnknownType.js +13 -12
  156. package/types/UuidType.d.ts +6 -5
  157. package/types/UuidType.js +20 -19
  158. package/types/index.d.ts +77 -49
  159. package/types/index.js +64 -26
  160. package/typings.d.ts +1388 -729
  161. package/typings.js +255 -231
  162. package/unit-of-work/ChangeSet.d.ts +28 -24
  163. package/unit-of-work/ChangeSet.js +58 -54
  164. package/unit-of-work/ChangeSetComputer.d.ts +13 -11
  165. package/unit-of-work/ChangeSetComputer.js +180 -159
  166. package/unit-of-work/ChangeSetPersister.d.ts +64 -41
  167. package/unit-of-work/ChangeSetPersister.js +443 -418
  168. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  169. package/unit-of-work/CommitOrderCalculator.js +89 -88
  170. package/unit-of-work/IdentityMap.d.ts +32 -25
  171. package/unit-of-work/IdentityMap.js +106 -99
  172. package/unit-of-work/UnitOfWork.d.ts +182 -127
  173. package/unit-of-work/UnitOfWork.js +1201 -1169
  174. package/utils/AbstractMigrator.d.ts +111 -91
  175. package/utils/AbstractMigrator.js +275 -275
  176. package/utils/AbstractSchemaGenerator.d.ts +43 -34
  177. package/utils/AbstractSchemaGenerator.js +121 -122
  178. package/utils/AsyncContext.d.ts +3 -3
  179. package/utils/AsyncContext.js +34 -35
  180. package/utils/Configuration.d.ts +853 -801
  181. package/utils/Configuration.js +360 -337
  182. package/utils/Cursor.d.ts +40 -22
  183. package/utils/Cursor.js +135 -127
  184. package/utils/DataloaderUtils.d.ts +58 -43
  185. package/utils/DataloaderUtils.js +203 -198
  186. package/utils/EntityComparator.d.ts +99 -80
  187. package/utils/EntityComparator.js +825 -727
  188. package/utils/NullHighlighter.d.ts +2 -1
  189. package/utils/NullHighlighter.js +4 -3
  190. package/utils/QueryHelper.d.ts +79 -51
  191. package/utils/QueryHelper.js +372 -361
  192. package/utils/RawQueryFragment.d.ts +54 -28
  193. package/utils/RawQueryFragment.js +110 -99
  194. package/utils/RequestContext.d.ts +33 -32
  195. package/utils/RequestContext.js +52 -53
  196. package/utils/TransactionContext.d.ts +17 -16
  197. package/utils/TransactionContext.js +28 -27
  198. package/utils/TransactionManager.d.ts +58 -58
  199. package/utils/TransactionManager.js +199 -197
  200. package/utils/Utils.d.ts +210 -145
  201. package/utils/Utils.js +820 -813
  202. package/utils/clone.js +104 -113
  203. package/utils/env-vars.js +90 -88
  204. package/utils/fs-utils.d.ts +15 -15
  205. package/utils/fs-utils.js +180 -181
  206. package/utils/upsert-utils.d.ts +20 -5
  207. package/utils/upsert-utils.js +114 -116
@@ -5,393 +5,454 @@ import { Reference } from './Reference.js';
5
5
  import { helper } from './wrap.js';
6
6
  import { EntityHelper } from './EntityHelper.js';
7
7
  import { JsonType } from '../types/JsonType.js';
8
+ import { isRaw } from '../utils/RawQueryFragment.js';
9
+ /** @internal Factory responsible for creating, merging, and hydrating entity instances. */
8
10
  export class EntityFactory {
9
- #driver;
10
- #platform;
11
- #config;
12
- #metadata;
13
- #hydrator;
14
- #eventManager;
15
- #comparator;
16
- #em;
17
- constructor(em) {
18
- this.#em = em;
19
- this.#driver = this.#em.getDriver();
20
- this.#platform = this.#driver.getPlatform();
21
- this.#config = this.#em.config;
22
- this.#metadata = this.#em.getMetadata();
23
- this.#hydrator = this.#config.getHydrator(this.#metadata);
24
- this.#eventManager = this.#em.getEventManager();
25
- this.#comparator = this.#em.getComparator();
11
+ #driver;
12
+ #platform;
13
+ #config;
14
+ #metadata;
15
+ #hydrator;
16
+ #eventManager;
17
+ #comparator;
18
+ #em;
19
+ constructor(em) {
20
+ this.#em = em;
21
+ this.#driver = this.#em.getDriver();
22
+ this.#platform = this.#driver.getPlatform();
23
+ this.#config = this.#em.config;
24
+ this.#metadata = this.#em.getMetadata();
25
+ this.#hydrator = this.#config.getHydrator(this.#metadata);
26
+ this.#eventManager = this.#em.getEventManager();
27
+ this.#comparator = this.#em.getComparator();
28
+ }
29
+ /** Creates a new entity instance or returns an existing one from the identity map, hydrating it with the provided data. */
30
+ create(entityName, data, options = {}) {
31
+ data = Reference.unwrapReference(data);
32
+ options.initialized ??= true;
33
+ if (data.__entity) {
34
+ return data;
26
35
  }
27
- create(entityName, data, options = {}) {
28
- data = Reference.unwrapReference(data);
29
- options.initialized ??= true;
30
- if (data.__entity) {
31
- return data;
32
- }
33
- const meta = this.#metadata.get(entityName);
34
- if (meta.virtual) {
35
- data = { ...data };
36
- const entity = this.createEntity(data, meta, options);
37
- this.hydrate(entity, meta, data, options);
38
- return entity;
39
- }
40
- if (meta.serializedPrimaryKey) {
41
- this.denormalizePrimaryKey(meta, data);
42
- }
43
- const meta2 = this.processDiscriminatorColumn(meta, data);
44
- const exists = this.findEntity(data, meta2, options);
45
- let wrapped = exists && helper(exists);
46
- if (wrapped && !options.refresh) {
47
- wrapped.__processing = true;
48
- Utils.dropUndefinedProperties(data);
49
- this.mergeData(meta2, exists, data, options);
50
- wrapped.__processing = false;
51
- if (wrapped.isInitialized()) {
52
- return exists;
36
+ const meta = this.#metadata.get(entityName);
37
+ if (meta.virtual) {
38
+ data = { ...data };
39
+ const entity = this.createEntity(data, meta, options);
40
+ this.hydrate(entity, meta, data, options);
41
+ return entity;
42
+ }
43
+ if (meta.serializedPrimaryKey) {
44
+ this.denormalizePrimaryKey(meta, data);
45
+ }
46
+ const meta2 = this.processDiscriminatorColumn(meta, data);
47
+ const exists = this.findEntity(data, meta2, options);
48
+ let wrapped = exists && helper(exists);
49
+ if (wrapped && !options.refresh) {
50
+ wrapped.__processing = true;
51
+ Utils.dropUndefinedProperties(data);
52
+ this.mergeData(meta2, exists, data, options);
53
+ wrapped.__processing = false;
54
+ if (wrapped.isInitialized()) {
55
+ return exists;
56
+ }
57
+ }
58
+ data = { ...data };
59
+ const entity = exists ?? this.createEntity(data, meta2, options);
60
+ wrapped = helper(entity);
61
+ wrapped.__processing = true;
62
+ wrapped.__initialized = options.initialized;
63
+ if (options.newEntity || meta.forceConstructor || meta.virtual) {
64
+ const tmp = { ...data };
65
+ meta.constructorParams?.forEach(prop => delete tmp[prop]);
66
+ this.hydrate(entity, meta2, tmp, options);
67
+ // since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
68
+ // we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
69
+ // even if they are not part of constructor parameters (as this is otherwise normalized during hydration, here only in `tmp`)
70
+ if (options.convertCustomTypes) {
71
+ for (const prop of meta.props) {
72
+ if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
73
+ if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
74
+ continue;
53
75
  }
54
- }
55
- data = { ...data };
56
- const entity = exists ?? this.createEntity(data, meta2, options);
57
- wrapped = helper(entity);
58
- wrapped.__processing = true;
59
- wrapped.__initialized = options.initialized;
60
- if (options.newEntity || meta.forceConstructor || meta.virtual) {
61
- const tmp = { ...data };
62
- meta.constructorParams?.forEach(prop => delete tmp[prop]);
63
- this.hydrate(entity, meta2, tmp, options);
64
- // since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
65
- // we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
66
- // even if they are not part of constructor parameters (as this is otherwise normalized during hydration, here only in `tmp`)
67
- if (options.convertCustomTypes) {
68
- for (const prop of meta.props) {
69
- if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
70
- if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
71
- continue;
72
- }
73
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
74
- Utils.isPlainObject(data[prop.name])) {
75
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
76
- }
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
- }
83
- }
84
- }
76
+ if (
77
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
78
+ Utils.isPlainObject(data[prop.name])
79
+ ) {
80
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
85
81
  }
86
- }
87
- else {
88
- this.hydrate(entity, meta2, data, options);
89
- }
90
- if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
91
- Object.setPrototypeOf(entity, meta2.prototype);
92
- }
93
- if (options.merge && wrapped.hasPrimaryKey()) {
94
- this.unitOfWork.register(entity, data, {
95
- // Always refresh to ensure the payload is in correct shape for joined strategy. When loading nested relations,
96
- // they will be created early without `Type.ensureComparable` being properly handled, resulting in extra updates.
97
- refresh: options.initialized,
98
- newEntity: options.newEntity,
99
- loaded: options.initialized,
100
- });
101
- if (options.recomputeSnapshot) {
102
- wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
82
+ if (prop.customType instanceof JsonType && this.#platform.convertsJsonAutomatically()) {
83
+ data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.#platform, {
84
+ key: prop.name,
85
+ mode: 'hydration',
86
+ });
103
87
  }
88
+ }
104
89
  }
105
- if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
106
- this.#eventManager.dispatchEvent(EventType.onInit, { entity, meta: meta2, em: this.#em });
107
- }
108
- wrapped.__processing = false;
109
- return entity;
90
+ }
91
+ } else {
92
+ this.hydrate(entity, meta2, data, options);
110
93
  }
111
- mergeData(meta, entity, data, options = {}) {
112
- // merge unchanged properties automatically
113
- data = QueryHelper.processParams(data);
114
- const existsData = this.#comparator.prepareEntity(entity);
115
- const originalEntityData = helper(entity).__originalEntityData ?? {};
116
- const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData);
117
- // version properties are not part of entity snapshots
118
- if (meta.versionProperty &&
119
- data[meta.versionProperty] &&
120
- data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
121
- diff[meta.versionProperty] = data[meta.versionProperty];
122
- }
123
- const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
124
- // do not override values changed by user
125
- Utils.keys(diff).forEach(key => delete diff2[key]);
126
- Utils.keys(diff2)
127
- .filter(key => {
128
- // ignore null values if there is already present non-null value
129
- if (existsData[key] != null) {
130
- return diff2[key] == null;
131
- }
132
- return diff2[key] === undefined;
133
- })
134
- .forEach(key => delete diff2[key]);
135
- // but always add collection properties and formulas if they are part of the `data`
136
- Utils.keys(data)
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]));
140
- // rehydrated with the new values, skip those changed by user
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);
145
- // we need to update the entity data only with keys that were not present before
146
- const nullVal = this.#config.get('forceUndefined') ? undefined : null;
147
- Utils.keys(diff2).forEach(key => {
148
- const prop = meta.properties[key];
149
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
150
- Utils.isPlainObject(data[prop.name])) {
151
- // oxfmt-ignore
152
- diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
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
- }
161
- originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
162
- helper(entity).__loadedProperties.add(key);
163
- });
164
- // in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
165
- meta.relations.forEach(prop => {
166
- if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
167
- Array.isArray(data[prop.name])) {
168
- // instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
169
- // we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
170
- data[prop.name]
171
- .filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
172
- .forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
173
- return;
174
- }
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
180
- }
181
- });
182
- this.unitOfWork.normalizeEntityData(meta, originalEntityData);
94
+ if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
95
+ Object.setPrototypeOf(entity, meta2.prototype);
183
96
  }
184
- createReference(entityName, id, options = {}) {
185
- options.convertCustomTypes ??= true;
186
- const meta = this.#metadata.get(entityName);
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
- }
200
- if (meta.simplePK) {
201
- const exists = this.unitOfWork.getById(entityName, id, schema);
202
- if (exists) {
203
- return exists;
204
- }
205
- const data = Utils.isPlainObject(id) ? id : { [meta.primaryKeys[0]]: Array.isArray(id) ? id[0] : id };
206
- return this.create(entityName, data, { ...options, initialized: false });
207
- }
208
- if (Array.isArray(id)) {
209
- id = Utils.getPrimaryKeyCondFromArray(id, meta);
210
- }
211
- const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
212
- const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
213
- if (exists) {
214
- return exists;
215
- }
216
- if (Utils.isPrimaryKey(id)) {
217
- id = { [meta.primaryKeys[0]]: id };
218
- }
219
- return this.create(entityName, id, { ...options, initialized: false });
97
+ if (options.merge && wrapped.hasPrimaryKey()) {
98
+ this.unitOfWork.register(entity, data, {
99
+ // Always refresh to ensure the payload is in correct shape for joined strategy. When loading nested relations,
100
+ // they will be created early without `Type.ensureComparable` being properly handled, resulting in extra updates.
101
+ refresh: options.initialized,
102
+ newEntity: options.newEntity,
103
+ loaded: options.initialized,
104
+ });
105
+ if (options.recomputeSnapshot) {
106
+ wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
107
+ }
220
108
  }
221
- createEmbeddable(entityName, data, options = {}) {
222
- data = { ...data };
223
- const meta = this.#metadata.get(entityName);
224
- const meta2 = this.processDiscriminatorColumn(meta, data);
225
- return this.createEntity(data, meta2, options);
109
+ if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
110
+ this.#eventManager.dispatchEvent(EventType.onInit, { entity, meta: meta2, em: this.#em });
226
111
  }
227
- getComparator() {
228
- return this.#comparator;
112
+ wrapped.__processing = false;
113
+ return entity;
114
+ }
115
+ /** Merges new data into an existing entity, preserving user-modified properties. */
116
+ mergeData(meta, entity, data, options = {}) {
117
+ // merge unchanged properties automatically
118
+ data = QueryHelper.processParams(data);
119
+ const existsData = this.#comparator.prepareEntity(entity);
120
+ const originalEntityData = helper(entity).__originalEntityData ?? {};
121
+ const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData);
122
+ // version properties are not part of entity snapshots
123
+ if (
124
+ meta.versionProperty &&
125
+ data[meta.versionProperty] &&
126
+ data[meta.versionProperty] !== originalEntityData[meta.versionProperty]
127
+ ) {
128
+ diff[meta.versionProperty] = data[meta.versionProperty];
229
129
  }
230
- createEntity(data, meta, options) {
231
- const schema = this.#driver.getSchemaName(meta, options);
232
- if (options.newEntity || meta.forceConstructor || meta.virtual) {
233
- if (meta.polymorphs) {
234
- throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
235
- }
236
- const params = this.extractConstructorParams(meta, data, options);
237
- const Entity = meta.class;
238
- // creates new instance via constructor as this is the new entity
239
- const entity = new Entity(...params);
240
- // creating managed entity instance when `forceEntityConstructor` is enabled,
241
- // we need to wipe all the values as they would cause update queries on next flush
242
- if (!options.newEntity && (meta.forceConstructor || this.#config.get('forceEntityConstructor'))) {
243
- meta.props
244
- .filter(prop => prop.persist !== false && !prop.primary && data[prop.name] === undefined)
245
- .forEach(prop => delete entity[prop.name]);
246
- }
247
- if (meta.virtual) {
248
- return entity;
249
- }
250
- helper(entity).__schema = schema;
251
- if (options.initialized) {
252
- EntityHelper.ensurePropagation(entity);
253
- }
254
- return entity;
255
- }
256
- // creates new entity instance, bypassing constructor call as its already persisted entity
257
- const entity = Object.create(meta.class.prototype);
258
- helper(entity).__managed = true;
259
- helper(entity).__processing = !meta.embeddable && !meta.virtual;
260
- helper(entity).__schema = schema;
261
- if (options.merge && !options.newEntity) {
262
- this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, options.parentSchema);
263
- this.unitOfWork.register(entity);
264
- }
265
- if (options.initialized) {
266
- EntityHelper.ensurePropagation(entity);
130
+ const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
131
+ // do not override values changed by user
132
+ Utils.keys(diff).forEach(key => delete diff2[key]);
133
+ Utils.keys(diff2)
134
+ .filter(key => {
135
+ // ignore null values if there is already present non-null value
136
+ if (existsData[key] != null) {
137
+ return diff2[key] == null;
267
138
  }
139
+ return diff2[key] === undefined;
140
+ })
141
+ .forEach(key => delete diff2[key]);
142
+ // but always add collection properties and formulas if they are part of the `data`
143
+ Utils.keys(data)
144
+ .filter(
145
+ key =>
146
+ meta.properties[key]?.formula ||
147
+ [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind),
148
+ )
149
+ .forEach(key => (diff2[key] = data[key]));
150
+ // rehydrated with the new values, skip those changed by user
151
+ // use full hydration if the entity is already initialized, even if the caller used `initialized: false`
152
+ // (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
153
+ const initialized = options.initialized || helper(entity).__initialized;
154
+ this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
155
+ // we need to update the entity data only with keys that were not present before
156
+ const nullVal = this.#config.get('forceUndefined') ? undefined : null;
157
+ Utils.keys(diff2).forEach(key => {
158
+ const prop = meta.properties[key];
159
+ if (
160
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
161
+ Utils.isPlainObject(data[prop.name])
162
+ ) {
163
+ // oxfmt-ignore
164
+ diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
165
+ }
166
+ if (
167
+ !options.convertCustomTypes &&
168
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
169
+ prop.customType?.ensureComparable(meta, prop) &&
170
+ diff2[key] != null
171
+ ) {
172
+ const converted = prop.customType.convertToJSValue(diff2[key], this.#platform, { force: true });
173
+ diff2[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { fromQuery: true });
174
+ }
175
+ originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
176
+ helper(entity).__loadedProperties.add(key);
177
+ });
178
+ // in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
179
+ meta.relations.forEach(prop => {
180
+ if (
181
+ [ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
182
+ Array.isArray(data[prop.name])
183
+ ) {
184
+ // instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
185
+ // we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
186
+ data[prop.name]
187
+ .filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
188
+ .forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
189
+ return;
190
+ }
191
+ if (
192
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
193
+ Utils.isPlainObject(data[prop.name]) &&
194
+ entity[prop.name] &&
195
+ helper(entity[prop.name]).__initialized
196
+ ) {
197
+ this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
198
+ }
199
+ });
200
+ this.unitOfWork.normalizeEntityData(meta, originalEntityData);
201
+ }
202
+ /** Creates or retrieves an uninitialized entity reference by its primary key or alternate key. */
203
+ createReference(entityName, id, options = {}) {
204
+ options.convertCustomTypes ??= true;
205
+ const meta = this.#metadata.get(entityName);
206
+ const schema = this.#driver.getSchemaName(meta, options);
207
+ // Handle alternate key lookup
208
+ if (options.key) {
209
+ const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
210
+ const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
211
+ if (exists) {
212
+ return exists;
213
+ }
214
+ // Create entity stub - storeByKey will set the alternate key property and store in identity map
215
+ const entity = this.create(entityName, {}, { ...options, initialized: false });
216
+ this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
217
+ return entity;
218
+ }
219
+ if (meta.simplePK) {
220
+ const exists = this.unitOfWork.getById(entityName, id, schema);
221
+ if (exists) {
222
+ return exists;
223
+ }
224
+ const data = Utils.isPlainObject(id) ? id : { [meta.primaryKeys[0]]: Array.isArray(id) ? id[0] : id };
225
+ return this.create(entityName, data, { ...options, initialized: false });
226
+ }
227
+ if (Array.isArray(id)) {
228
+ id = Utils.getPrimaryKeyCondFromArray(id, meta);
229
+ }
230
+ const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
231
+ const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
232
+ if (exists) {
233
+ return exists;
234
+ }
235
+ if (Utils.isPrimaryKey(id)) {
236
+ id = { [meta.primaryKeys[0]]: id };
237
+ }
238
+ return this.create(entityName, id, { ...options, initialized: false });
239
+ }
240
+ /** Creates an embeddable entity instance from the provided data. */
241
+ createEmbeddable(entityName, data, options = {}) {
242
+ data = { ...data };
243
+ const meta = this.#metadata.get(entityName);
244
+ const meta2 = this.processDiscriminatorColumn(meta, data);
245
+ return this.createEntity(data, meta2, options);
246
+ }
247
+ /** Returns the EntityComparator instance used for diffing entities. */
248
+ getComparator() {
249
+ return this.#comparator;
250
+ }
251
+ createEntity(data, meta, options) {
252
+ const schema = this.#driver.getSchemaName(meta, options);
253
+ if (options.newEntity || meta.forceConstructor || meta.virtual) {
254
+ if (meta.polymorphs) {
255
+ throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
256
+ }
257
+ const params = this.extractConstructorParams(meta, data, options);
258
+ const Entity = meta.class;
259
+ // creates new instance via constructor as this is the new entity
260
+ const entity = new Entity(...params);
261
+ // creating managed entity instance when `forceEntityConstructor` is enabled,
262
+ // we need to wipe all the values as they would cause update queries on next flush
263
+ if (!options.newEntity && (meta.forceConstructor || this.#config.get('forceEntityConstructor'))) {
264
+ meta.props
265
+ .filter(prop => prop.persist !== false && !prop.primary && data[prop.name] === undefined)
266
+ .forEach(prop => delete entity[prop.name]);
267
+ }
268
+ if (meta.virtual) {
268
269
  return entity;
270
+ }
271
+ helper(entity).__schema = schema;
272
+ if (options.initialized) {
273
+ EntityHelper.ensurePropagation(entity);
274
+ }
275
+ return entity;
269
276
  }
270
- assignDefaultValues(entity, meta) {
271
- for (const prop of meta.props) {
272
- if (prop.onCreate) {
273
- entity[prop.name] ??= prop.onCreate(entity, this.#em);
274
- }
275
- }
277
+ // creates new entity instance, bypassing constructor call as its already persisted entity
278
+ const entity = Object.create(meta.class.prototype);
279
+ helper(entity).__managed = true;
280
+ helper(entity).__processing = !meta.embeddable && !meta.virtual;
281
+ helper(entity).__schema = schema;
282
+ if (options.merge && !options.newEntity) {
283
+ this.#hydrator.hydrateReference(
284
+ entity,
285
+ meta,
286
+ data,
287
+ this,
288
+ options.convertCustomTypes,
289
+ options.schema,
290
+ options.parentSchema,
291
+ );
292
+ this.unitOfWork.register(entity);
276
293
  }
277
- hydrate(entity, meta, data, options) {
278
- if (options.initialized) {
279
- this.#hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
280
- }
281
- else {
282
- this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
283
- }
284
- Utils.keys(data).forEach(key => {
285
- helper(entity)?.__loadedProperties.add(key);
286
- helper(entity)?.__serializationContext.fields?.add(key);
287
- });
288
- const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
289
- if (options.newEntity && processOnCreateHooksEarly) {
290
- this.assignDefaultValues(entity, meta);
291
- }
294
+ if (options.initialized) {
295
+ EntityHelper.ensurePropagation(entity);
292
296
  }
293
- findEntity(data, meta, options) {
294
- const schema = this.#driver.getSchemaName(meta, options);
295
- if (meta.simplePK) {
296
- return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
297
- }
298
- if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
299
- return undefined;
297
+ return entity;
298
+ }
299
+ assignDefaultValues(entity, meta) {
300
+ for (const prop of meta.props) {
301
+ if (prop.embedded || [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
302
+ continue;
303
+ }
304
+ if (prop.onCreate) {
305
+ entity[prop.name] ??= prop.onCreate(entity, this.#em);
306
+ } else if (prop.default != null && !isRaw(prop.default) && entity[prop.name] === undefined) {
307
+ entity[prop.name] = prop.default;
308
+ }
309
+ if (prop.kind === ReferenceKind.EMBEDDED && entity[prop.name]) {
310
+ const items = prop.array ? entity[prop.name] : [entity[prop.name]];
311
+ for (const item of items) {
312
+ this.assignDefaultValues(item, prop.targetMeta);
300
313
  }
301
- const pks = Utils.getOrderedPrimaryKeys(data, meta, this.#platform, options.convertCustomTypes);
302
- return this.unitOfWork.getById(meta.class, pks, schema);
314
+ }
315
+ }
316
+ }
317
+ hydrate(entity, meta, data, options) {
318
+ if (options.initialized) {
319
+ this.#hydrator.hydrate(
320
+ entity,
321
+ meta,
322
+ data,
323
+ this,
324
+ 'full',
325
+ options.newEntity,
326
+ options.convertCustomTypes,
327
+ options.schema,
328
+ this.#driver.getSchemaName(meta, options),
329
+ options.normalizeAccessors,
330
+ );
331
+ } else {
332
+ this.#hydrator.hydrateReference(
333
+ entity,
334
+ meta,
335
+ data,
336
+ this,
337
+ options.convertCustomTypes,
338
+ options.schema,
339
+ this.#driver.getSchemaName(meta, options),
340
+ options.normalizeAccessors,
341
+ );
342
+ }
343
+ Utils.keys(data).forEach(key => {
344
+ helper(entity)?.__loadedProperties.add(key);
345
+ helper(entity)?.__serializationContext.fields?.add(key);
346
+ });
347
+ const processOnCreateHooksEarly =
348
+ options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
349
+ if (options.newEntity && processOnCreateHooksEarly) {
350
+ this.assignDefaultValues(entity, meta);
351
+ }
352
+ }
353
+ findEntity(data, meta, options) {
354
+ const schema = this.#driver.getSchemaName(meta, options);
355
+ if (meta.simplePK) {
356
+ return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
357
+ }
358
+ if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
359
+ return undefined;
303
360
  }
304
- processDiscriminatorColumn(meta, data) {
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;
311
- return meta;
361
+ const pks = Utils.getOrderedPrimaryKeys(data, meta, this.#platform, options.convertCustomTypes);
362
+ return this.unitOfWork.getById(meta.class, pks, schema);
363
+ }
364
+ processDiscriminatorColumn(meta, data) {
365
+ // Handle STI discriminator (persisted column)
366
+ if (meta.root.inheritanceType === 'sti') {
367
+ const prop = meta.properties[meta.root.discriminatorColumn];
368
+ const value = data[prop.name];
369
+ const type = meta.root.discriminatorMap[value];
370
+ meta = type ? this.#metadata.get(type) : meta;
371
+ return meta;
372
+ }
373
+ // Handle TPT discriminator (computed at query time)
374
+ if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
375
+ const value = data[meta.root.tptDiscriminatorColumn];
376
+ if (value) {
377
+ const type = meta.root.discriminatorMap[value];
378
+ meta = type ? this.#metadata.get(type) : meta;
379
+ }
380
+ }
381
+ return meta;
382
+ }
383
+ /**
384
+ * denormalize PK to value required by driver (e.g. ObjectId)
385
+ */
386
+ denormalizePrimaryKey(meta, data) {
387
+ const pk = meta.getPrimaryProp();
388
+ const spk = meta.properties[meta.serializedPrimaryKey];
389
+ if (!spk?.serializedPrimaryKey) {
390
+ return;
391
+ }
392
+ if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
393
+ data[pk.name] = this.#platform.denormalizePrimaryKey(data[spk.name] || data[pk.name]);
394
+ delete data[spk.name];
395
+ }
396
+ }
397
+ /**
398
+ * returns parameters for entity constructor, creating references from plain ids
399
+ */
400
+ extractConstructorParams(meta, data, options) {
401
+ if (!meta.constructorParams) {
402
+ return [data];
403
+ }
404
+ return meta.constructorParams.map(k => {
405
+ const prop = meta.properties[k];
406
+ const value = data[k];
407
+ if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
408
+ const pk = Reference.unwrapReference(value);
409
+ const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
410
+ if (entity) {
411
+ return entity;
312
412
  }
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
- }
413
+ if (Utils.isEntity(value)) {
414
+ return value;
320
415
  }
321
- return meta;
322
- }
323
- /**
324
- * denormalize PK to value required by driver (e.g. ObjectId)
325
- */
326
- denormalizePrimaryKey(meta, data) {
327
- const pk = meta.getPrimaryProp();
328
- const spk = meta.properties[meta.serializedPrimaryKey];
329
- if (!spk?.serializedPrimaryKey) {
330
- return;
416
+ const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
417
+ if (Utils.isObject(value) && !nakedPk) {
418
+ return this.create(prop.targetMeta.class, value, options);
331
419
  }
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];
420
+ const { newEntity, initialized, ...rest } = options;
421
+ const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
422
+ return Reference.wrapReference(target, prop);
423
+ }
424
+ if (prop?.kind === ReferenceKind.EMBEDDED && value) {
425
+ /* v8 ignore next */
426
+ if (Utils.isEntity(value)) {
427
+ return value;
335
428
  }
336
- }
337
- /**
338
- * returns parameters for entity constructor, creating references from plain ids
339
- */
340
- extractConstructorParams(meta, data, options) {
341
- if (!meta.constructorParams) {
342
- return [data];
429
+ return this.createEmbeddable(prop.targetMeta.class, value, options);
430
+ }
431
+ if (!prop) {
432
+ const tmp = { ...data };
433
+ for (const prop of meta.props) {
434
+ if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
435
+ continue;
436
+ }
437
+ if (
438
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
439
+ Utils.isPlainObject(tmp[prop.name]) &&
440
+ !Utils.extractPK(tmp[prop.name], prop.targetMeta, true)
441
+ ) {
442
+ tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
443
+ } else if (prop.kind === ReferenceKind.SCALAR) {
444
+ tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
445
+ }
343
446
  }
344
- return meta.constructorParams.map(k => {
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);
350
- if (entity) {
351
- return entity;
352
- }
353
- if (Utils.isEntity(value)) {
354
- return value;
355
- }
356
- const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
357
- if (Utils.isObject(value) && !nakedPk) {
358
- return this.create(prop.targetMeta.class, value, options);
359
- }
360
- const { newEntity, initialized, ...rest } = options;
361
- const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
362
- return Reference.wrapReference(target, prop);
363
- }
364
- if (prop?.kind === ReferenceKind.EMBEDDED && value) {
365
- /* v8 ignore next */
366
- if (Utils.isEntity(value)) {
367
- return value;
368
- }
369
- return this.createEmbeddable(prop.targetMeta.class, value, options);
370
- }
371
- if (!prop) {
372
- const tmp = { ...data };
373
- for (const prop of meta.props) {
374
- if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
375
- continue;
376
- }
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);
381
- }
382
- else if (prop.kind === ReferenceKind.SCALAR) {
383
- tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
384
- }
385
- }
386
- return tmp;
387
- }
388
- if (options.convertCustomTypes && prop.customType && value != null) {
389
- return prop.customType.convertToJSValue(value, this.#platform);
390
- }
391
- return value;
392
- });
393
- }
394
- get unitOfWork() {
395
- return this.#em.getUnitOfWork(false);
396
- }
447
+ return tmp;
448
+ }
449
+ if (options.convertCustomTypes && prop.customType && value != null) {
450
+ return prop.customType.convertToJSValue(value, this.#platform);
451
+ }
452
+ return value;
453
+ });
454
+ }
455
+ get unitOfWork() {
456
+ return this.#em.getUnitOfWork(false);
457
+ }
397
458
  }