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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/EntityManager.d.ts +70 -75
  2. package/EntityManager.js +487 -402
  3. package/MikroORM.d.ts +45 -38
  4. package/MikroORM.js +123 -156
  5. package/README.md +7 -4
  6. package/cache/FileCacheAdapter.d.ts +2 -7
  7. package/cache/FileCacheAdapter.js +35 -30
  8. package/cache/GeneratedCacheAdapter.d.ts +1 -2
  9. package/cache/GeneratedCacheAdapter.js +6 -8
  10. package/cache/MemoryCacheAdapter.d.ts +1 -2
  11. package/cache/MemoryCacheAdapter.js +8 -8
  12. package/cache/index.d.ts +1 -2
  13. package/cache/index.js +0 -2
  14. package/connections/Connection.d.ts +12 -5
  15. package/connections/Connection.js +37 -15
  16. package/drivers/DatabaseDriver.d.ts +25 -18
  17. package/drivers/DatabaseDriver.js +144 -45
  18. package/drivers/IDatabaseDriver.d.ts +118 -23
  19. package/entity/BaseEntity.d.ts +63 -4
  20. package/entity/BaseEntity.js +0 -3
  21. package/entity/Collection.d.ts +95 -31
  22. package/entity/Collection.js +487 -139
  23. package/entity/EntityAssigner.js +37 -25
  24. package/entity/EntityFactory.d.ts +8 -9
  25. package/entity/EntityFactory.js +152 -100
  26. package/entity/EntityHelper.d.ts +2 -2
  27. package/entity/EntityHelper.js +69 -27
  28. package/entity/EntityLoader.d.ts +12 -13
  29. package/entity/EntityLoader.js +286 -125
  30. package/entity/EntityRepository.d.ts +28 -8
  31. package/entity/EntityRepository.js +8 -2
  32. package/entity/PolymorphicRef.d.ts +12 -0
  33. package/entity/PolymorphicRef.js +18 -0
  34. package/entity/Reference.d.ts +3 -8
  35. package/entity/Reference.js +62 -29
  36. package/entity/WrappedEntity.d.ts +7 -10
  37. package/entity/WrappedEntity.js +6 -7
  38. package/entity/defineEntity.d.ts +472 -313
  39. package/entity/defineEntity.js +134 -290
  40. package/entity/index.d.ts +2 -2
  41. package/entity/index.js +2 -2
  42. package/entity/utils.d.ts +6 -1
  43. package/entity/utils.js +46 -11
  44. package/entity/validators.d.ts +11 -0
  45. package/entity/validators.js +66 -0
  46. package/enums.d.ts +8 -6
  47. package/enums.js +13 -17
  48. package/errors.d.ts +26 -16
  49. package/errors.js +63 -31
  50. package/events/EventManager.d.ts +3 -5
  51. package/events/EventManager.js +37 -26
  52. package/events/index.d.ts +1 -1
  53. package/events/index.js +0 -1
  54. package/exceptions.js +9 -2
  55. package/hydration/Hydrator.js +1 -2
  56. package/hydration/ObjectHydrator.d.ts +5 -6
  57. package/hydration/ObjectHydrator.js +109 -50
  58. package/index.d.ts +2 -2
  59. package/index.js +1 -2
  60. package/logging/DefaultLogger.d.ts +1 -1
  61. package/logging/DefaultLogger.js +3 -4
  62. package/logging/SimpleLogger.d.ts +1 -1
  63. package/logging/colors.d.ts +1 -1
  64. package/logging/colors.js +4 -6
  65. package/logging/index.d.ts +2 -1
  66. package/logging/index.js +1 -1
  67. package/logging/inspect.d.ts +2 -0
  68. package/logging/inspect.js +11 -0
  69. package/metadata/EntitySchema.d.ts +47 -23
  70. package/metadata/EntitySchema.js +103 -34
  71. package/metadata/MetadataDiscovery.d.ts +65 -18
  72. package/metadata/MetadataDiscovery.js +940 -424
  73. package/metadata/MetadataProvider.d.ts +11 -2
  74. package/metadata/MetadataProvider.js +71 -2
  75. package/metadata/MetadataStorage.d.ts +11 -13
  76. package/metadata/MetadataStorage.js +79 -48
  77. package/metadata/MetadataValidator.d.ts +32 -9
  78. package/metadata/MetadataValidator.js +214 -44
  79. package/metadata/discover-entities.d.ts +5 -0
  80. package/metadata/discover-entities.js +40 -0
  81. package/metadata/index.d.ts +1 -1
  82. package/metadata/index.js +0 -1
  83. package/metadata/types.d.ts +577 -0
  84. package/metadata/types.js +1 -0
  85. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  86. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  87. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/EntityCaseNamingStrategy.js +7 -6
  89. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  90. package/naming-strategy/MongoNamingStrategy.js +6 -6
  91. package/naming-strategy/NamingStrategy.d.ts +28 -4
  92. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  93. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  94. package/naming-strategy/index.d.ts +1 -1
  95. package/naming-strategy/index.js +0 -1
  96. package/not-supported.d.ts +2 -0
  97. package/not-supported.js +8 -0
  98. package/package.json +47 -36
  99. package/platforms/ExceptionConverter.js +1 -1
  100. package/platforms/Platform.d.ts +33 -15
  101. package/platforms/Platform.js +125 -69
  102. package/serialization/EntitySerializer.d.ts +6 -3
  103. package/serialization/EntitySerializer.js +54 -30
  104. package/serialization/EntityTransformer.js +37 -22
  105. package/serialization/SerializationContext.d.ts +10 -14
  106. package/serialization/SerializationContext.js +24 -19
  107. package/types/ArrayType.d.ts +1 -1
  108. package/types/ArrayType.js +2 -3
  109. package/types/BigIntType.js +1 -1
  110. package/types/BlobType.d.ts +0 -1
  111. package/types/BlobType.js +0 -3
  112. package/types/BooleanType.d.ts +1 -0
  113. package/types/BooleanType.js +3 -0
  114. package/types/DecimalType.js +2 -2
  115. package/types/DoubleType.js +1 -1
  116. package/types/EnumArrayType.js +1 -2
  117. package/types/JsonType.d.ts +1 -1
  118. package/types/JsonType.js +7 -2
  119. package/types/TinyIntType.js +1 -1
  120. package/types/Type.d.ts +2 -4
  121. package/types/Type.js +3 -3
  122. package/types/Uint8ArrayType.d.ts +0 -1
  123. package/types/Uint8ArrayType.js +1 -4
  124. package/types/UuidType.d.ts +2 -0
  125. package/types/UuidType.js +14 -2
  126. package/types/index.d.ts +3 -2
  127. package/typings.d.ts +427 -170
  128. package/typings.js +100 -45
  129. package/unit-of-work/ChangeSet.d.ts +4 -6
  130. package/unit-of-work/ChangeSet.js +8 -9
  131. package/unit-of-work/ChangeSetComputer.d.ts +2 -12
  132. package/unit-of-work/ChangeSetComputer.js +61 -38
  133. package/unit-of-work/ChangeSetPersister.d.ts +10 -17
  134. package/unit-of-work/ChangeSetPersister.js +136 -73
  135. package/unit-of-work/CommitOrderCalculator.d.ts +13 -14
  136. package/unit-of-work/CommitOrderCalculator.js +22 -20
  137. package/unit-of-work/IdentityMap.d.ts +12 -3
  138. package/unit-of-work/IdentityMap.js +51 -13
  139. package/unit-of-work/UnitOfWork.d.ts +39 -23
  140. package/unit-of-work/UnitOfWork.js +441 -246
  141. package/utils/AbstractMigrator.d.ts +101 -0
  142. package/utils/AbstractMigrator.js +303 -0
  143. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  144. package/utils/AbstractSchemaGenerator.js +30 -18
  145. package/utils/AsyncContext.d.ts +6 -0
  146. package/utils/AsyncContext.js +42 -0
  147. package/utils/Configuration.d.ts +647 -185
  148. package/utils/Configuration.js +215 -252
  149. package/utils/ConfigurationLoader.d.ts +1 -52
  150. package/utils/ConfigurationLoader.js +1 -330
  151. package/utils/Cursor.d.ts +3 -6
  152. package/utils/Cursor.js +32 -17
  153. package/utils/DataloaderUtils.d.ts +10 -5
  154. package/utils/DataloaderUtils.js +42 -22
  155. package/utils/EntityComparator.d.ts +21 -21
  156. package/utils/EntityComparator.js +224 -118
  157. package/utils/QueryHelper.d.ts +34 -7
  158. package/utils/QueryHelper.js +183 -72
  159. package/utils/RawQueryFragment.d.ts +28 -34
  160. package/utils/RawQueryFragment.js +37 -72
  161. package/utils/RequestContext.js +2 -2
  162. package/utils/TransactionContext.js +2 -2
  163. package/utils/TransactionManager.js +11 -8
  164. package/utils/Utils.d.ts +16 -127
  165. package/utils/Utils.js +104 -402
  166. package/utils/clone.js +13 -23
  167. package/utils/env-vars.d.ts +7 -0
  168. package/utils/env-vars.js +98 -0
  169. package/utils/fs-utils.d.ts +20 -0
  170. package/utils/fs-utils.js +193 -0
  171. package/utils/index.d.ts +1 -3
  172. package/utils/index.js +1 -3
  173. package/utils/upsert-utils.d.ts +9 -4
  174. package/utils/upsert-utils.js +51 -5
  175. package/decorators/Check.d.ts +0 -3
  176. package/decorators/Check.js +0 -13
  177. package/decorators/CreateRequestContext.d.ts +0 -3
  178. package/decorators/CreateRequestContext.js +0 -32
  179. package/decorators/Embeddable.d.ts +0 -8
  180. package/decorators/Embeddable.js +0 -11
  181. package/decorators/Embedded.d.ts +0 -12
  182. package/decorators/Embedded.js +0 -18
  183. package/decorators/Entity.d.ts +0 -33
  184. package/decorators/Entity.js +0 -12
  185. package/decorators/Enum.d.ts +0 -9
  186. package/decorators/Enum.js +0 -16
  187. package/decorators/Filter.d.ts +0 -2
  188. package/decorators/Filter.js +0 -8
  189. package/decorators/Formula.d.ts +0 -4
  190. package/decorators/Formula.js +0 -15
  191. package/decorators/Indexed.d.ts +0 -19
  192. package/decorators/Indexed.js +0 -20
  193. package/decorators/ManyToMany.d.ts +0 -42
  194. package/decorators/ManyToMany.js +0 -14
  195. package/decorators/ManyToOne.d.ts +0 -34
  196. package/decorators/ManyToOne.js +0 -14
  197. package/decorators/OneToMany.d.ts +0 -28
  198. package/decorators/OneToMany.js +0 -17
  199. package/decorators/OneToOne.d.ts +0 -28
  200. package/decorators/OneToOne.js +0 -7
  201. package/decorators/PrimaryKey.d.ts +0 -8
  202. package/decorators/PrimaryKey.js +0 -20
  203. package/decorators/Property.d.ts +0 -250
  204. package/decorators/Property.js +0 -32
  205. package/decorators/Transactional.d.ts +0 -14
  206. package/decorators/Transactional.js +0 -28
  207. package/decorators/hooks.d.ts +0 -16
  208. package/decorators/hooks.js +0 -47
  209. package/decorators/index.d.ts +0 -17
  210. package/decorators/index.js +0 -17
  211. package/entity/ArrayCollection.d.ts +0 -118
  212. package/entity/ArrayCollection.js +0 -407
  213. package/entity/EntityValidator.d.ts +0 -19
  214. package/entity/EntityValidator.js +0 -150
  215. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  216. package/metadata/ReflectMetadataProvider.js +0 -44
  217. package/utils/resolveContextProvider.d.ts +0 -10
  218. package/utils/resolveContextProvider.js +0 -28
