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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/EntityManager.d.ts +114 -63
  2. package/EntityManager.js +385 -310
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -143
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +17 -8
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +0 -1
  11. package/cache/index.js +0 -1
  12. package/connections/Connection.d.ts +16 -7
  13. package/connections/Connection.js +23 -14
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +119 -36
  16. package/drivers/IDatabaseDriver.d.ts +125 -23
  17. package/entity/BaseEntity.d.ts +63 -4
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +102 -31
  20. package/entity/Collection.js +446 -108
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +13 -1
  24. package/entity/EntityFactory.js +106 -60
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +65 -20
  27. package/entity/EntityLoader.d.ts +13 -11
  28. package/entity/EntityLoader.js +257 -107
  29. package/entity/EntityRepository.d.ts +28 -8
  30. package/entity/EntityRepository.js +8 -2
  31. package/entity/PolymorphicRef.d.ts +12 -0
  32. package/entity/PolymorphicRef.js +18 -0
  33. package/entity/Reference.d.ts +9 -12
  34. package/entity/Reference.js +34 -9
  35. package/entity/WrappedEntity.d.ts +3 -8
  36. package/entity/WrappedEntity.js +3 -8
  37. package/entity/defineEntity.d.ts +753 -0
  38. package/entity/defineEntity.js +537 -0
  39. package/entity/index.d.ts +4 -2
  40. package/entity/index.js +4 -2
  41. package/entity/utils.d.ts +13 -1
  42. package/entity/utils.js +49 -4
  43. package/entity/validators.d.ts +11 -0
  44. package/entity/validators.js +65 -0
  45. package/enums.d.ts +23 -8
  46. package/enums.js +15 -1
  47. package/errors.d.ts +25 -9
  48. package/errors.js +67 -21
  49. package/events/EventManager.d.ts +2 -1
  50. package/events/EventManager.js +19 -11
  51. package/events/EventSubscriber.d.ts +3 -1
  52. package/hydration/Hydrator.js +1 -2
  53. package/hydration/ObjectHydrator.d.ts +4 -4
  54. package/hydration/ObjectHydrator.js +89 -36
  55. package/index.d.ts +2 -2
  56. package/index.js +1 -2
  57. package/logging/DefaultLogger.d.ts +1 -1
  58. package/logging/DefaultLogger.js +1 -0
  59. package/logging/SimpleLogger.d.ts +1 -1
  60. package/logging/colors.d.ts +1 -1
  61. package/logging/colors.js +7 -6
  62. package/logging/index.d.ts +1 -0
  63. package/logging/index.js +1 -0
  64. package/logging/inspect.d.ts +2 -0
  65. package/logging/inspect.js +11 -0
  66. package/metadata/EntitySchema.d.ts +53 -27
  67. package/metadata/EntitySchema.js +125 -52
  68. package/metadata/MetadataDiscovery.d.ts +64 -10
  69. package/metadata/MetadataDiscovery.js +823 -344
  70. package/metadata/MetadataProvider.d.ts +11 -2
  71. package/metadata/MetadataProvider.js +66 -2
  72. package/metadata/MetadataStorage.d.ts +13 -11
  73. package/metadata/MetadataStorage.js +71 -38
  74. package/metadata/MetadataValidator.d.ts +32 -9
  75. package/metadata/MetadataValidator.js +198 -42
  76. package/metadata/discover-entities.d.ts +5 -0
  77. package/metadata/discover-entities.js +40 -0
  78. package/metadata/index.d.ts +1 -1
  79. package/metadata/index.js +1 -1
  80. package/metadata/types.d.ts +577 -0
  81. package/metadata/types.js +1 -0
  82. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  83. package/naming-strategy/AbstractNamingStrategy.js +20 -2
  84. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  85. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  86. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  87. package/naming-strategy/MongoNamingStrategy.js +6 -6
  88. package/naming-strategy/NamingStrategy.d.ts +28 -4
  89. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  90. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  91. package/not-supported.d.ts +2 -0
  92. package/not-supported.js +4 -0
  93. package/package.json +22 -11
  94. package/platforms/ExceptionConverter.js +1 -1
  95. package/platforms/Platform.d.ts +14 -16
  96. package/platforms/Platform.js +24 -44
  97. package/serialization/EntitySerializer.d.ts +8 -3
  98. package/serialization/EntitySerializer.js +47 -27
  99. package/serialization/EntityTransformer.js +33 -21
  100. package/serialization/SerializationContext.d.ts +6 -6
  101. package/serialization/SerializationContext.js +16 -13
  102. package/types/ArrayType.d.ts +1 -1
  103. package/types/ArrayType.js +2 -3
  104. package/types/BigIntType.d.ts +9 -6
  105. package/types/BigIntType.js +4 -1
  106. package/types/BlobType.d.ts +0 -1
  107. package/types/BlobType.js +0 -3
  108. package/types/BooleanType.d.ts +2 -1
  109. package/types/BooleanType.js +3 -0
  110. package/types/DecimalType.d.ts +6 -4
  111. package/types/DecimalType.js +3 -3
  112. package/types/DoubleType.js +2 -2
  113. package/types/EnumArrayType.js +1 -2
  114. package/types/JsonType.d.ts +1 -1
  115. package/types/JsonType.js +7 -2
  116. package/types/TinyIntType.js +1 -1
  117. package/types/Type.d.ts +2 -4
  118. package/types/Type.js +3 -3
  119. package/types/Uint8ArrayType.d.ts +0 -1
  120. package/types/Uint8ArrayType.js +1 -4
  121. package/types/index.d.ts +1 -1
  122. package/typings.d.ts +469 -175
  123. package/typings.js +120 -45
  124. package/unit-of-work/ChangeSet.d.ts +4 -6
  125. package/unit-of-work/ChangeSet.js +4 -5
  126. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  127. package/unit-of-work/ChangeSetComputer.js +44 -21
  128. package/unit-of-work/ChangeSetPersister.d.ts +15 -12
  129. package/unit-of-work/ChangeSetPersister.js +113 -45
  130. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  131. package/unit-of-work/CommitOrderCalculator.js +13 -13
  132. package/unit-of-work/IdentityMap.d.ts +12 -0
  133. package/unit-of-work/IdentityMap.js +39 -1
  134. package/unit-of-work/UnitOfWork.d.ts +28 -3
  135. package/unit-of-work/UnitOfWork.js +315 -110
  136. package/utils/AbstractMigrator.d.ts +101 -0
  137. package/utils/AbstractMigrator.js +305 -0
  138. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  139. package/utils/AbstractSchemaGenerator.js +32 -18
  140. package/utils/AsyncContext.d.ts +6 -0
  141. package/utils/AsyncContext.js +42 -0
  142. package/utils/Configuration.d.ts +801 -207
  143. package/utils/Configuration.js +150 -191
  144. package/utils/ConfigurationLoader.d.ts +1 -54
  145. package/utils/ConfigurationLoader.js +1 -352
  146. package/utils/Cursor.d.ts +3 -6
  147. package/utils/Cursor.js +27 -11
  148. package/utils/DataloaderUtils.d.ts +15 -5
  149. package/utils/DataloaderUtils.js +65 -17
  150. package/utils/EntityComparator.d.ts +21 -10
  151. package/utils/EntityComparator.js +243 -106
  152. package/utils/QueryHelper.d.ts +24 -6
  153. package/utils/QueryHelper.js +122 -26
  154. package/utils/RawQueryFragment.d.ts +60 -32
  155. package/utils/RawQueryFragment.js +69 -66
  156. package/utils/RequestContext.js +2 -2
  157. package/utils/TransactionContext.js +2 -2
  158. package/utils/TransactionManager.d.ts +65 -0
  159. package/utils/TransactionManager.js +223 -0
  160. package/utils/Utils.d.ts +15 -122
  161. package/utils/Utils.js +108 -376
  162. package/utils/clone.js +8 -23
  163. package/utils/env-vars.d.ts +7 -0
  164. package/utils/env-vars.js +97 -0
  165. package/utils/fs-utils.d.ts +34 -0
  166. package/utils/fs-utils.js +196 -0
  167. package/utils/index.d.ts +2 -3
  168. package/utils/index.js +2 -3
  169. package/utils/upsert-utils.d.ts +9 -4
  170. package/utils/upsert-utils.js +55 -4
  171. package/decorators/Check.d.ts +0 -3
  172. package/decorators/Check.js +0 -13
  173. package/decorators/CreateRequestContext.d.ts +0 -3
  174. package/decorators/CreateRequestContext.js +0 -32
  175. package/decorators/Embeddable.d.ts +0 -8
  176. package/decorators/Embeddable.js +0 -11
  177. package/decorators/Embedded.d.ts +0 -18
  178. package/decorators/Embedded.js +0 -18
  179. package/decorators/Entity.d.ts +0 -18
  180. package/decorators/Entity.js +0 -13
  181. package/decorators/Enum.d.ts +0 -9
  182. package/decorators/Enum.js +0 -16
  183. package/decorators/Filter.d.ts +0 -2
  184. package/decorators/Filter.js +0 -8
  185. package/decorators/Formula.d.ts +0 -5
  186. package/decorators/Formula.js +0 -15
  187. package/decorators/Indexed.d.ts +0 -17
  188. package/decorators/Indexed.js +0 -20
  189. package/decorators/ManyToMany.d.ts +0 -40
  190. package/decorators/ManyToMany.js +0 -14
  191. package/decorators/ManyToOne.d.ts +0 -30
  192. package/decorators/ManyToOne.js +0 -14
  193. package/decorators/OneToMany.d.ts +0 -28
  194. package/decorators/OneToMany.js +0 -17
  195. package/decorators/OneToOne.d.ts +0 -24
  196. package/decorators/OneToOne.js +0 -7
  197. package/decorators/PrimaryKey.d.ts +0 -9
  198. package/decorators/PrimaryKey.js +0 -20
  199. package/decorators/Property.d.ts +0 -250
  200. package/decorators/Property.js +0 -32
  201. package/decorators/Transactional.d.ts +0 -13
  202. package/decorators/Transactional.js +0 -28
  203. package/decorators/hooks.d.ts +0 -16
  204. package/decorators/hooks.js +0 -47
  205. package/decorators/index.d.ts +0 -17
  206. package/decorators/index.js +0 -17
  207. package/entity/ArrayCollection.d.ts +0 -116
  208. package/entity/ArrayCollection.js +0 -395
  209. package/entity/EntityValidator.d.ts +0 -19
  210. package/entity/EntityValidator.js +0 -150
  211. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  212. package/metadata/ReflectMetadataProvider.js +0 -44
  213. package/utils/resolveContextProvider.d.ts +0 -10
  214. package/utils/resolveContextProvider.js +0 -28
