@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,28 +1,29 @@
1
1
  import { EntityIdentifier } from '../entity/EntityIdentifier.js';
2
+ import { PolymorphicRef } from '../entity/PolymorphicRef.js';
2
3
  import { helper } from '../entity/wrap.js';
3
4
  import { ChangeSetType } from './ChangeSet.js';
4
5
  import { isRaw } from '../utils/RawQueryFragment.js';
5
6
  import { Utils } from '../utils/Utils.js';
6
- import { OptimisticLockError } from '../errors.js';
7
+ import { OptimisticLockError, ValidationError } from '../errors.js';
7
8
  import { ReferenceKind } from '../enums.js';
8
9
  export class ChangeSetPersister {
10
+ em;
11
+ platform;
12
+ comparator;
13
+ usesReturningStatement;
9
14
  driver;
10
15
  metadata;
11
16
  hydrator;
12
17
  factory;
13
- validator;
14
18
  config;
15
- platform;
16
- comparator;
17
- usesReturningStatement;
18
- constructor(driver, metadata, hydrator, factory, validator, config) {
19
- this.driver = driver;
20
- this.metadata = metadata;
21
- this.hydrator = hydrator;
22
- this.factory = factory;
23
- this.validator = validator;
24
- this.config = config;
19
+ constructor(em) {
20
+ this.em = em;
21
+ this.driver = this.em.getDriver();
22
+ this.config = this.em.config;
23
+ this.metadata = this.em.getMetadata();
24
+ this.factory = this.em.getEntityFactory();
25
25
  this.platform = this.driver.getPlatform();
26
+ this.hydrator = this.config.getHydrator(this.metadata);
26
27
  this.comparator = this.config.getComparator(this.metadata);
27
28
  this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
28
29
  }
@@ -30,7 +31,7 @@ export class ChangeSetPersister {
30
31
  if (!withSchema) {
31
32
  return this.runForEachSchema(changeSets, 'executeInserts', options);
32
33
  }
33
- const meta = this.metadata.find(changeSets[0].name);
34
+ const meta = changeSets[0].meta;
34
35
  changeSets.forEach(changeSet => this.processProperties(changeSet));
35
36
  if (changeSets.length > 1 && this.config.get('useBatchInserts', this.platform.usesBatchInserts())) {
36
37
  return this.persistNewEntities(meta, changeSets, options);
@@ -43,9 +44,12 @@ export class ChangeSetPersister {
43
44
  if (!withSchema) {
44
45
  return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
45
46
  }
46
- const meta = this.metadata.find(changeSets[0].name);
47
+ const meta = changeSets[0].meta;
47
48
  changeSets.forEach(changeSet => this.processProperties(changeSet));
48
- if (batched && changeSets.length > 1 && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
49
+ // For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
50
+ const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
51
+ const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
52
+ if (batched && changeSets.length > 1 && !hasMixedTypes && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
49
53
  return this.persistManagedEntities(meta, changeSets, options);
50
54
  }
51
55
  for (const changeSet of changeSets) {
@@ -62,8 +66,8 @@ export class ChangeSetPersister {
62
66
  for (let i = 0; i < changeSets.length; i += size) {
63
67
  const chunk = changeSets.slice(i, i + size);
64
68
  const pks = chunk.map(cs => cs.getPrimaryKey());
65
- options = this.propagateSchemaFromMetadata(meta, options);
66
- await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
69
+ options = this.prepareOptions(meta, options);
70
+ await this.driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
67
71
  }
68
72
  }
69
73
  async runForEachSchema(changeSets, method, options, ...args) {
@@ -79,21 +83,42 @@ export class ChangeSetPersister {
79
83
  await this[method](group, ...args, options, true);
80
84
  }
81
85
  }
86
+ validateRequired(entity) {
87
+ const wrapped = helper(entity);
88
+ for (const prop of wrapped.__meta.props) {
89
+ if (!prop.nullable &&
90
+ !prop.autoincrement &&
91
+ !prop.default &&
92
+ !prop.defaultRaw &&
93
+ !prop.onCreate &&
94
+ !prop.generated &&
95
+ !prop.embedded &&
96
+ ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
97
+ prop.name !== wrapped.__meta.root.discriminatorColumn &&
98
+ prop.type !== 'ObjectId' &&
99
+ prop.persist !== false &&
100
+ entity[prop.name] == null) {
101
+ throw ValidationError.propertyRequired(entity, prop);
102
+ }
103
+ }
104
+ }
82
105
  processProperties(changeSet) {
83
- const meta = this.metadata.find(changeSet.name);
106
+ const meta = changeSet.meta;
84
107
  for (const prop of meta.relations) {
85
108
  this.processProperty(changeSet, prop);
86
109
  }
87
110
  if (changeSet.type === ChangeSetType.CREATE && this.config.get('validateRequired')) {
88
- this.validator.validateRequired(changeSet.entity);
111
+ this.validateRequired(changeSet.entity);
89
112
  }
90
113
  }
91
114
  async persistNewEntity(meta, changeSet, options) {
92
115
  const wrapped = helper(changeSet.entity);
93
- options = this.propagateSchemaFromMetadata(meta, options, {
116
+ options = this.prepareOptions(meta, options, {
94
117
  convertCustomTypes: false,
95
118
  });
96
- const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
119
+ this.resolveTPTIdentifiers(changeSet);
120
+ // Use changeSet's own meta for STI entities to get correct field mappings
121
+ const res = await this.driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
97
122
  if (!wrapped.hasPrimaryKey()) {
98
123
  this.mapPrimaryKey(meta, res.insertId, changeSet);
99
124
  }
@@ -116,19 +141,24 @@ export class ChangeSetPersister {
116
141
  }
117
142
  }
118
143
  }
119
- propagateSchemaFromMetadata(meta, options, additionalOptions) {
144
+ prepareOptions(meta, options, additionalOptions) {
145
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
120
146
  return {
121
147
  ...options,
122
148
  ...additionalOptions,
123
149
  schema: options?.schema ?? meta.schema,
150
+ loggerContext,
124
151
  };
125
152
  }
126
153
  async persistNewEntitiesBatch(meta, changeSets, options) {
127
- options = this.propagateSchemaFromMetadata(meta, options, {
154
+ options = this.prepareOptions(meta, options, {
128
155
  convertCustomTypes: false,
129
156
  processCollections: false,
130
157
  });
131
- const res = await this.driver.nativeInsertMany(meta.className, changeSets.map(cs => cs.payload), options);
158
+ for (const changeSet of changeSets) {
159
+ this.resolveTPTIdentifiers(changeSet);
160
+ }
161
+ const res = await this.driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
132
162
  for (let i = 0; i < changeSets.length; i++) {
133
163
  const changeSet = changeSets[i];
134
164
  const wrapped = helper(changeSet.entity);
@@ -146,7 +176,7 @@ export class ChangeSetPersister {
146
176
  }
147
177
  }
148
178
  async persistManagedEntity(changeSet, options) {
149
- const meta = this.metadata.find(changeSet.name);
179
+ const meta = changeSet.meta;
150
180
  const res = await this.updateEntity(meta, changeSet, options);
151
181
  this.checkOptimisticLock(meta, changeSet, res);
152
182
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
@@ -175,7 +205,7 @@ export class ChangeSetPersister {
175
205
  }
176
206
  async persistManagedEntitiesBatch(meta, changeSets, options) {
177
207
  await this.checkOptimisticLocks(meta, changeSets, options);
178
- options = this.propagateSchemaFromMetadata(meta, options, {
208
+ options = this.prepareOptions(meta, options, {
179
209
  convertCustomTypes: false,
180
210
  processCollections: false,
181
211
  });
@@ -187,7 +217,7 @@ export class ChangeSetPersister {
187
217
  cond.push(where);
188
218
  payload.push(changeSet.payload);
189
219
  }
190
- const res = await this.driver.nativeUpdateMany(meta.className, cond, payload, options);
220
+ const res = await this.driver.nativeUpdateMany(meta.class, cond, payload, options);
191
221
  const map = new Map();
192
222
  res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
193
223
  for (const changeSet of changeSets) {
@@ -210,7 +240,9 @@ export class ChangeSetPersister {
210
240
  // of using the raw value from db, we convert it back to the db value explicitly
211
241
  value = prop.customType ? prop.customType.convertToDatabaseValue(insertId, this.platform, { mode: 'serialization' }) : value;
212
242
  changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
213
- wrapped.__identifier?.setValue(value);
243
+ if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
244
+ wrapped.__identifier.setValue(value);
245
+ }
214
246
  }
215
247
  /**
216
248
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
@@ -233,17 +265,17 @@ export class ChangeSetPersister {
233
265
  }
234
266
  async updateEntity(meta, changeSet, options) {
235
267
  const cond = changeSet.getPrimaryKey(true);
236
- options = this.propagateSchemaFromMetadata(meta, options, {
268
+ options = this.prepareOptions(meta, options, {
237
269
  convertCustomTypes: false,
238
270
  });
239
271
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
240
- return this.driver.nativeUpdate(changeSet.name, cond, changeSet.payload, options);
272
+ return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
241
273
  }
242
274
  if (meta.versionProperty) {
243
- cond[meta.versionProperty] = this.platform.quoteVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
275
+ cond[meta.versionProperty] = this.platform.convertVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
244
276
  }
245
277
  this.checkConcurrencyKeys(meta, changeSet, cond);
246
- return this.driver.nativeUpdate(changeSet.name, cond, changeSet.payload, options);
278
+ return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
247
279
  }
248
280
  async checkOptimisticLocks(meta, changeSets, options) {
249
281
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
@@ -255,15 +287,15 @@ export class ChangeSetPersister {
255
287
  const cond = Utils.getPrimaryKeyCond(cs.originalEntity, meta.primaryKeys.concat(...meta.concurrencyCheckKeys));
256
288
  if (meta.versionProperty) {
257
289
  // @ts-ignore
258
- cond[meta.versionProperty] = this.platform.quoteVersionValue(cs.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
290
+ cond[meta.versionProperty] = this.platform.convertVersionValue(cs.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
259
291
  }
260
292
  return cond;
261
293
  });
262
294
  const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
263
- options = this.propagateSchemaFromMetadata(meta, options, {
295
+ options = this.prepareOptions(meta, options, {
264
296
  fields: primaryKeys,
265
297
  });
266
- const res = await this.driver.find(meta.root.className, { $or }, options);
298
+ const res = await this.driver.find(meta.root.class, { $or }, options);
267
299
  if (res.length !== changeSets.length) {
268
300
  const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
269
301
  const entity = changeSets.find(cs => {
@@ -284,11 +316,22 @@ export class ChangeSetPersister {
284
316
  async reloadVersionValues(meta, changeSets, options) {
285
317
  const reloadProps = meta.versionProperty && !this.usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
286
318
  if (changeSets[0].type === ChangeSetType.CREATE) {
287
- // do not reload things that already had a runtime value
288
- meta.props
289
- .filter(prop => prop.persist !== false && (prop.autoincrement || prop.generated || prop.defaultRaw))
290
- .filter(prop => (changeSets[0].entity[prop.name] == null && prop.defaultRaw !== 'null') || isRaw(changeSets[0].entity[prop.name]))
291
- .forEach(prop => reloadProps.push(prop));
319
+ for (const prop of meta.props) {
320
+ if (prop.persist === false) {
321
+ continue;
322
+ }
323
+ if (isRaw(changeSets[0].entity[prop.name])) {
324
+ reloadProps.push(prop);
325
+ continue;
326
+ }
327
+ // do not reload things that already had a runtime value
328
+ if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
329
+ continue;
330
+ }
331
+ if (prop.autoincrement || prop.generated || prop.defaultRaw) {
332
+ reloadProps.push(prop);
333
+ }
334
+ }
292
335
  }
293
336
  if (changeSets[0].type === ChangeSetType.UPDATE) {
294
337
  const returning = new Set();
@@ -319,25 +362,50 @@ export class ChangeSetPersister {
319
362
  }
320
363
  return val;
321
364
  });
322
- options = this.propagateSchemaFromMetadata(meta, options, {
365
+ options = this.prepareOptions(meta, options, {
323
366
  fields: Utils.unique(reloadProps.map(prop => prop.name)),
324
367
  });
325
- const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
368
+ const data = await this.driver.find(meta.class, { [pk]: { $in: pks } }, options);
326
369
  const map = new Map();
327
- data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
370
+ data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
328
371
  for (const changeSet of changeSets) {
329
372
  const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
330
373
  this.hydrator.hydrate(changeSet.entity, meta, data, this.factory, 'full', false, true);
331
374
  Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
332
375
  }
333
376
  }
377
+ /**
378
+ * For TPT child tables, resolve EntityIdentifier values in PK fields.
379
+ * The parent table insert assigns the actual PK value, which the child table references.
380
+ */
381
+ resolveTPTIdentifiers(changeSet) {
382
+ if (changeSet.meta.inheritanceType !== 'tpt' || !changeSet.meta.tptParent) {
383
+ return;
384
+ }
385
+ for (const pk of changeSet.meta.primaryKeys) {
386
+ const value = changeSet.payload[pk];
387
+ if (value instanceof EntityIdentifier) {
388
+ changeSet.payload[pk] = value.getValue();
389
+ }
390
+ }
391
+ }
334
392
  processProperty(changeSet, prop) {
335
- const meta = this.metadata.find(changeSet.name);
393
+ const meta = changeSet.meta;
336
394
  const value = changeSet.payload[prop.name]; // for inline embeddables
337
395
  if (value instanceof EntityIdentifier) {
338
396
  changeSet.payload[prop.name] = value.getValue();
339
397
  return;
340
398
  }
399
+ if (value instanceof PolymorphicRef) {
400
+ if (value.id instanceof EntityIdentifier) {
401
+ value.id = value.id.getValue();
402
+ }
403
+ return;
404
+ }
405
+ if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
406
+ changeSet.payload[prop.name] = value.map(item => item.getValue());
407
+ return;
408
+ }
341
409
  if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
342
410
  changeSet.payload[prop.name] = value.map(val => val instanceof EntityIdentifier ? val.getValue() : val);
343
411
  return;
@@ -361,7 +429,7 @@ export class ChangeSetPersister {
361
429
  if ((!this.usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
362
430
  return;
363
431
  }
364
- const mapped = this.comparator.mapResult(meta.className, row);
432
+ const mapped = this.comparator.mapResult(meta, row);
365
433
  if (entity) {
366
434
  this.hydrator.hydrate(entity, meta, mapped, this.factory, 'full', false, true);
367
435
  }
@@ -1,17 +1,18 @@
1
- import type { Dictionary, EntityProperty } from '../typings.js';
1
+ import type { EntityProperty } from '../typings.js';
2
2
  export declare const enum NodeState {
3
3
  NOT_VISITED = 0,
4
4
  IN_PROGRESS = 1,
5
5
  VISITED = 2
6
6
  }
7
+ type Hash = number;
7
8
  export interface Node {
8
- hash: string;
9
+ hash: Hash;
9
10
  state: NodeState;
10
- dependencies: Dictionary<Edge>;
11
+ dependencies: Map<Hash, Edge>;
11
12
  }
12
13
  export interface Edge {
13
- from: string;
14
- to: string;
14
+ from: Hash;
15
+ to: Hash;
15
16
  weight: number;
16
17
  }
17
18
  /**
@@ -32,23 +33,23 @@ export declare class CommitOrderCalculator {
32
33
  /**
33
34
  * Checks for node existence in graph.
34
35
  */
35
- hasNode(hash: string): boolean;
36
+ hasNode(hash: Hash): boolean;
36
37
  /**
37
38
  * Adds a new node to the graph, assigning its hash.
38
39
  */
39
- addNode(hash: string): void;
40
+ addNode(hash: Hash): void;
40
41
  /**
41
42
  * Adds a new dependency (edge) to the graph using their hashes.
42
43
  */
43
- addDependency(from: string, to: string, weight: number): void;
44
- discoverProperty(prop: EntityProperty, entityName: string): void;
44
+ addDependency(from: Hash, to: Hash, weight: number): void;
45
+ discoverProperty(prop: EntityProperty, entityName: Hash): void;
45
46
  /**
46
47
  * Return a valid order list of all current nodes.
47
48
  * The desired topological sorting is the reverse post order of these searches.
48
49
  *
49
50
  * @internal Highly performance-sensitive method.
50
51
  */
51
- sort(): string[];
52
+ sort(): Hash[];
52
53
  /**
53
54
  * Visit a given node definition for reordering.
54
55
  *
@@ -60,3 +61,4 @@ export declare class CommitOrderCalculator {
60
61
  */
61
62
  private visitOpenNode;
62
63
  }
64
+ export {};
@@ -17,26 +17,26 @@ export var NodeState;
17
17
  */
18
18
  export class CommitOrderCalculator {
19
19
  /** Matrix of nodes, keys are provided hashes and values are the node definition objects. */
20
- nodes = {};
20
+ nodes = new Map();
21
21
  /** Volatile variable holding calculated nodes during sorting process. */
22
22
  sortedNodeList = [];
23
23
  /**
24
24
  * Checks for node existence in graph.
25
25
  */
26
26
  hasNode(hash) {
27
- return hash in this.nodes;
27
+ return this.nodes.has(hash);
28
28
  }
29
29
  /**
30
30
  * Adds a new node to the graph, assigning its hash.
31
31
  */
32
32
  addNode(hash) {
33
- this.nodes[hash] = { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: {} };
33
+ this.nodes.set(hash, { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: new Map() });
34
34
  }
35
35
  /**
36
36
  * Adds a new dependency (edge) to the graph using their hashes.
37
37
  */
38
38
  addDependency(from, to, weight) {
39
- this.nodes[from].dependencies[to] = { from, to, weight };
39
+ this.nodes.get(from).dependencies.set(to, { from, to, weight });
40
40
  }
41
41
  discoverProperty(prop, entityName) {
42
42
  const toOneOwner = (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) || prop.kind === ReferenceKind.MANY_TO_ONE;
@@ -44,8 +44,8 @@ export class CommitOrderCalculator {
44
44
  if (!toOneOwner && !toManyOwner) {
45
45
  return;
46
46
  }
47
- const propertyType = prop.targetMeta?.root.className;
48
- if (!propertyType || !this.hasNode(propertyType)) {
47
+ const propertyType = prop.targetMeta?.root._id;
48
+ if (propertyType == null || !this.hasNode(propertyType)) {
49
49
  return;
50
50
  }
51
51
  this.addDependency(propertyType, entityName, prop.nullable || prop.persist === false ? 0 : 1);
@@ -57,14 +57,14 @@ export class CommitOrderCalculator {
57
57
  * @internal Highly performance-sensitive method.
58
58
  */
59
59
  sort() {
60
- for (const vertex of Object.values(this.nodes)) {
60
+ for (const vertex of this.nodes.values()) {
61
61
  if (vertex.state !== 0 /* NodeState.NOT_VISITED */) {
62
62
  continue;
63
63
  }
64
64
  this.visit(vertex);
65
65
  }
66
66
  const sortedList = this.sortedNodeList.reverse();
67
- this.nodes = {};
67
+ this.nodes = new Map();
68
68
  this.sortedNodeList = [];
69
69
  return sortedList;
70
70
  }
@@ -75,8 +75,8 @@ export class CommitOrderCalculator {
75
75
  */
76
76
  visit(node) {
77
77
  node.state = 1 /* NodeState.IN_PROGRESS */;
78
- for (const edge of Object.values(node.dependencies)) {
79
- const target = this.nodes[edge.to];
78
+ for (const edge of node.dependencies.values()) {
79
+ const target = this.nodes.get(edge.to);
80
80
  switch (target.state) {
81
81
  case 2 /* NodeState.VISITED */: break; // Do nothing, since node was already visited
82
82
  case 1 /* NodeState.IN_PROGRESS */:
@@ -94,11 +94,11 @@ export class CommitOrderCalculator {
94
94
  * Visits all target's dependencies if in cycle with given node
95
95
  */
96
96
  visitOpenNode(node, target, edge) {
97
- if (!target.dependencies[node.hash] || target.dependencies[node.hash].weight >= edge.weight) {
97
+ if (!target.dependencies.has(node.hash) || target.dependencies.get(node.hash).weight >= edge.weight) {
98
98
  return;
99
99
  }
100
- for (const edge of Object.values(target.dependencies)) {
101
- const targetNode = this.nodes[edge.to];
100
+ for (const edge of target.dependencies.values()) {
101
+ const targetNode = this.nodes.get(edge.to);
102
102
  if (targetNode.state === 0 /* NodeState.NOT_VISITED */) {
103
103
  this.visit(targetNode);
104
104
  }
@@ -3,7 +3,14 @@ export declare class IdentityMap {
3
3
  private readonly defaultSchema?;
4
4
  constructor(defaultSchema?: string | undefined);
5
5
  private readonly registry;
6
+ /** Tracks alternate key hashes for each entity so we can clean them up on delete */
7
+ private readonly alternateKeys;
6
8
  store<T>(item: T): void;
9
+ /**
10
+ * Stores an entity under an alternate key (non-PK property).
11
+ * This allows looking up entities by unique properties that are not the primary key.
12
+ */
13
+ storeByKey<T>(item: T, key: string, value: string, schema?: string): void;
7
14
  delete<T>(item: T): void;
8
15
  getByHash<T>(meta: EntityMetadata<T>, hash: string): T | undefined;
9
16
  getStore<T>(meta: EntityMetadata<T>): Map<string, T>;
@@ -16,4 +23,9 @@ export declare class IdentityMap {
16
23
  */
17
24
  get<T>(hash: string): T | undefined;
18
25
  private getPkHash;
26
+ /**
27
+ * Creates a hash for an alternate key lookup.
28
+ * Format: `[key]value` or `schema:[key]value`
29
+ */
30
+ getKeyHash(key: string, value: string, schema?: string): string;
19
31
  }
@@ -4,11 +4,38 @@ export class IdentityMap {
4
4
  this.defaultSchema = defaultSchema;
5
5
  }
6
6
  registry = new Map();
7
+ /** Tracks alternate key hashes for each entity so we can clean them up on delete */
8
+ alternateKeys = new WeakMap();
7
9
  store(item) {
8
10
  this.getStore(item.__meta.root).set(this.getPkHash(item), item);
9
11
  }
12
+ /**
13
+ * Stores an entity under an alternate key (non-PK property).
14
+ * This allows looking up entities by unique properties that are not the primary key.
15
+ */
16
+ storeByKey(item, key, value, schema) {
17
+ const hash = this.getKeyHash(key, value, schema);
18
+ this.getStore(item.__meta.root).set(hash, item);
19
+ // Track this alternate key so we can clean it up when the entity is deleted
20
+ let keys = this.alternateKeys.get(item);
21
+ if (!keys) {
22
+ keys = new Set();
23
+ this.alternateKeys.set(item, keys);
24
+ }
25
+ keys.add(hash);
26
+ }
10
27
  delete(item) {
11
- this.getStore(item.__meta.root).delete(this.getPkHash(item));
28
+ const meta = item.__meta.root;
29
+ const store = this.getStore(meta);
30
+ store.delete(this.getPkHash(item));
31
+ // Also delete any alternate key entries for this entity
32
+ const altKeys = this.alternateKeys.get(item);
33
+ if (altKeys) {
34
+ for (const hash of altKeys) {
35
+ store.delete(hash);
36
+ }
37
+ this.alternateKeys.delete(item);
38
+ }
12
39
  }
13
40
  getByHash(meta, hash) {
14
41
  const store = this.getStore(meta);
@@ -69,4 +96,15 @@ export class IdentityMap {
69
96
  }
70
97
  return hash;
71
98
  }
99
+ /**
100
+ * Creates a hash for an alternate key lookup.
101
+ * Format: `[key]value` or `schema:[key]value`
102
+ */
103
+ getKeyHash(key, value, schema) {
104
+ const hash = `[${key}]${value}`;
105
+ if (schema) {
106
+ return schema + ':' + hash;
107
+ }
108
+ return hash;
109
+ }
72
110
  }
@@ -1,4 +1,4 @@
1
- import type { AnyEntity, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary } from '../typings.js';
1
+ import type { AnyEntity, EntityData, EntityMetadata, EntityName, EntityProperty, FilterQuery, Primary } from '../typings.js';
2
2
  import { Collection } from '../entity/Collection.js';
3
3
  import { Reference } from '../entity/Reference.js';
4
4
  import { ChangeSet, ChangeSetType } from './ChangeSet.js';
@@ -28,6 +28,12 @@ export declare class UnitOfWork {
28
28
  private working;
29
29
  constructor(em: EntityManager);
30
30
  merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
31
+ /**
32
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
33
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
34
+ * @internal
35
+ */
36
+ normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
31
37
  /**
32
38
  * @internal
33
39
  */
@@ -39,8 +45,21 @@ export declare class UnitOfWork {
39
45
  /**
40
46
  * 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`.
41
47
  */
42
- getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string): T | undefined;
43
- tryGetById<T extends object>(entityName: string, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
48
+ getById<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
49
+ /**
50
+ * Returns entity from the identity map by an alternate key (non-PK property).
51
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
52
+ * If false (default), the value is assumed to be in JS format already.
53
+ */
54
+ getByKey<T extends object>(entityName: EntityName<T>, key: string, value: unknown, schema?: string, convertCustomTypes?: boolean): T | undefined;
55
+ /**
56
+ * Stores an entity in the identity map under an alternate key (non-PK property).
57
+ * Also sets the property value on the entity.
58
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
59
+ * If false (default), the value is assumed to be in JS format already.
60
+ */
61
+ storeByKey<T extends object>(entity: T, key: string, value: unknown, schema?: string, convertCustomTypes?: boolean): void;
62
+ tryGetById<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
44
63
  /**
45
64
  * Returns map of all managed entities.
46
65
  */
@@ -77,6 +96,11 @@ export declare class UnitOfWork {
77
96
  getOrphanRemoveStack(): Set<AnyEntity>;
78
97
  getChangeSetPersister(): ChangeSetPersister;
79
98
  private findNewEntities;
99
+ /**
100
+ * For TPT inheritance, creates separate changesets for each table in the hierarchy.
101
+ * Uses the same entity instance for all changesets - only the metadata and payload differ.
102
+ */
103
+ private createTPTChangeSets;
80
104
  /**
81
105
  * Returns `true` when the change set should be skipped as it will be empty after the extra update.
82
106
  */
@@ -103,6 +127,7 @@ export declare class UnitOfWork {
103
127
  private commitDeleteChangeSets;
104
128
  private commitExtraUpdates;
105
129
  private commitCollectionUpdates;
130
+ private filterCollectionUpdates;
106
131
  /**
107
132
  * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
108
133
  */