@@ -1,4 +1,3 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
1
  import { Collection } from '../entity/Collection.js';
3
2
  import { EntityHelper } from '../entity/EntityHelper.js';
4
3
  import { helper } from '../entity/wrap.js';
@@ -13,41 +12,42 @@ import { Cascade, DeferMode, EventType, LockMode, ReferenceKind } from '../enums
13
12
  import { OptimisticLockError, ValidationError } from '../errors.js';
14
13
  import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
15
14
  import { IdentityMap } from './IdentityMap.js';
15
+ import { createAsyncContext } from '../utils/AsyncContext.js';
16
16
  // to deal with validation for flush inside flush hooks and `Promise.all`
17
- const insideFlush = new AsyncLocalStorage();
17
+ const insideFlush = createAsyncContext();
18
18
  export class UnitOfWork {
19
- em;
20
19
  /** map of references to managed entities */
21
- identityMap;
22
- persistStack = new Set();
23
- removeStack = new Set();
24
- orphanRemoveStack = new Set();
25
- changeSets = new Map();
26
- collectionUpdates = new Set();
27
- extraUpdates = new Set();
28
- metadata;
29
- platform;
30
- eventManager;
31
- comparator;
32
- changeSetComputer;
33
- changeSetPersister;
34
- queuedActions = new Set();
35
- loadedEntities = new Set();
36
- flushQueue = [];
37
- working = false;
20
+ #identityMap;
21
+ #persistStack = new Set();
22
+ #removeStack = new Set();
23
+ #orphanRemoveStack = new Set();
24
+ #changeSets = new Map();
25
+ #collectionUpdates = new Set();
26
+ #extraUpdates = new Set();
27
+ #metadata;
28
+ #platform;
29
+ #eventManager;
30
+ #comparator;
31
+ #changeSetComputer;
32
+ #changeSetPersister;
33
+ #queuedActions = new Set();
34
+ #loadedEntities = new Set();
35
+ #flushQueue = [];
36
+ #working = false;
37
+ #em;
38
38
  constructor(em) {
39
- this.em = em;
40
- this.metadata = this.em.getMetadata();
41
- this.platform = this.em.getPlatform();
42
- this.identityMap = new IdentityMap(this.platform.getDefaultSchemaName());
43
- this.eventManager = this.em.getEventManager();
44
- this.comparator = this.em.getComparator();
45
- this.changeSetComputer = new ChangeSetComputer(this.em.getValidator(), this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
46
- this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
39
+ this.#em = em;
40
+ this.#metadata = this.#em.getMetadata();
41
+ this.#platform = this.#em.getPlatform();
42
+ this.#identityMap = new IdentityMap(this.#platform.getDefaultSchemaName());
43
+ this.#eventManager = this.#em.getEventManager();
44
+ this.#comparator = this.#em.getComparator();
45
+ this.#changeSetComputer = new ChangeSetComputer(this.#em, this.#collectionUpdates);
46
+ this.#changeSetPersister = new ChangeSetPersister(this.#em);
47
47
  }
48
48
  merge(entity, visited) {
49
49
  const wrapped = helper(entity);
50
- wrapped.__em = this.em;
50
+ wrapped.__em = this.#em;
51
51
  if (!wrapped.hasPrimaryKey()) {
52
52
  return;
53
53
  }
@@ -56,57 +56,81 @@ export class UnitOfWork {
56
56
  if (!wrapped.__managed && visited) {
57
57
  return;
58
58
  }
59
- this.identityMap.store(entity);
59
+ this.#identityMap.store(entity);
60
60
  // if visited is available, we are cascading, and need to be careful when resetting the entity data
61
61
  // as there can be some entity with already changed state that is not yet flushed
62
62
  if (wrapped.__initialized && (!visited || !wrapped.__originalEntityData)) {
63
- wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
64
- wrapped.__touched = false;
63
+ wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
65
64
  }
66
65
  this.cascade(entity, Cascade.MERGE, visited ?? new Set());
67
66
  }
67
+ /**
68
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
69
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
70
+ * @internal
71
+ */
72
+ normalizeEntityData(meta, data) {
73
+ const forceUndefined = this.#em.config.get('forceUndefined');
74
+ for (const key of Utils.keys(data)) {
75
+ const prop = meta.properties[key];
76
+ if (!prop) {
77
+ continue;
78
+ }
79
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
80
+ Utils.isPlainObject(data[prop.name])) {
81
+ // Skip polymorphic relations - they use PolymorphicRef wrapper
82
+ if (!prop.polymorphic) {
83
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
84
+ }
85
+ }
86
+ else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
87
+ for (const p of prop.targetMeta.props) {
88
+ /* v8 ignore next */
89
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
90
+ data[(prefix + p.name)] = data[prop.name][p.name];
91
+ }
92
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
93
+ }
94
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
95
+ const converted = prop.customType.convertToJSValue(data[key], this.#platform, {
96
+ key,
97
+ mode: 'hydration',
98
+ force: true,
99
+ });
100
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { key, mode: 'hydration' });
101
+ }
102
+ if (forceUndefined) {
103
+ if (data[key] === null) {
104
+ data[key] = undefined;
105
+ }
106
+ }
107
+ }
108
+ }
68
109
  /**
69
110
  * @internal
70
111
  */