@@ -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,8 +12,9 @@ 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
19
  em;
20
20
  /** map of references to managed entities */
@@ -42,8 +42,8 @@ export class UnitOfWork {
42
42
  this.identityMap = new IdentityMap(this.platform.getDefaultSchemaName());
43
43
  this.eventManager = this.em.getEventManager();
44
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);
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);
@@ -61,10 +61,46 @@ export class UnitOfWork {
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
63
  wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
64
- wrapped.__touched = false;
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) && Utils.isPlainObject(data[prop.name])) {
80
+ // Skip polymorphic relations - they use PolymorphicRef wrapper
81
+ if (!prop.polymorphic) {
82
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
83
+ }
84
+ }
85
+ else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
86
+ for (const p of prop.targetMeta.props) {
87
+ /* v8 ignore next */
88
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
89
+ data[prefix + p.name] = data[prop.name][p.name];
90
+ }
91
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
92
+ }
93
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
94
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
95
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
96
+ }
97
+ if (forceUndefined) {
98
+ if (data[key] === null) {
99
+ data[key] = undefined;
100
+ }
101
+ }
102
+ }
103
+ }
68
104
  /**
69
105
  * @internal
70
106
  */
@@ -82,31 +118,14 @@ export class UnitOfWork {
82
118
  wrapped.__em ??= this.em;
83
119
  wrapped.__managed = true;
84
120
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
121
+ this.normalizeEntityData(wrapped.__meta, data);
85
122
  for (const key of Utils.keys(data)) {
86
123
  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.primaryKeys, 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.primaryKeys, true);
101
- }
102
- if (forceUndefined) {
103
- if (data[key] === null) {
104
- data[key] = undefined;
105
- }
124
+ if (prop) {
125
+ wrapped.__loadedProperties.add(key);
106
126
  }
107
127
  }