71
112
  register(entity, data, options) {
72
- this.identityMap.store(entity);
113
+ this.#identityMap.store(entity);
73
114
  EntityHelper.ensurePropagation(entity);
74
115
  if (options?.newEntity) {
75
116
  return entity;
76
117
  }
77
- const forceUndefined = this.em.config.get('forceUndefined');
118
+ const forceUndefined = this.#em.config.get('forceUndefined');
78
119
  const wrapped = helper(entity);
79
120
  if (options?.loaded && wrapped.__initialized && !wrapped.__onLoadFired) {
80
- this.loadedEntities.add(entity);
121
+ this.#loadedEntities.add(entity);
81
122
  }
82
- wrapped.__em ??= this.em;
123
+ wrapped.__em ??= this.#em;
83
124
  wrapped.__managed = true;
84
125
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
126
+ this.normalizeEntityData(wrapped.__meta, data);
85
127
  for (const key of Utils.keys(data)) {
86
128
  const prop = wrapped.__meta.properties[key];
87
- if (!prop) {
88
- continue;
89
- }
90
- wrapped.__loadedProperties.add(key);
91
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
92
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
93
- }
94
- else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
95
- for (const p of prop.targetMeta.props) {
96
- /* v8 ignore next */
97
- const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
98
- data[prefix + p.name] = data[prop.name][p.name];
99
- }
100
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
101
- }
102
- if (forceUndefined) {
103
- if (data[key] === null) {
104
- data[key] = undefined;
105
- }
129
+ if (prop) {
130
+ wrapped.__loadedProperties.add(key);
106
131
  }
107
132
  }
108
133
  wrapped.__originalEntityData = data;
109
- wrapped.__touched = false;
110
134
  }
111
135
  return entity;
112
136
  }
@@ -114,13 +138,19 @@ export class UnitOfWork {
114
138
  * @internal
115
139
  */
116
140
  async dispatchOnLoadEvent() {
117
- for (const entity of this.loadedEntities) {
118
- if (this.eventManager.hasListeners(EventType.onLoad, entity.__meta)) {
119
- await this.eventManager.dispatchEvent(EventType.onLoad, { entity, meta: entity.__meta, em: this.em });
141
+ for (const entity of this.#loadedEntities) {
142
+ if (this.#eventManager.hasListeners(EventType.onLoad, entity.__meta)) {
143
+ await this.#eventManager.dispatchEvent(EventType.onLoad, { entity, meta: entity.__meta, em: this.#em });
120
144
  helper(entity).__onLoadFired = true;
121
145
  }
122
146
  }
123
- this.loadedEntities.clear();
147
+ this.#loadedEntities.clear();
148
+ }
149
+ /**
150
+ * @internal
151
+ */
152
+ unmarkAsLoaded(entity) {
153
+ this.#loadedEntities.delete(entity);
124
154
  }
125
155
  /**
126
156
  * Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
@@ -129,7 +159,7 @@ export class UnitOfWork {
129
159
  if (id == null || (Array.isArray(id) && id.length === 0)) {
130
160
  return undefined;
131
161
  }
132
- const meta = this.metadata.find(entityName).root;
162
+ const meta = this.#metadata.find(entityName).root;
133
163
  let hash;
134
164
  if (meta.simplePK) {
135
165
  hash = '' + id;
@@ -138,7 +168,7 @@ export class UnitOfWork {
138
168
  let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
139
169
  keys = meta.getPrimaryProps(true).map((p, i) => {
140
170
  if (!convertCustomTypes && p.customType) {
141
- return p.customType.convertToDatabaseValue(keys[i], this.platform, {
171
+ return p.customType.convertToDatabaseValue(keys[i], this.#platform, {
142
172
  key: p.name,
143
173
  mode: 'hydration',
144
174
  });
@@ -147,14 +177,48 @@ export class UnitOfWork {
147
177
  });
148
178
  hash = Utils.getPrimaryKeyHash(keys);
149
179
  }
150
- schema ??= meta.schema ?? this.em.config.getSchema();
180
+ schema ??= meta.schema ?? this.#em.config.getSchema();
151
181
  if (schema) {
152
182
  hash = `${schema}:${hash}`;
153
183
  }
154
- return this.identityMap.getByHash(meta, hash);
184
+ return this.#identityMap.getByHash(meta, hash);
185
+ }
186
+ /**
187
+ * Returns entity from the identity map by an alternate key (non-PK property).
188
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
189
+ * If false (default), the value is assumed to be in JS format already.
190
+ */
191
+ getByKey(entityName, key, value, schema, convertCustomTypes) {
192
+ const meta = this.#metadata.find(entityName).root;
193
+ schema ??= meta.schema ?? this.#em.config.getSchema();
194
+ const prop = meta.properties[key];
195
+ // Convert from DB format to JS format if needed
196
+ if (convertCustomTypes && prop?.customType) {
197
+ value = prop.customType.convertToJSValue(value, this.#platform, { mode: 'hydration' });
198
+ }
199
+ const hash = this.#identityMap.getKeyHash(key, '' + value, schema);
200
+ return this.#identityMap.getByHash(meta, hash);
201
+ }
202
+ /**
203
+ * Stores an entity in the identity map under an alternate key (non-PK property).
204
+ * Also sets the property value on the entity.
205
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
206
+ * If false (default), the value is assumed to be in JS format already.
207
+ */
208
+ storeByKey(entity, key, value, schema, convertCustomTypes) {
209
+ const meta = entity.__meta.root;
210
+ schema ??= meta.schema ?? this.#em.config.getSchema();
211
+ const prop = meta.properties[key];
212
+ // Convert from DB format to JS format if needed
213
+ if (convertCustomTypes && prop?.customType) {
214
+ value = prop.customType.convertToJSValue(value, this.#platform, { mode: 'hydration' });
215
+ }
216
+ // Set the property on the entity
217
+ entity[key] = value;
218
+ this.#identityMap.storeByKey(entity, key, '' + value, schema);
155
219
  }
156
220
  tryGetById(entityName, where, schema, strict = true) {
157
- const pk = Utils.extractPK(where, this.metadata.find(entityName), strict);
221
+ const pk = Utils.extractPK(where, this.#metadata.find(entityName), strict);
158
222
  if (!pk) {
159
223
  return null;
160
224
  }
@@ -164,7 +228,7 @@ export class UnitOfWork {
164
228
  * Returns map of all managed entities.
165
229
  */
166
230
  getIdentityMap() {
167
- return this.identityMap;
231
+ return this.#identityMap;
168
232
  }
169
233
  /**
170
234
  * Returns stored snapshot of entity state that is used for change set computation.
@@ -173,76 +237,76 @@ export class UnitOfWork {
173
237
  return helper(entity).__originalEntityData;
174
238
  }
175
239
  getPersistStack() {
176
- return this.persistStack;
240
+ return this.#persistStack;
177
241
  }
178
242
  getRemoveStack() {
179
- return this.removeStack;
243
+ return this.#removeStack;
180
244
  }
181
245
  getChangeSets() {
182
- return [...this.changeSets.values()];
246
+ return [...this.#changeSets.values()];
183
247
  }
184
248
  getCollectionUpdates() {
185
- return [...this.collectionUpdates];
249
+ return [...this.#collectionUpdates];
186
250
  }
187
251
  getExtraUpdates() {
188
- return this.extraUpdates;
252
+ return this.#extraUpdates;
189
253
  }
190
254
  shouldAutoFlush(meta) {
191
255
  if (insideFlush.getStore()) {
192
256
  return false;
193
257
  }
194
- if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
258
+ if (this.#queuedActions.has(meta.class) || this.#queuedActions.has(meta.root.class)) {
195
259
  return true;
196
260
  }
197
- for (const entity of this.identityMap.getStore(meta).values()) {
198
- if (helper(entity).__initialized && helper(entity).isTouched()) {
199
- return true;
200
- }
261
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.#queuedActions.has(v))) {
262
+ return true;
201
263
  }
202
264
  return false;
203
265
  }
204
266
  clearActionsQueue() {
205
- this.queuedActions.clear();
267
+ this.#queuedActions.clear();
206
268
  }
207
269
  computeChangeSet(entity, type) {
208
270
  const wrapped = helper(entity);
209
- if (type) {
210
- this.changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
271
+ if (type === ChangeSetType.DELETE || type === ChangeSetType.DELETE_EARLY) {
272
+ this.#changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
211
273
  return;
212
274
  }
213
- const cs = this.changeSetComputer.computeChangeSet(entity);
275
+ const cs = this.#changeSetComputer.computeChangeSet(entity);
214
276
  if (!cs || this.checkUniqueProps(cs)) {
215
277
  return;
216
278
  }
279
+ /* v8 ignore next */
280
+ if (type) {
281
+ cs.type = type;
282
+ }
217
283
  this.initIdentifier(entity);
218
- this.changeSets.set(entity, cs);
219
- this.persistStack.delete(entity);
220
- wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
221
- wrapped.__touched = false;
284
+ this.#changeSets.set(entity, cs);
285
+ this.#persistStack.delete(entity);
286
+ wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
222
287
  }
223
288
  recomputeSingleChangeSet(entity) {
224
- const changeSet = this.changeSets.get(entity);
289
+ const changeSet = this.#changeSets.get(entity);
225
290
  if (!changeSet) {
226
291
  return;
227
292
  }
228
- const cs = this.changeSetComputer.computeChangeSet(entity);
293
+ const cs = this.#changeSetComputer.computeChangeSet(entity);
229
294
  if (cs && !this.checkUniqueProps(cs)) {
230
295
  Object.assign(changeSet.payload, cs.payload);
231
- helper(entity).__originalEntityData = this.comparator.prepareEntity(entity);
232
- helper(entity).__touched = false;
296
+ helper(entity).__originalEntityData = this.#comparator.prepareEntity(entity);
233
297
  }
234
298
  }
235
299
  persist(entity, visited, options = {}) {
236
300
  EntityHelper.ensurePropagation(entity);
237
- if (options.checkRemoveStack && this.removeStack.has(entity)) {
301
+ if (options.checkRemoveStack && this.#removeStack.has(entity)) {
238
302
  return;
239
303
  }
240
304
  const wrapped = helper(entity);
241
- this.persistStack.add(entity);
242
- this.queuedActions.add(wrapped.__meta.className);
243
- this.removeStack.delete(entity);
305
+ this.#persistStack.add(entity);
306
+ this.#queuedActions.add(wrapped.__meta.class);
307
+ this.#removeStack.delete(entity);
244
308
  if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
245
- this.identityMap.store(entity);
309
+ this.#identityMap.store(entity);
246
310
  }
247
311
  if (options.cascade ?? true) {
248
312
  this.cascade(entity, Cascade.PERSIST, visited, options);
@@ -250,13 +314,13 @@ export class UnitOfWork {
250
314
  }
251
315
  remove(entity, visited, options = {}) {
252
316
  // allow removing not managed entities if they are not part of the persist stack
253
- if (helper(entity).__managed || !this.persistStack.has(entity)) {
254
- this.removeStack.add(entity);
255
- this.queuedActions.add(helper(entity).__meta.className);
317
+ if (helper(entity).__managed || !this.#persistStack.has(entity)) {
318
+ this.#removeStack.add(entity);
319
+ this.#queuedActions.add(helper(entity).__meta.class);
256
320
  }
257
321
  else {
258
- this.persistStack.delete(entity);
259
- this.identityMap.delete(entity);
322
+ this.#persistStack.delete(entity);
323
+ this.#identityMap.delete(entity);
260
324
  }
261
325
  // remove from referencing relations that are nullable
262
326
  for (const prop of helper(entity).__meta.bidirectionalRelations) {
@@ -269,7 +333,7 @@ export class UnitOfWork {
269
333
  }
270
334
  continue;
271
335
  }
272
- const target = relation && relation[inverseProp];
336
+ const target = relation?.[inverseProp];
273
337
  if (relation && Utils.isCollection(target)) {
274
338
  target.removeWithoutPropagation(entity);
275
339
  }
@@ -279,12 +343,12 @@ export class UnitOfWork {
279
343
  }
280
344
  }
281
345
  async commit() {
282
- if (this.working) {
346
+ if (this.#working) {
283
347
  if (insideFlush.getStore()) {
284
348
  throw ValidationError.cannotCommit();
285
349
  }
286
350
  return new Promise((resolve, reject) => {
287
- this.flushQueue.push(() => {
351
+ this.#flushQueue.push(() => {
288
352
  return insideFlush.run(true, () => {
289
353
  return this.doCommit().then(resolve, reject);
290
354
  });
@@ -292,60 +356,61 @@ export class UnitOfWork {
292
356
  });
293
357
  }
294
358
  try {
295
- this.working = true;
359
+ this.#working = true;
296
360
  await insideFlush.run(true, () => this.doCommit());
297
- while (this.flushQueue.length) {
298
- await this.flushQueue.shift()();
361
+ while (this.#flushQueue.length) {
362
+ await this.#flushQueue.shift()();
299
363
  }
300
364
  }
301
365
  finally {
302
366
  this.postCommitCleanup();
303
- this.working = false;
367
+ this.#working = false;
304
368
  }
305
369
  }
306
370
  async doCommit() {
307
- const oldTx = this.em.getTransactionContext();
371
+ const oldTx = this.#em.getTransactionContext();
308
372
  try {
309
- await this.eventManager.dispatchEvent(EventType.beforeFlush, { em: this.em, uow: this });
373
+ await this.#eventManager.dispatchEvent(EventType.beforeFlush, { em: this.#em, uow: this });
310
374
  this.computeChangeSets();
311
- for (const cs of this.changeSets.values()) {
375
+ for (const cs of this.#changeSets.values()) {
312
376
  cs.entity.__helper.__processing = true;
313
377
  }
314
- await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
378
+ await this.#eventManager.dispatchEvent(EventType.onFlush, { em: this.#em, uow: this });
315
379
  this.filterCollectionUpdates();
316
380
  // nothing to do, do not start transaction
317
- if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
318
- return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
381
+ if (this.#changeSets.size === 0 && this.#collectionUpdates.size === 0 && this.#extraUpdates.size === 0) {
382
+ await this.#eventManager.dispatchEvent(EventType.afterFlush, { em: this.#em, uow: this });
383
+ return;
319
384
  }
320
385
  const groups = this.getChangeSetGroups();
321
- const platform = this.em.getPlatform();
322
- const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
386
+ const platform = this.#em.getPlatform();
387
+ const runInTransaction = !this.#em.isInTransaction() && platform.supportsTransactions() && this.#em.config.get('implicitTransactions');
323
388
  if (runInTransaction) {
324
- const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
325
- await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
389
+ const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
390
+ await this.#em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
326
391
  ctx: oldTx,
327
- eventBroadcaster: new TransactionEventBroadcaster(this.em),
392
+ eventBroadcaster: new TransactionEventBroadcaster(this.#em),
328
393
  loggerContext,
329
394
  });
330
395
  }
331
396
  else {
332
- await this.persistToDatabase(groups, this.em.getTransactionContext());
397
+ await this.persistToDatabase(groups, this.#em.getTransactionContext());
333
398
  }
334
399
  this.resetTransaction(oldTx);
335
- for (const cs of this.changeSets.values()) {
400
+ for (const cs of this.#changeSets.values()) {
336
401
  cs.entity.__helper.__processing = false;
337
402
  }
338
- await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this });
403
+ await this.#eventManager.dispatchEvent(EventType.afterFlush, { em: this.#em, uow: this });
339
404
  }
340
405
  finally {
341
406
  this.resetTransaction(oldTx);
342
407
  }
343
408
  }
344
409
  async lock(entity, options) {
345
- if (!this.getById(entity.constructor.name, helper(entity).__primaryKeys, helper(entity).__schema)) {
410
+ if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
346
411
  throw ValidationError.entityNotManaged(entity);
347
412
  }
348
- const meta = this.metadata.find(entity.constructor.name);
413
+ const meta = this.#metadata.find(entity.constructor);
349
414
  if (options.lockMode === LockMode.OPTIMISTIC) {
350
415
  await this.lockOptimistic(entity, meta, options.lockVersion);
351
416
  }
@@ -354,22 +419,25 @@ export class UnitOfWork {
354
419
  }
355
420
  }
356
421
  clear() {
357
- this.identityMap.clear();
358
- this.loadedEntities.clear();
422
+ this.#identityMap.clear();
423
+ this.#loadedEntities.clear();
359
424
  this.postCommitCleanup();
360
425
  }
361
426
  unsetIdentity(entity) {
362
- this.identityMap.delete(entity);
427
+ this.#identityMap.delete(entity);
363
428
  const wrapped = helper(entity);
364
429
  const serializedPK = wrapped.getSerializedPrimaryKey();
365
430
  // remove references of this entity in all managed entities, otherwise flushing could reinsert the entity
366
431
  for (const { meta, prop } of wrapped.__meta.referencingProperties) {
367
- for (const referrer of this.identityMap.getStore(meta).values()) {
432
+ for (const referrer of this.#identityMap.getStore(meta).values()) {
368
433
  const rel = Reference.unwrapReference(referrer[prop.name]);
369
434
  if (Utils.isCollection(rel)) {
370
435
  rel.removeWithoutPropagation(entity);
371
436
  }
372
- else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.type, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
437
+ else if (rel &&
438
+ (prop.mapToPk
439
+ ? helper(this.#em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK
440
+ : rel === entity)) {
373
441
  if (prop.formula) {
374
442
  delete referrer[prop.name];
375
443
  }
@@ -381,55 +449,54 @@ export class UnitOfWork {
381
449
  }
382
450
  delete wrapped.__identifier;
383
451
  delete wrapped.__originalEntityData;
384
- wrapped.__touched = false;
385
452
  wrapped.__managed = false;
386
453
  }
387
454
  computeChangeSets() {
388
- this.changeSets.clear();
455
+ this.#changeSets.clear();
389
456
  const visited = new Set();
390
- for (const entity of this.removeStack) {
457
+ for (const entity of this.#removeStack) {
391
458
  this.cascade(entity, Cascade.REMOVE, visited);
392
459
  }
393
460
  visited.clear();
394
- for (const entity of this.persistStack) {
395
- this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
396
- }
397
- for (const entity of this.identityMap) {
398
- if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
461
+ for (const entity of this.#identityMap) {
462
+ if (!this.#removeStack.has(entity) && !this.#persistStack.has(entity) && !this.#orphanRemoveStack.has(entity)) {
399
463
  this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
400
464
  }
401
465
  }
466
+ for (const entity of this.#persistStack) {
467
+ this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
468
+ }
402
469
  visited.clear();
403
- for (const entity of this.persistStack) {
470
+ for (const entity of this.#persistStack) {
404
471
  this.findNewEntities(entity, visited);
405
472
  }
406
- for (const entity of this.orphanRemoveStack) {
473
+ for (const entity of this.#orphanRemoveStack) {
407
474
  if (!helper(entity).__processing) {
408
- this.removeStack.add(entity);
475
+ this.#removeStack.add(entity);
409
476
  }
410
477
  }
411
478
  // Check insert stack if there are any entities matching something from delete stack. This can happen when recreating entities.
412
479
  const inserts = {};
413
- for (const cs of this.changeSets.values()) {
480
+ for (const cs of this.#changeSets.values()) {
414
481
  if (cs.type === ChangeSetType.CREATE) {
415
- inserts[cs.meta.className] ??= [];
416
- inserts[cs.meta.className].push(cs);
482
+ inserts[cs.meta.uniqueName] ??= [];
483
+ inserts[cs.meta.uniqueName].push(cs);
417
484
  }
418
485
  }
419
- for (const cs of this.changeSets.values()) {
486
+ for (const cs of this.#changeSets.values()) {
420
487
  if (cs.type === ChangeSetType.UPDATE) {
421
- this.findEarlyUpdates(cs, inserts[cs.meta.className]);
488
+ this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
422
489
  }
423
490
  }
424
- for (const entity of this.removeStack) {
491
+ for (const entity of this.#removeStack) {
425
492
  const wrapped = helper(entity);
426
- /* v8 ignore next 3 */
493
+ /* v8 ignore next */
427
494
  if (wrapped.__processing) {
428
495
  continue;
429
496
  }
430
497
  const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
431
498
  let type = ChangeSetType.DELETE;
432
- for (const cs of inserts[wrapped.__meta.className] ?? []) {
499
+ for (const cs of inserts[wrapped.__meta.uniqueName] ?? []) {
433
500
  if (deletePkHash.some(hash => hash === cs.getSerializedPrimaryKey() || this.expandUniqueProps(cs.entity).find(child => hash === child))) {
434
501
  type = ChangeSetType.DELETE_EARLY;
435
502
  }
@@ -446,9 +513,9 @@ export class UnitOfWork {
446
513
  if (!props.some(prop => prop.name in changeSet.payload)) {
447
514
  return;
448
515
  }
449
- for (const cs of this.changeSets.values()) {
516
+ for (const cs of this.#changeSets.values()) {
450
517
  for (const prop of props) {
451
- if (prop.name in cs.payload && cs.rootName === changeSet.rootName && cs.type === changeSet.type) {
518
+ if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
452
519
  conflicts = true;
453
520
  if (changeSet.payload[prop.name] == null) {
454
521
  type = ChangeSetType.UPDATE_EARLY;
@@ -459,7 +526,13 @@ export class UnitOfWork {
459
526
  if (!conflicts) {
460
527
  return;
461
528
  }
462
- this.extraUpdates.add([changeSet.entity, props.map(p => p.name), props.map(p => changeSet.entity[p.name]), changeSet, type]);
529
+ this.#extraUpdates.add([
530
+ changeSet.entity,
531
+ props.map(p => p.name),
532
+ props.map(p => changeSet.entity[p.name]),
533
+ changeSet,
534
+ type,
535
+ ]);
463
536
  for (const p of props) {
464
537
  delete changeSet.entity[p.name];
465
538
  delete changeSet.payload[p.name];
@@ -467,21 +540,22 @@ export class UnitOfWork {
467
540
  }
468
541
  scheduleOrphanRemoval(entity, visited) {
469
542
  if (entity) {
470
- helper(entity).__em = this.em;
471
- this.orphanRemoveStack.add(entity);
472
- this.queuedActions.add(entity.__meta.className);
543
+ const wrapped = helper(entity);
544
+ wrapped.__em = this.#em;
545
+ this.#orphanRemoveStack.add(entity);
546
+ this.#queuedActions.add(wrapped.__meta.class);
473
547
  this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
474
548
  }
475
549
  }
476
550
  cancelOrphanRemoval(entity, visited) {
477
- this.orphanRemoveStack.delete(entity);
551
+ this.#orphanRemoveStack.delete(entity);
478
552
  this.cascade(entity, Cascade.CANCEL_ORPHAN_REMOVAL, visited);
479
553
  }
480
554
  getOrphanRemoveStack() {
481
- return this.orphanRemoveStack;
555
+ return this.#orphanRemoveStack;
482
556
  }
483
557
  getChangeSetPersister() {
484
- return this.changeSetPersister;
558
+ return this.#changeSetPersister;
485
559
  }
486
560
  findNewEntities(entity, visited, idx = 0, processed = new Set()) {
487
561
  if (visited.has(entity)) {
@@ -490,11 +564,11 @@ export class UnitOfWork {
490
564
  visited.add(entity);
491
565
  processed.add(entity);
492
566
  const wrapped = helper(entity);
493
- if (wrapped.__processing || this.removeStack.has(entity) || this.orphanRemoveStack.has(entity)) {
567
+ if (wrapped.__processing || this.#removeStack.has(entity) || this.#orphanRemoveStack.has(entity)) {
494
568
  return;
495
569
  }
496
570
  // Set entityManager default schema
497
- wrapped.__schema ??= this.em.schema;
571
+ wrapped.__schema ??= this.#em.schema;
498
572
  this.initIdentifier(entity);
499
573
  for (const prop of wrapped.__meta.relations) {
500
574
  const targets = Utils.unwrapProperty(entity, wrapped.__meta, prop);
@@ -503,9 +577,70 @@ export class UnitOfWork {
503
577
  this.processReference(entity, prop, kind, visited, processed, idx);
504
578
  }
505
579
  }
506
- const changeSet = this.changeSetComputer.computeChangeSet(entity);
580
+ const changeSet = this.#changeSetComputer.computeChangeSet(entity);
507
581
  if (changeSet && !this.checkUniqueProps(changeSet)) {
508
- this.changeSets.set(entity, changeSet);
582
+ // For TPT child entities, create changesets for each table in hierarchy
583
+ if (wrapped.__meta.inheritanceType === 'tpt' && wrapped.__meta.tptParent) {
584
+ this.createTPTChangeSets(entity, changeSet);
585
+ }
586
+ else {
587
+ this.#changeSets.set(entity, changeSet);
588
+ }
589
+ }
590
+ }
591
+ /**
592
+ * For TPT inheritance, creates separate changesets for each table in the hierarchy.
593
+ * Uses the same entity instance for all changesets - only the metadata and payload differ.
594
+ */
595
+ createTPTChangeSets(entity, originalChangeSet) {
596
+ const meta = helper(entity).__meta;
597
+ const isCreate = originalChangeSet.type === ChangeSetType.CREATE;
598
+ let current = meta;
599
+ let leafCs;
600
+ const parentChangeSets = [];
601
+ while (current) {
602
+ const isRoot = !current.tptParent;
603
+ const payload = {};
604
+ for (const prop of current.ownProps) {
605
+ if (prop.name in originalChangeSet.payload) {
606
+ payload[prop.name] = originalChangeSet.payload[prop.name];
607
+ }
608
+ }
609
+ // For CREATE on non-root tables, include the PK (EntityIdentifier for deferred resolution)
610
+ if (isCreate && !isRoot) {
611
+ const wrapped = helper(entity);
612
+ const identifier = wrapped.__identifier;
613
+ const identifiers = Array.isArray(identifier) ? identifier : [identifier];
614
+ for (let i = 0; i < current.primaryKeys.length; i++) {
615
+ const pk = current.primaryKeys[i];
616
+ payload[pk] = identifiers[i] ?? originalChangeSet.payload[pk];
617
+ }
618
+ }
619
+ if (!isCreate && Object.keys(payload).length === 0) {
620
+ current = current.tptParent;
621
+ continue;
622
+ }
623
+ const cs = new ChangeSet(entity, originalChangeSet.type, payload, current);
624
+ if (current === meta) {
625
+ cs.originalEntity = originalChangeSet.originalEntity;
626
+ leafCs = cs;
627
+ }
628
+ else {
629
+ parentChangeSets.push(cs);
630
+ }
631
+ current = current.tptParent;
632
+ }
633
+ // When only parent properties changed (UPDATE), leaf payload is empty—create a stub anchor
634
+ if (!leafCs && parentChangeSets.length > 0) {
635
+ leafCs = new ChangeSet(entity, originalChangeSet.type, {}, meta);
636
+ leafCs.originalEntity = originalChangeSet.originalEntity;
637
+ }
638
+ // Store the leaf changeset in the main map (entity as key), with parent CSs attached
639
+ if (leafCs) {
640
+ if (parentChangeSets.length > 0) {
641
+ leafCs.tptChangeSets = parentChangeSets;
642
+ }
643
+ this.#changeSets.set(entity, leafCs);
509
644
  }
510
645
  }
511
646
  /**
@@ -518,7 +653,7 @@ export class UnitOfWork {
518
653
  // when changing a unique nullable property (or a 1:1 relation), we can't do it in a single
519
654
  // query as it would cause unique constraint violations
520
655
  const uniqueProps = changeSet.meta.uniqueProps.filter(prop => {
521
- return (prop.nullable || changeSet.type !== ChangeSetType.CREATE);
656
+ return prop.nullable || changeSet.type !== ChangeSetType.CREATE;
522
657
  });
523
658
  this.scheduleExtraUpdate(changeSet, uniqueProps);
524
659
  return changeSet.type === ChangeSetType.UPDATE && !Utils.hasObjectKeys(changeSet.payload);
@@ -528,25 +663,33 @@ export class UnitOfWork {
528
663
  if (!wrapped.__meta.hasUniqueProps) {
529
664
  return [];
530
665
  }
531
- const simpleUniqueHashes = wrapped.__meta.uniqueProps.map(prop => {
666
+ const simpleUniqueHashes = wrapped.__meta.uniqueProps
667
+ .map(prop => {
532
668
  if (entity[prop.name] != null) {
533
- return prop.kind === ReferenceKind.SCALAR || prop.mapToPk ? entity[prop.name] : helper(entity[prop.name]).getSerializedPrimaryKey();
669
+ return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
670
+ ? entity[prop.name]
671
+ : helper(entity[prop.name]).getSerializedPrimaryKey();
534
672
  }
535
673
  if (wrapped.__originalEntityData?.[prop.name] != null) {
536
674
  return Utils.getPrimaryKeyHash(Utils.asArray(wrapped.__originalEntityData[prop.name]));
537
675
  }
538
676
  return undefined;
539
- }).filter(i => i);
540
- const compoundUniqueHashes = wrapped.__meta.uniques.map(unique => {
677
+ })
678
+ .filter(i => i);
679
+ const compoundUniqueHashes = wrapped.__meta.uniques
680
+ .map(unique => {
541
681
  const props = Utils.asArray(unique.properties);
542
682
  if (props.every(prop => entity[prop] != null)) {
543
683
  return Utils.getPrimaryKeyHash(props.map(p => {
544
684
  const prop = wrapped.__meta.properties[p];
545
- return prop.kind === ReferenceKind.SCALAR || prop.mapToPk ? entity[prop.name] : helper(entity[prop.name]).getSerializedPrimaryKey();
685
+ return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
686
+ ? entity[prop.name]
687
+ : helper(entity[prop.name]).getSerializedPrimaryKey();
546
688
  }));
547
689
  }
548
690
  return undefined;
549
- }).filter(i => i);
691
+ })
692
+ .filter(i => i);
550
693
  return simpleUniqueHashes.concat(compoundUniqueHashes);
551
694
  }
552
695
  initIdentifier(entity) {
@@ -578,7 +721,8 @@ export class UnitOfWork {
578
721
  return this.processToOneReference(kind, visited, processed, idx);
579
722
  }
580
723
  if (Utils.isCollection(kind)) {
581
- kind.getItems(false)
724
+ kind
725
+ .getItems(false)
582
726
  .filter(item => !item.__helper.__originalEntityData)
583
727
  .forEach(item => {
584
728
  // propagate schema from parent
@@ -596,29 +740,30 @@ export class UnitOfWork {
596
740
  }
597
741
  processToManyReference(collection, visited, processed, parent, prop) {
598
742
  if (this.isCollectionSelfReferenced(collection, processed)) {
599
- this.extraUpdates.add([parent, prop.name, collection, undefined, ChangeSetType.UPDATE]);
743
+ this.#extraUpdates.add([parent, prop.name, collection, undefined, ChangeSetType.UPDATE]);
600
744
  const coll = new Collection(parent);
601
745
  coll.property = prop;
602
746
  parent[prop.name] = coll;
603
747
  return;
604
748
  }
605
- collection.getItems(false)
749
+ collection
750
+ .getItems(false)
606
751
  .filter(item => !item.__helper.__originalEntityData)
607
752
  .forEach(item => this.findNewEntities(item, visited, 0, processed));
608
753
  }
609
754
  async runHooks(type, changeSet, sync = false) {
610
755
  const meta = changeSet.meta;
611
- if (!this.eventManager.hasListeners(type, meta)) {
756
+ if (!this.#eventManager.hasListeners(type, meta)) {
612
757
  return;
613
758
  }
614
759
  if (!sync) {
615
- await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
760
+ await this.#eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.#em, changeSet });
616
761
  return;
617
762
  }
618
- const copy = this.comparator.prepareEntity(changeSet.entity);
619
- await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
620
- const current = this.comparator.prepareEntity(changeSet.entity);
621
- const diff = this.comparator.diffEntities(changeSet.name, copy, current);
763
+ const copy = this.#comparator.prepareEntity(changeSet.entity);
764
+ await this.#eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.#em, changeSet });
765
+ const current = this.#comparator.prepareEntity(changeSet.entity);
766
+ const diff = this.#comparator.diffEntities(changeSet.meta.class, copy, current);
622
767
  Object.assign(changeSet.payload, diff);
623
768
  const wrapped = helper(changeSet.entity);
624
769
  if (wrapped.__identifier) {
@@ -633,19 +778,19 @@ export class UnitOfWork {
633
778
  }
634
779
  }
635
780
  postCommitCleanup() {
636
- for (const cs of this.changeSets.values()) {
781
+ for (const cs of this.#changeSets.values()) {
637
782
  const wrapped = helper(cs.entity);
638
783
  wrapped.__processing = false;
639
784
  delete wrapped.__pk;
640
785
  }
641
- this.persistStack.clear();
642
- this.removeStack.clear();
643
- this.orphanRemoveStack.clear();
644
- this.changeSets.clear();
645
- this.collectionUpdates.clear();
646
- this.extraUpdates.clear();
647
- this.queuedActions.clear();
648
- this.working = false;
786
+ this.#persistStack.clear();
787
+ this.#removeStack.clear();
788
+ this.#orphanRemoveStack.clear();
789
+ this.#changeSets.clear();
790
+ this.#collectionUpdates.clear();
791
+ this.#extraUpdates.clear();
792
+ this.#queuedActions.clear();
793
+ this.#working = false;
649
794
  }
650
795
  cascade(entity, type, visited = new Set(), options = {}) {
651
796
  if (visited.has(entity)) {
@@ -694,7 +839,8 @@ export class UnitOfWork {
694
839
  return filtered.some(items => processed.has(items));
695
840
  }
696
841
  shouldCascade(prop, type) {
697
- if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) && prop.orphanRemoval) {
842
+ if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) &&
843
+ prop.orphanRemoval) {
698
844
  return true;
699
845
  }
700
846
  // ignore user settings for merge, it is kept only for back compatibility, this should have never been configurable
@@ -704,16 +850,16 @@ export class UnitOfWork {
704
850
  return prop.cascade && (prop.cascade.includes(type) || prop.cascade.includes(Cascade.ALL));
705
851
  }
706
852
  async lockPessimistic(entity, options) {
707
- if (!this.em.isInTransaction()) {
853
+ if (!this.#em.isInTransaction()) {
708
854
  throw ValidationError.transactionRequired();
709
855
  }
710
- await this.em.getDriver().lockPessimistic(entity, { ctx: this.em.getTransactionContext(), ...options });
856
+ await this.#em.getDriver().lockPessimistic(entity, { ctx: this.#em.getTransactionContext(), ...options });
711
857
  }
712
858
  async lockOptimistic(entity, meta, version) {
713
859
  if (!meta.versionProperty) {
714
860
  throw OptimisticLockError.notVersioned(meta);
715
861
  }
716
- if (!Utils.isDefined(version)) {
862
+ if (typeof version === 'undefined') {
717
863
  return;
718
864
  }
719
865
  const wrapped = helper(entity);
@@ -727,63 +873,67 @@ export class UnitOfWork {
727
873
  }
728
874
  fixMissingReference(entity, prop) {
729
875
  const reference = entity[prop.name];
730
- const kind = Reference.unwrapReference(reference);
731
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && kind && !prop.mapToPk) {
732
- if (!Utils.isEntity(kind)) {
733
- entity[prop.name] = this.em.getReference(prop.type, kind, { wrapped: !!prop.ref });
876
+ const target = Reference.unwrapReference(reference);
877
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
878
+ if (!Utils.isEntity(target)) {
879
+ entity[prop.name] = this.#em.getReference(prop.targetMeta.class, target, {
880
+ wrapped: !!prop.ref,
881
+ });
734
882
  }
735
- else if (!helper(kind).__initialized && !helper(kind).__em) {
736
- const pk = helper(kind).getPrimaryKey();
737
- entity[prop.name] = this.em.getReference(prop.type, pk, { wrapped: !!prop.ref });
883
+ else if (!helper(target).__initialized && !helper(target).__em) {
884
+ const pk = helper(target).getPrimaryKey();
885
+ entity[prop.name] = this.#em.getReference(prop.targetMeta.class, pk, {
886
+ wrapped: !!prop.ref,
887
+ });
738
888
  }
739
889
  }
740
- // perf: set the `Collection._property` to skip the getter, as it can be slow when there is a lot of relations
741
- if (Utils.isCollection(kind)) {
742
- kind.property = prop;
890
+ // perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
891
+ if (Utils.isCollection(target)) {
892
+ target.property = prop;
743
893
  }
744
894
  const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
745
- if (isCollection && Array.isArray(kind)) {
895
+ if (isCollection && Array.isArray(target)) {
746
896
  const collection = new Collection(entity);
747
897
  collection.property = prop;
748
898
  entity[prop.name] = collection;
749
- collection.set(kind);
899
+ collection.set(target);
750
900
  }
751
901
  }
752
902
  async persistToDatabase(groups, ctx) {
753
903
  if (ctx) {
754
- this.em.setTransactionContext(ctx);
904
+ this.#em.setTransactionContext(ctx);
755
905
  }
756
906
  const commitOrder = this.getCommitOrder();
757
907
  const commitOrderReversed = [...commitOrder].reverse();
758
908
  // early delete - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
759
- for (const name of commitOrderReversed) {
760
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(name) ?? [], ctx);
909
+ for (const meta of commitOrderReversed) {
910
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(meta) ?? [], ctx);
761
911
  }
762
912
  // early update - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
763
- for (const name of commitOrder) {
764
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(name) ?? [], ctx);
913
+ for (const meta of commitOrder) {
914
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(meta) ?? [], ctx);
765
915
  }
766
916
  // extra updates
767
917
  await this.commitExtraUpdates(ChangeSetType.UPDATE_EARLY, ctx);
768
918
  // create
769
- for (const name of commitOrder) {
770
- await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(name) ?? [], ctx);
919
+ for (const meta of commitOrder) {
920
+ await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(meta) ?? [], ctx);
771
921
  }
772
922
  // update
773
- for (const name of commitOrder) {
774
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(name) ?? [], ctx);
923
+ for (const meta of commitOrder) {
924
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(meta) ?? [], ctx);
775
925
  }
776
926
  // extra updates
777
927
  await this.commitExtraUpdates(ChangeSetType.UPDATE, ctx);
778
928
  // collection updates
779
929
  await this.commitCollectionUpdates(ctx);
780
930
  // delete - entity deletions need to be in reverse commit order
781
- for (const name of commitOrderReversed) {
782
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(name) ?? [], ctx);
931
+ for (const meta of commitOrderReversed) {
932
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(meta) ?? [], ctx);
783
933
  }
784
934
  // take snapshots of all persisted collections
785
935
  const visited = new Set();
786
- for (const changeSet of this.changeSets.values()) {
936
+ for (const changeSet of this.#changeSets.values()) {
787
937
  this.takeCollectionSnapshots(changeSet.entity, visited);
788
938
  }
789
939
  }
@@ -792,15 +942,15 @@ export class UnitOfWork {
792
942
  return;
793
943
  }
794
944
  const props = changeSets[0].meta.root.relations.filter(prop => {
795
- return (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner)
796
- || prop.kind === ReferenceKind.MANY_TO_ONE
797
- || (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.platform.usesPivotTable());
945
+ return ((prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) ||
946
+ prop.kind === ReferenceKind.MANY_TO_ONE ||
947
+ (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.#platform.usesPivotTable()));
798
948
  });
799
949
  for (const changeSet of changeSets) {
800
950
  this.findExtraUpdates(changeSet, props);
801
951
  await this.runHooks(EventType.beforeCreate, changeSet, true);
802
952
  }
803
- await this.changeSetPersister.executeInserts(changeSets, { ctx });
953
+ await this.#changeSetPersister.executeInserts(changeSets, { ctx });
804
954
  for (const changeSet of changeSets) {
805
955
  this.register(changeSet.entity, changeSet.payload, { refresh: true });
806
956
  await this.runHooks(EventType.afterCreate, changeSet);
@@ -814,17 +964,29 @@ export class UnitOfWork {
814
964
  }
815
965
  if (Utils.isCollection(ref)) {
816
966
  ref.getItems(false).some(item => {
817
- const cs = this.changeSets.get(Reference.unwrapReference(item));
818
- const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
967
+ const cs = this.#changeSets.get(Reference.unwrapReference(item));
968
+ const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
819
969
  if (isScheduledForInsert) {
820
970
  this.scheduleExtraUpdate(changeSet, [prop]);
821
971
  return true;
822
972
  }
823
973
  return false;
824
974
  });
975
+ continue;
825
976
  }
826
- const cs = this.changeSets.get(Reference.unwrapReference(ref));
827
- const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
977
+ const refEntity = Reference.unwrapReference(ref);
978
+ // For mapToPk properties, the value is a primitive (string/array), not an entity
979
+ if (!Utils.isEntity(refEntity)) {
980
+ continue;
981
+ }
982
+ // For TPT entities, check if the ROOT table's changeset has been persisted
983
+ // (since the FK is to the root table, not the concrete entity's table)
984
+ let cs = this.#changeSets.get(refEntity);
985
+ if (cs?.tptChangeSets?.length) {
986
+ // Root table changeset is the last one (ordered immediate parent → root)
987
+ cs = cs.tptChangeSets[cs.tptChangeSets.length - 1];
988
+ }
989
+ const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
828
990
  if (isScheduledForInsert) {
829
991
  this.scheduleExtraUpdate(changeSet, [prop]);
830
992
  }
@@ -851,14 +1013,14 @@ export class UnitOfWork {
851
1013
  for (const changeSet of changeSets) {
852
1014
  await this.runHooks(EventType.beforeUpdate, changeSet, true);
853
1015
  }
854
- await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
1016
+ await this.#changeSetPersister.executeUpdates(changeSets, batched, { ctx });
855
1017
  for (const changeSet of changeSets) {
856
1018
  const wrapped = helper(changeSet.entity);
857
- wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
858
- wrapped.__touched = false;
1019
+ wrapped.__originalEntityData = this.#comparator.prepareEntity(changeSet.entity);
859
1020
  if (!wrapped.__initialized) {
860
1021
  for (const prop of changeSet.meta.relations) {
861
- if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
1022
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
1023
+ changeSet.entity[prop.name] == null) {
862
1024
  changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
863
1025
  }
864
1026
  }
@@ -874,7 +1036,7 @@ export class UnitOfWork {
874
1036
  for (const changeSet of changeSets) {
875
1037
  await this.runHooks(EventType.beforeDelete, changeSet, true);
876
1038
  }
877
- await this.changeSetPersister.executeDeletes(changeSets, { ctx });
1039
+ await this.#changeSetPersister.executeDeletes(changeSets, { ctx });
878
1040
  for (const changeSet of changeSets) {
879
1041
  this.unsetIdentity(changeSet.entity);
880
1042
  await this.runHooks(EventType.afterDelete, changeSet);
@@ -882,17 +1044,17 @@ export class UnitOfWork {
882
1044
  }
883
1045
  async commitExtraUpdates(type, ctx) {
884
1046
  const extraUpdates = [];
885
- for (const extraUpdate of this.extraUpdates) {
1047
+ for (const extraUpdate of this.#extraUpdates) {
886
1048
  if (extraUpdate[4] !== type) {
887
1049
  continue;
888
1050
  }
889
1051
  if (Array.isArray(extraUpdate[1])) {
890
- extraUpdate[1].forEach((p, i) => extraUpdate[0][p] = extraUpdate[2][i]);
1052
+ extraUpdate[1].forEach((p, i) => (extraUpdate[0][p] = extraUpdate[2][i]));
891
1053
  }
892
1054
  else {
893
1055
  extraUpdate[0][extraUpdate[1]] = extraUpdate[2];
894
1056
  }
895
- const changeSet = this.changeSetComputer.computeChangeSet(extraUpdate[0]);
1057
+ const changeSet = this.#changeSetComputer.computeChangeSet(extraUpdate[0]);
896
1058
  if (changeSet) {
897
1059
  extraUpdates.push([changeSet, extraUpdate[3]]);
898
1060
  }
@@ -907,21 +1069,21 @@ export class UnitOfWork {
907
1069
  }
908
1070
  async commitCollectionUpdates(ctx) {
909
1071
  this.filterCollectionUpdates();
910
- const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
911
- await this.em.getDriver().syncCollections(this.collectionUpdates, {
1072
+ const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
1073
+ await this.#em.getDriver().syncCollections(this.#collectionUpdates, {
912
1074
  ctx,
913
- schema: this.em.schema,
1075
+ schema: this.#em.schema,
914
1076
  loggerContext,
915
1077
  });
916
- for (const coll of this.collectionUpdates) {
1078
+ for (const coll of this.#collectionUpdates) {
917
1079
  coll.takeSnapshot();
918
1080
  }
919
1081
  }
920
1082
  filterCollectionUpdates() {
921
- for (const coll of this.collectionUpdates) {
1083
+ for (const coll of this.#collectionUpdates) {
922
1084
  let skip = true;
923
1085
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
924
- if (this.platform.usesPivotTable()) {
1086
+ if (this.#platform.usesPivotTable()) {
925
1087
  skip = false;
926
1088
  }
927
1089
  }
@@ -932,7 +1094,7 @@ export class UnitOfWork {
932
1094
  skip = false;
933
1095
  }
934
1096
  if (skip) {
935
- this.collectionUpdates.delete(coll);
1097
+ this.#collectionUpdates.delete(coll);
936
1098
  }
937
1099
  }
938
1100
  }
@@ -947,12 +1109,24 @@ export class UnitOfWork {
947
1109
  [ChangeSetType.UPDATE_EARLY]: new Map(),
948
1110
  [ChangeSetType.DELETE_EARLY]: new Map(),
949
1111
  };
950
- for (const cs of this.changeSets.values()) {
1112
+ const addToGroup = (cs) => {
1113
+ // Skip stub TPT changesets with empty payload (e.g. leaf with no own-property changes on UPDATE)
1114
+ if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) &&
1115
+ !Utils.hasObjectKeys(cs.payload)) {
1116
+ return;
1117
+ }
951
1118
  const group = groups[cs.type];
952
- const classGroup = group.get(cs.rootName) ?? [];
1119
+ const groupKey = cs.meta.inheritanceType === 'tpt' ? cs.meta : cs.rootMeta;
1120
+ const classGroup = group.get(groupKey) ?? [];
953
1121
  classGroup.push(cs);
954
- if (!group.has(cs.rootName)) {
955
- group.set(cs.rootName, classGroup);
1122
+ if (!group.has(groupKey)) {
1123
+ group.set(groupKey, classGroup);
1124
+ }
1125
+ };
1126
+ for (const cs of this.#changeSets.values()) {
1127
+ addToGroup(cs);
1128
+ for (const parentCs of cs.tptChangeSets ?? []) {
1129
+ addToGroup(parentCs);
956
1130
  }
957
1131
  }
958
1132
  return groups;
@@ -960,21 +1134,42 @@ export class UnitOfWork {
960
1134
  getCommitOrder() {
961
1135
  const calc = new CommitOrderCalculator();
962
1136
  const set = new Set();
963
- this.changeSets.forEach(cs => set.add(cs.rootName));
964
- set.forEach(entityName => calc.addNode(entityName));
965
- for (const entityName of set) {
966
- for (const prop of this.metadata.find(entityName).props) {
967
- calc.discoverProperty(prop, entityName);
1137
+ this.#changeSets.forEach(cs => {
1138
+ if (cs.meta.inheritanceType === 'tpt') {
1139
+ set.add(cs.meta);
1140
+ for (const parentCs of cs.tptChangeSets ?? []) {
1141
+ set.add(parentCs.meta);
1142
+ }
1143
+ }
1144
+ else {
1145
+ set.add(cs.rootMeta);
1146
+ }
1147
+ });
1148
+ set.forEach(meta => calc.addNode(meta._id));
1149
+ for (const meta of set) {
1150
+ for (const prop of meta.relations) {
1151
+ if (prop.polymorphTargets) {
1152
+ for (const targetMeta of prop.polymorphTargets) {
1153
+ calc.discoverProperty({ ...prop, targetMeta }, meta._id);
1154
+ }
1155
+ }
1156
+ else {
1157
+ calc.discoverProperty(prop, meta._id);
1158
+ }
1159
+ }
1160
+ // For TPT, parent table must be inserted BEFORE child tables
1161
+ if (meta.inheritanceType === 'tpt' && meta.tptParent && set.has(meta.tptParent)) {
1162
+ calc.addDependency(meta.tptParent._id, meta._id, 1);
968
1163
  }
969
1164
  }
970
- return calc.sort();
1165
+ return calc.sort().map(id => this.#metadata.getById(id));
971
1166
  }
972
1167
  resetTransaction(oldTx) {
973
1168
  if (oldTx) {
974
- this.em.setTransactionContext(oldTx);
1169
+ this.#em.setTransactionContext(oldTx);
975
1170
  }
976
1171
  else {
977
- this.em.resetTransactionContext();
1172
+ this.#em.resetTransactionContext();
978
1173
  }
979
1174
  }
980
1175
  /**