108
128
  wrapped.__originalEntityData = data;
109
- wrapped.__touched = false;
110
129
  }
111
130
  return entity;
112
131
  }
@@ -125,7 +144,7 @@ export class UnitOfWork {
125
144
  /**
126
145
  * 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`.
127
146
  */
128
- getById(entityName, id, schema) {
147
+ getById(entityName, id, schema, convertCustomTypes) {
129
148
  if (id == null || (Array.isArray(id) && id.length === 0)) {
130
149
  return undefined;
131
150
  }
@@ -135,7 +154,16 @@ export class UnitOfWork {
135
154
  hash = '' + id;
136
155
  }
137
156
  else {
138
- const keys = Array.isArray(id) ? Utils.flatten(id) : [id];
157
+ let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
158
+ keys = meta.getPrimaryProps(true).map((p, i) => {
159
+ if (!convertCustomTypes && p.customType) {
160
+ return p.customType.convertToDatabaseValue(keys[i], this.platform, {
161
+ key: p.name,
162
+ mode: 'hydration',
163
+ });
164
+ }
165
+ return keys[i];
166
+ });
139
167
  hash = Utils.getPrimaryKeyHash(keys);
140
168
  }
141
169
  schema ??= meta.schema ?? this.em.config.getSchema();
@@ -144,6 +172,40 @@ export class UnitOfWork {
144
172
  }
145
173
  return this.identityMap.getByHash(meta, hash);
146
174
  }
175
+ /**
176
+ * Returns entity from the identity map by an alternate key (non-PK property).
177
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
178
+ * If false (default), the value is assumed to be in JS format already.
179
+ */
180
+ getByKey(entityName, key, value, schema, convertCustomTypes) {
181
+ const meta = this.metadata.find(entityName).root;
182
+ schema ??= meta.schema ?? this.em.config.getSchema();
183
+ const prop = meta.properties[key];
184
+ // Convert from DB format to JS format if needed
185
+ if (convertCustomTypes && prop?.customType) {
186
+ value = prop.customType.convertToJSValue(value, this.platform, { mode: 'hydration' });
187
+ }
188
+ const hash = this.identityMap.getKeyHash(key, '' + value, schema);
189
+ return this.identityMap.getByHash(meta, hash);
190
+ }
191
+ /**
192
+ * Stores an entity in the identity map under an alternate key (non-PK property).
193
+ * Also sets the property value on the entity.
194
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
195
+ * If false (default), the value is assumed to be in JS format already.
196
+ */
197
+ storeByKey(entity, key, value, schema, convertCustomTypes) {
198
+ const meta = entity.__meta.root;
199
+ schema ??= meta.schema ?? this.em.config.getSchema();
200
+ const prop = meta.properties[key];
201
+ // Convert from DB format to JS format if needed
202
+ if (convertCustomTypes && prop?.customType) {
203
+ value = prop.customType.convertToJSValue(value, this.platform, { mode: 'hydration' });
204
+ }
205
+ // Set the property on the entity
206
+ entity[key] = value;
207
+ this.identityMap.storeByKey(entity, key, '' + value, schema);
208
+ }
147
209
  tryGetById(entityName, where, schema, strict = true) {
148
210
  const pk = Utils.extractPK(where, this.metadata.find(entityName), strict);
149
211
  if (!pk) {
@@ -182,13 +244,11 @@ export class UnitOfWork {
182
244
  if (insideFlush.getStore()) {
183
245
  return false;
184
246
  }
185
- if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
247
+ if (this.queuedActions.has(meta.class) || this.queuedActions.has(meta.root.class)) {
186
248
  return true;
187
249
  }
188
- for (const entity of this.identityMap.getStore(meta).values()) {
189
- if (helper(entity).__initialized && helper(entity).isTouched()) {
190
- return true;
191
- }
250
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
251
+ return true;
192
252
  }
193
253
  return false;
194
254
  }
@@ -197,7 +257,7 @@ export class UnitOfWork {
197
257
  }
198
258
  computeChangeSet(entity, type) {
199
259
  const wrapped = helper(entity);
200
- if (type) {
260
+ if (type === ChangeSetType.DELETE || type === ChangeSetType.DELETE_EARLY) {
201
261
  this.changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
202
262
  return;
203
263
  }
@@ -205,11 +265,14 @@ export class UnitOfWork {
205
265
  if (!cs || this.checkUniqueProps(cs)) {
206
266
  return;
207
267
  }
268
+ /* v8 ignore next */
269
+ if (type) {
270
+ cs.type = type;
271
+ }
208
272
  this.initIdentifier(entity);
209
273
  this.changeSets.set(entity, cs);
210
274
  this.persistStack.delete(entity);
211
275
  wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
212
- wrapped.__touched = false;
213
276
  }
214
277
  recomputeSingleChangeSet(entity) {
215
278
  const changeSet = this.changeSets.get(entity);
@@ -220,7 +283,6 @@ export class UnitOfWork {
220
283
  if (cs && !this.checkUniqueProps(cs)) {
221
284
  Object.assign(changeSet.payload, cs.payload);
222
285
  helper(entity).__originalEntityData = this.comparator.prepareEntity(entity);
223
- helper(entity).__touched = false;
224
286
  }
225
287
  }
226
288
  persist(entity, visited, options = {}) {
@@ -230,7 +292,7 @@ export class UnitOfWork {
230
292
  }
231
293
  const wrapped = helper(entity);
232
294
  this.persistStack.add(entity);
233
- this.queuedActions.add(wrapped.__meta.className);
295
+ this.queuedActions.add(wrapped.__meta.class);
234
296
  this.removeStack.delete(entity);
235
297
  if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
236
298
  this.identityMap.store(entity);
@@ -243,7 +305,7 @@ export class UnitOfWork {
243
305
  // allow removing not managed entities if they are not part of the persist stack
244
306
  if (helper(entity).__managed || !this.persistStack.has(entity)) {
245
307
  this.removeStack.add(entity);
246
- this.queuedActions.add(helper(entity).__meta.className);
308
+ this.queuedActions.add(helper(entity).__meta.class);
247
309
  }
248
310
  else {
249
311
  this.persistStack.delete(entity);
@@ -303,17 +365,21 @@ export class UnitOfWork {
303
365
  cs.entity.__helper.__processing = true;
304
366
  }
305
367
  await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
368
+ this.filterCollectionUpdates();
306
369
  // nothing to do, do not start transaction
307
370
  if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
308
- return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
371
+ await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this });
372
+ return;
309
373
  }
310
374
  const groups = this.getChangeSetGroups();
311
375
  const platform = this.em.getPlatform();
312
376
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
377
  if (runInTransaction) {
378
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
379
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
380
  ctx: oldTx,
316
381
  eventBroadcaster: new TransactionEventBroadcaster(this.em),
382
+ loggerContext,
317
383
  });
318
384
  }
319
385
  else {
@@ -330,10 +396,10 @@ export class UnitOfWork {
330
396
  }
331
397
  }
332
398
  async lock(entity, options) {
333
- if (!this.getById(entity.constructor.name, helper(entity).__primaryKeys, helper(entity).__schema)) {
399
+ if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
334
400
  throw ValidationError.entityNotManaged(entity);
335
401
  }
336
- const meta = this.metadata.find(entity.constructor.name);
402
+ const meta = this.metadata.find(entity.constructor);
337
403
  if (options.lockMode === LockMode.OPTIMISTIC) {
338
404
  await this.lockOptimistic(entity, meta, options.lockVersion);
339
405
  }
@@ -357,7 +423,7 @@ export class UnitOfWork {
357
423
  if (Utils.isCollection(rel)) {
358
424
  rel.removeWithoutPropagation(entity);
359
425
  }
360
- else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.type, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
426
+ else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
361
427
  if (prop.formula) {
362
428
  delete referrer[prop.name];
363
429
  }
@@ -369,7 +435,6 @@ export class UnitOfWork {
369
435
  }
370
436
  delete wrapped.__identifier;
371
437
  delete wrapped.__originalEntityData;
372
- wrapped.__touched = false;
373
438
  wrapped.__managed = false;
374
439
  }
375
440
  computeChangeSets() {
@@ -379,14 +444,14 @@ export class UnitOfWork {
379
444
  this.cascade(entity, Cascade.REMOVE, visited);
380
445
  }
381
446
  visited.clear();
382
- for (const entity of this.persistStack) {
383
- this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
384
- }
385
447
  for (const entity of this.identityMap) {
386
448
  if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
387
449
  this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
388
450
  }
389
451
  }
452
+ for (const entity of this.persistStack) {
453
+ this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
454
+ }
390
455
  visited.clear();
391
456
  for (const entity of this.persistStack) {
392
457
  this.findNewEntities(entity, visited);
@@ -400,24 +465,24 @@ export class UnitOfWork {
400
465
  const inserts = {};
401
466
  for (const cs of this.changeSets.values()) {
402
467
  if (cs.type === ChangeSetType.CREATE) {
403
- inserts[cs.meta.className] ??= [];
404
- inserts[cs.meta.className].push(cs);
468
+ inserts[cs.meta.uniqueName] ??= [];
469
+ inserts[cs.meta.uniqueName].push(cs);
405
470
  }
406
471
  }
407
472
  for (const cs of this.changeSets.values()) {
408
473
  if (cs.type === ChangeSetType.UPDATE) {
409
- this.findEarlyUpdates(cs, inserts[cs.meta.className]);
474
+ this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
410
475
  }
411
476
  }
412
477
  for (const entity of this.removeStack) {
413
478
  const wrapped = helper(entity);
414
- /* v8 ignore next 3 */
479
+ /* v8 ignore next */
415
480
  if (wrapped.__processing) {
416
481
  continue;
417
482
  }
418
483
  const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
419
484
  let type = ChangeSetType.DELETE;
420
- for (const cs of inserts[wrapped.__meta.className] ?? []) {
485
+ for (const cs of inserts[wrapped.__meta.uniqueName] ?? []) {
421
486
  if (deletePkHash.some(hash => hash === cs.getSerializedPrimaryKey() || this.expandUniqueProps(cs.entity).find(child => hash === child))) {
422
487
  type = ChangeSetType.DELETE_EARLY;
423
488
  }
@@ -436,7 +501,7 @@ export class UnitOfWork {
436
501
  }
437
502
  for (const cs of this.changeSets.values()) {
438
503
  for (const prop of props) {
439
- if (prop.name in cs.payload && cs.rootName === changeSet.rootName && cs.type === changeSet.type) {
504
+ if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
440
505
  conflicts = true;
441
506
  if (changeSet.payload[prop.name] == null) {
442
507
  type = ChangeSetType.UPDATE_EARLY;
@@ -455,9 +520,10 @@ export class UnitOfWork {
455
520
  }
456
521
  scheduleOrphanRemoval(entity, visited) {
457
522
  if (entity) {
458
- helper(entity).__em = this.em;
523
+ const wrapped = helper(entity);
524
+ wrapped.__em = this.em;
459
525
  this.orphanRemoveStack.add(entity);
460
- this.queuedActions.add(entity.__meta.className);
526
+ this.queuedActions.add(wrapped.__meta.class);
461
527
  this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
462
528
  }
463
529
  }
@@ -493,7 +559,68 @@ export class UnitOfWork {
493
559
  }
494
560
  const changeSet = this.changeSetComputer.computeChangeSet(entity);
495
561
  if (changeSet && !this.checkUniqueProps(changeSet)) {
496
- this.changeSets.set(entity, changeSet);
562
+ // For TPT child entities, create changesets for each table in hierarchy
563
+ if (wrapped.__meta.inheritanceType === 'tpt' && wrapped.__meta.tptParent) {
564
+ this.createTPTChangeSets(entity, changeSet);
565
+ }
566
+ else {
567
+ this.changeSets.set(entity, changeSet);
568
+ }
569
+ }
570
+ }
571
+ /**
572
+ * For TPT inheritance, creates separate changesets for each table in the hierarchy.
573
+ * Uses the same entity instance for all changesets - only the metadata and payload differ.
574
+ */
575
+ createTPTChangeSets(entity, originalChangeSet) {
576
+ const meta = helper(entity).__meta;
577
+ const isCreate = originalChangeSet.type === ChangeSetType.CREATE;
578
+ let current = meta;
579
+ let leafCs;
580
+ const parentChangeSets = [];
581
+ while (current) {
582
+ const isRoot = !current.tptParent;
583
+ const payload = {};
584
+ for (const prop of current.ownProps) {
585
+ if (prop.name in originalChangeSet.payload) {
586
+ payload[prop.name] = originalChangeSet.payload[prop.name];
587
+ }
588
+ }
589
+ // For CREATE on non-root tables, include the PK (EntityIdentifier for deferred resolution)
590
+ if (isCreate && !isRoot) {
591
+ const wrapped = helper(entity);
592
+ const identifier = wrapped.__identifier;
593
+ const identifiers = Array.isArray(identifier) ? identifier : [identifier];
594
+ for (let i = 0; i < current.primaryKeys.length; i++) {
595
+ const pk = current.primaryKeys[i];
596
+ payload[pk] = identifiers[i] ?? originalChangeSet.payload[pk];
597
+ }
598
+ }
599
+ if (!isCreate && Object.keys(payload).length === 0) {
600
+ current = current.tptParent;
601
+ continue;
602
+ }
603
+ const cs = new ChangeSet(entity, originalChangeSet.type, payload, current);
604
+ if (current === meta) {
605
+ cs.originalEntity = originalChangeSet.originalEntity;
606
+ leafCs = cs;
607
+ }
608
+ else {
609
+ parentChangeSets.push(cs);
610
+ }
611
+ current = current.tptParent;
612
+ }
613
+ // When only parent properties changed (UPDATE), leaf payload is empty—create a stub anchor
614
+ if (!leafCs && parentChangeSets.length > 0) {
615
+ leafCs = new ChangeSet(entity, originalChangeSet.type, {}, meta);
616
+ leafCs.originalEntity = originalChangeSet.originalEntity;
617
+ }
618
+ // Store the leaf changeset in the main map (entity as key), with parent CSs attached
619
+ if (leafCs) {
620
+ if (parentChangeSets.length > 0) {
621
+ leafCs.tptChangeSets = parentChangeSets;
622
+ }
623
+ this.changeSets.set(entity, leafCs);
497
624
  }
498
625
  }
499
626
  /**
@@ -542,13 +669,22 @@ export class UnitOfWork {
542
669
  if (!wrapped || wrapped.__identifier || wrapped.hasPrimaryKey()) {
543
670
  return;
544
671
  }
545
- const pk = wrapped.__meta.getPrimaryProps()[0];
546
- if (pk.kind === ReferenceKind.SCALAR) {
547
- wrapped.__identifier = new EntityIdentifier();
672
+ const pks = wrapped.__meta.getPrimaryProps();
673
+ const idents = [];
674
+ for (const pk of pks) {
675
+ if (pk.kind === ReferenceKind.SCALAR) {
676
+ idents.push(new EntityIdentifier(entity[pk.name]));
677
+ }
678
+ else if (entity[pk.name]) {
679
+ this.initIdentifier(entity[pk.name]);
680
+ idents.push(helper(entity[pk.name])?.__identifier);
681
+ }
548
682
  }
549
- else if (entity[pk.name]) {
550
- this.initIdentifier(entity[pk.name]);
551
- wrapped.__identifier = helper(entity[pk.name])?.__identifier;
683
+ if (pks.length === 1) {
684
+ wrapped.__identifier = idents[0];
685
+ }
686
+ else {
687
+ wrapped.__identifier = idents;
552
688
  }
553
689
  }
554
690
  processReference(parent, prop, kind, visited, processed, idx) {
@@ -597,11 +733,18 @@ export class UnitOfWork {
597
733
  const copy = this.comparator.prepareEntity(changeSet.entity);
598
734
  await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
599
735
  const current = this.comparator.prepareEntity(changeSet.entity);
600
- const diff = this.comparator.diffEntities(changeSet.name, copy, current);
736
+ const diff = this.comparator.diffEntities(changeSet.meta.class, copy, current);
601
737
  Object.assign(changeSet.payload, diff);
602
738
  const wrapped = helper(changeSet.entity);
603
- if (wrapped.__identifier && diff[wrapped.__meta.primaryKeys[0]]) {
604
- wrapped.__identifier.setValue(diff[wrapped.__meta.primaryKeys[0]]);
739
+ if (wrapped.__identifier) {
740
+ const idents = Utils.asArray(wrapped.__identifier);
741
+ let i = 0;
742
+ for (const pk of wrapped.__meta.primaryKeys) {
743
+ if (diff[pk]) {
744
+ idents[i].setValue(diff[pk]);
745
+ }
746
+ i++;
747
+ }
605
748
  }
606
749
  }
607
750
  postCommitCleanup() {
@@ -685,7 +828,7 @@ export class UnitOfWork {
685
828
  if (!meta.versionProperty) {
686
829
  throw OptimisticLockError.notVersioned(meta);
687
830
  }
688
- if (!Utils.isDefined(version)) {
831
+ if (typeof version === 'undefined') {
689
832
  return;
690
833
  }
691
834
  const wrapped = helper(entity);
@@ -699,26 +842,26 @@ export class UnitOfWork {
699
842
  }
700
843
  fixMissingReference(entity, prop) {
701
844
  const reference = entity[prop.name];
702
- const kind = Reference.unwrapReference(reference);
703
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && kind && !prop.mapToPk) {
704
- if (!Utils.isEntity(kind)) {
705
- entity[prop.name] = this.em.getReference(prop.type, kind, { wrapped: !!prop.ref });
845
+ const target = Reference.unwrapReference(reference);
846
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
847
+ if (!Utils.isEntity(target)) {
848
+ entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, { wrapped: !!prop.ref });
706
849
  }
707
- else if (!helper(kind).__initialized && !helper(kind).__em) {
708
- const pk = helper(kind).getPrimaryKey();
709
- entity[prop.name] = this.em.getReference(prop.type, pk, { wrapped: !!prop.ref });
850
+ else if (!helper(target).__initialized && !helper(target).__em) {
851
+ const pk = helper(target).getPrimaryKey();
852
+ entity[prop.name] = this.em.getReference(prop.targetMeta.class, pk, { wrapped: !!prop.ref });
710
853
  }
711
854
  }
712
- // perf: set the `Collection._property` to skip the getter, as it can be slow when there is a lot of relations
713
- if (Utils.isCollection(kind)) {
714
- kind.property = prop;
855
+ // perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
856
+ if (Utils.isCollection(target)) {
857
+ target.property = prop;
715
858
  }
716
859
  const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
717
- if (isCollection && Array.isArray(kind)) {
860
+ if (isCollection && Array.isArray(target)) {
718
861
  const collection = new Collection(entity);
719
862
  collection.property = prop;
720
863
  entity[prop.name] = collection;
721
- collection.set(kind);
864
+ collection.set(target);
722
865
  }
723
866
  }
724
867
  async persistToDatabase(groups, ctx) {
@@ -728,30 +871,30 @@ export class UnitOfWork {
728
871
  const commitOrder = this.getCommitOrder();
729
872
  const commitOrderReversed = [...commitOrder].reverse();
730
873
  // early delete - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
731
- for (const name of commitOrderReversed) {
732
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(name) ?? [], ctx);
874
+ for (const meta of commitOrderReversed) {
875
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(meta) ?? [], ctx);
733
876
  }
734
877
  // early update - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
735
- for (const name of commitOrder) {
736
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(name) ?? [], ctx);
878
+ for (const meta of commitOrder) {
879
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(meta) ?? [], ctx);
737
880
  }
738
881
  // extra updates
739
882
  await this.commitExtraUpdates(ChangeSetType.UPDATE_EARLY, ctx);
740
883
  // create
741
- for (const name of commitOrder) {
742
- await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(name) ?? [], ctx);
884
+ for (const meta of commitOrder) {
885
+ await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(meta) ?? [], ctx);
743
886
  }
744
887
  // update
745
- for (const name of commitOrder) {
746
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(name) ?? [], ctx);
888
+ for (const meta of commitOrder) {
889
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(meta) ?? [], ctx);
747
890
  }
748
891
  // extra updates
749
892
  await this.commitExtraUpdates(ChangeSetType.UPDATE, ctx);
750
893
  // collection updates
751
894
  await this.commitCollectionUpdates(ctx);
752
895
  // delete - entity deletions need to be in reverse commit order
753
- for (const name of commitOrderReversed) {
754
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(name) ?? [], ctx);
896
+ for (const meta of commitOrderReversed) {
897
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(meta) ?? [], ctx);
755
898
  }
756
899
  // take snapshots of all persisted collections
757
900
  const visited = new Set();
@@ -787,16 +930,28 @@ export class UnitOfWork {
787
930
  if (Utils.isCollection(ref)) {
788
931
  ref.getItems(false).some(item => {
789
932
  const cs = this.changeSets.get(Reference.unwrapReference(item));
790
- const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
933
+ const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
791
934
  if (isScheduledForInsert) {
792
935
  this.scheduleExtraUpdate(changeSet, [prop]);
793
936
  return true;
794
937
  }
795
938
  return false;
796
939
  });
940
+ continue;
941
+ }
942
+ const refEntity = Reference.unwrapReference(ref);
943
+ // For mapToPk properties, the value is a primitive (string/array), not an entity
944
+ if (!Utils.isEntity(refEntity)) {
945
+ continue;
946
+ }
947
+ // For TPT entities, check if the ROOT table's changeset has been persisted
948
+ // (since the FK is to the root table, not the concrete entity's table)
949
+ let cs = this.changeSets.get(refEntity);
950
+ if (cs?.tptChangeSets?.length) {
951
+ // Root table changeset is the last one (ordered immediate parent → root)
952
+ cs = cs.tptChangeSets[cs.tptChangeSets.length - 1];
797
953
  }
798
- const cs = this.changeSets.get(Reference.unwrapReference(ref));
799
- const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
954
+ const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
800
955
  if (isScheduledForInsert) {
801
956
  this.scheduleExtraUpdate(changeSet, [prop]);
802
957
  }
@@ -825,9 +980,16 @@ export class UnitOfWork {
825
980
  }
826
981
  await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
827
982
  for (const changeSet of changeSets) {
828
- helper(changeSet.entity).__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
829
- helper(changeSet.entity).__touched = false;
830
- helper(changeSet.entity).__initialized = true;
983
+ const wrapped = helper(changeSet.entity);
984
+ wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
985
+ if (!wrapped.__initialized) {
986
+ for (const prop of changeSet.meta.relations) {
987
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
988
+ changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
989
+ }
990
+ }
991
+ wrapped.__initialized = true;
992
+ }
831
993
  await this.runHooks(EventType.afterUpdate, changeSet);
832
994
  }
833
995
  }
@@ -870,23 +1032,34 @@ export class UnitOfWork {
870
1032
  }
871
1033
  }
872
1034
  async commitCollectionUpdates(ctx) {
873
- const collectionUpdates = [];
1035
+ this.filterCollectionUpdates();
1036
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
1037
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
1038
+ ctx,
1039
+ schema: this.em.schema,
1040
+ loggerContext,
1041
+ });
1042
+ for (const coll of this.collectionUpdates) {
1043
+ coll.takeSnapshot();
1044
+ }
1045
+ }
1046
+ filterCollectionUpdates() {
874
1047
  for (const coll of this.collectionUpdates) {
1048
+ let skip = true;
875
1049
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
876
1050
  if (this.platform.usesPivotTable()) {
877
- collectionUpdates.push(coll);
1051
+ skip = false;
878
1052
  }
879
1053
  }
880
1054
  else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
881
- collectionUpdates.push(coll);
1055
+ skip = false;
882
1056
  }
883
1057
  else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
884
- collectionUpdates.push(coll);
1058
+ skip = false;
1059
+ }
1060
+ if (skip) {
1061
+ this.collectionUpdates.delete(coll);
885
1062
  }
886
- }
887
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx });
888
- for (const coll of this.collectionUpdates) {
889
- coll.takeSnapshot();
890
1063
  }
891
1064
  }
892
1065
  /**
@@ -900,12 +1073,23 @@ export class UnitOfWork {
900
1073
  [ChangeSetType.UPDATE_EARLY]: new Map(),
901
1074
  [ChangeSetType.DELETE_EARLY]: new Map(),
902
1075
  };
903
- for (const cs of this.changeSets.values()) {
1076
+ const addToGroup = (cs) => {
1077
+ // Skip stub TPT changesets with empty payload (e.g. leaf with no own-property changes on UPDATE)
1078
+ if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) && !Utils.hasObjectKeys(cs.payload)) {
1079
+ return;
1080
+ }
904
1081
  const group = groups[cs.type];
905
- const classGroup = group.get(cs.rootName) ?? [];
1082
+ const groupKey = cs.meta.inheritanceType === 'tpt' ? cs.meta : cs.rootMeta;
1083
+ const classGroup = group.get(groupKey) ?? [];
906
1084
  classGroup.push(cs);
907
- if (!group.has(cs.rootName)) {
908
- group.set(cs.rootName, classGroup);
1085
+ if (!group.has(groupKey)) {
1086
+ group.set(groupKey, classGroup);
1087
+ }
1088
+ };
1089
+ for (const cs of this.changeSets.values()) {
1090
+ addToGroup(cs);
1091
+ for (const parentCs of cs.tptChangeSets ?? []) {
1092
+ addToGroup(parentCs);
909
1093
  }
910
1094
  }
911
1095
  return groups;
@@ -913,14 +1097,35 @@ export class UnitOfWork {
913
1097
  getCommitOrder() {
914
1098
  const calc = new CommitOrderCalculator();
915
1099
  const set = new Set();
916
- this.changeSets.forEach(cs => set.add(cs.rootName));
917
- set.forEach(entityName => calc.addNode(entityName));
918
- for (const entityName of set) {
919
- for (const prop of this.metadata.find(entityName).props) {
920
- calc.discoverProperty(prop, entityName);
1100
+ this.changeSets.forEach(cs => {
1101
+ if (cs.meta.inheritanceType === 'tpt') {
1102
+ set.add(cs.meta);
1103
+ for (const parentCs of cs.tptChangeSets ?? []) {
1104
+ set.add(parentCs.meta);
1105
+ }
1106
+ }
1107
+ else {
1108
+ set.add(cs.rootMeta);
1109
+ }
1110
+ });
1111
+ set.forEach(meta => calc.addNode(meta._id));
1112
+ for (const meta of set) {
1113
+ for (const prop of meta.relations) {
1114
+ if (prop.polymorphTargets) {
1115
+ for (const targetMeta of prop.polymorphTargets) {
1116
+ calc.discoverProperty({ ...prop, targetMeta }, meta._id);
1117
+ }
1118
+ }
1119
+ else {
1120
+ calc.discoverProperty(prop, meta._id);
1121
+ }
1122
+ }
1123
+ // For TPT, parent table must be inserted BEFORE child tables
1124
+ if (meta.inheritanceType === 'tpt' && meta.tptParent && set.has(meta.tptParent)) {
1125
+ calc.addDependency(meta.tptParent._id, meta._id, 1);
921
1126
  }
922
1127
  }
923
- return calc.sort();
1128
+ return calc.sort().map(id => this.metadata.getById(id));
924
1129
  }
925
1130
  resetTransaction(oldTx) {
926
1131
  if (oldTx) {