@mikro-orm/core 7.0.0-dev.12 → 7.0.0-dev.120

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/EntityManager.d.ts +85 -56
  2. package/EntityManager.js +332 -293
  3. package/MikroORM.d.ts +41 -32
  4. package/MikroORM.js +100 -140
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +8 -7
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +0 -1
  11. package/cache/index.js +0 -1
  12. package/connections/Connection.d.ts +16 -7
  13. package/connections/Connection.js +23 -14
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +35 -19
  16. package/drivers/IDatabaseDriver.d.ts +38 -17
  17. package/entity/BaseEntity.d.ts +0 -1
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +95 -30
  20. package/entity/Collection.js +439 -99
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +7 -0
  24. package/entity/EntityFactory.js +72 -53
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +30 -15
  27. package/entity/EntityLoader.d.ts +7 -6
  28. package/entity/EntityLoader.js +84 -72
  29. package/entity/EntityRepository.d.ts +1 -1
  30. package/entity/EntityRepository.js +2 -2
  31. package/entity/Reference.d.ts +6 -5
  32. package/entity/Reference.js +34 -9
  33. package/entity/WrappedEntity.d.ts +2 -7
  34. package/entity/WrappedEntity.js +3 -8
  35. package/entity/defineEntity.d.ts +568 -0
  36. package/entity/defineEntity.js +529 -0
  37. package/entity/index.d.ts +3 -2
  38. package/entity/index.js +3 -2
  39. package/entity/utils.d.ts +7 -0
  40. package/entity/utils.js +16 -4
  41. package/entity/validators.d.ts +11 -0
  42. package/entity/validators.js +65 -0
  43. package/enums.d.ts +21 -6
  44. package/enums.js +14 -1
  45. package/errors.d.ts +17 -9
  46. package/errors.js +41 -21
  47. package/events/EventManager.d.ts +2 -1
  48. package/events/EventManager.js +19 -11
  49. package/hydration/Hydrator.js +1 -2
  50. package/hydration/ObjectHydrator.d.ts +4 -4
  51. package/hydration/ObjectHydrator.js +50 -33
  52. package/index.d.ts +2 -2
  53. package/index.js +1 -2
  54. package/logging/DefaultLogger.d.ts +1 -1
  55. package/logging/DefaultLogger.js +1 -0
  56. package/logging/SimpleLogger.d.ts +1 -1
  57. package/logging/colors.d.ts +1 -1
  58. package/logging/colors.js +7 -6
  59. package/logging/index.d.ts +1 -0
  60. package/logging/index.js +1 -0
  61. package/logging/inspect.d.ts +2 -0
  62. package/logging/inspect.js +11 -0
  63. package/metadata/EntitySchema.d.ts +13 -17
  64. package/metadata/EntitySchema.js +67 -51
  65. package/metadata/MetadataDiscovery.d.ts +6 -10
  66. package/metadata/MetadataDiscovery.js +289 -298
  67. package/metadata/MetadataProvider.d.ts +11 -2
  68. package/metadata/MetadataProvider.js +46 -2
  69. package/metadata/MetadataStorage.d.ts +13 -11
  70. package/metadata/MetadataStorage.js +70 -37
  71. package/metadata/MetadataValidator.d.ts +2 -9
  72. package/metadata/MetadataValidator.js +22 -38
  73. package/metadata/discover-entities.d.ts +5 -0
  74. package/metadata/discover-entities.js +40 -0
  75. package/metadata/index.d.ts +1 -1
  76. package/metadata/index.js +1 -1
  77. package/metadata/types.d.ts +480 -0
  78. package/metadata/types.js +1 -0
  79. package/naming-strategy/AbstractNamingStrategy.d.ts +8 -4
  80. package/naming-strategy/AbstractNamingStrategy.js +8 -2
  81. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  82. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  83. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  84. package/naming-strategy/MongoNamingStrategy.js +6 -6
  85. package/naming-strategy/NamingStrategy.d.ts +14 -4
  86. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  87. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  88. package/not-supported.d.ts +2 -0
  89. package/not-supported.js +4 -0
  90. package/package.json +19 -11
  91. package/platforms/ExceptionConverter.js +1 -1
  92. package/platforms/Platform.d.ts +6 -13
  93. package/platforms/Platform.js +17 -43
  94. package/serialization/EntitySerializer.d.ts +5 -0
  95. package/serialization/EntitySerializer.js +47 -27
  96. package/serialization/EntityTransformer.js +28 -18
  97. package/serialization/SerializationContext.d.ts +6 -6
  98. package/serialization/SerializationContext.js +16 -13
  99. package/types/ArrayType.d.ts +1 -1
  100. package/types/ArrayType.js +2 -3
  101. package/types/BigIntType.d.ts +8 -6
  102. package/types/BigIntType.js +1 -1
  103. package/types/BlobType.d.ts +0 -1
  104. package/types/BlobType.js +0 -3
  105. package/types/BooleanType.d.ts +2 -1
  106. package/types/BooleanType.js +3 -0
  107. package/types/DecimalType.d.ts +6 -4
  108. package/types/DecimalType.js +3 -3
  109. package/types/DoubleType.js +2 -2
  110. package/types/EnumArrayType.js +1 -2
  111. package/types/JsonType.d.ts +1 -1
  112. package/types/JsonType.js +7 -2
  113. package/types/TinyIntType.js +1 -1
  114. package/types/Type.d.ts +2 -4
  115. package/types/Type.js +3 -3
  116. package/types/Uint8ArrayType.d.ts +0 -1
  117. package/types/Uint8ArrayType.js +1 -4
  118. package/types/index.d.ts +1 -1
  119. package/typings.d.ts +124 -86
  120. package/typings.js +50 -42
  121. package/unit-of-work/ChangeSet.d.ts +2 -6
  122. package/unit-of-work/ChangeSet.js +4 -5
  123. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  124. package/unit-of-work/ChangeSetComputer.js +14 -12
  125. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  126. package/unit-of-work/ChangeSetPersister.js +65 -33
  127. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  128. package/unit-of-work/CommitOrderCalculator.js +13 -13
  129. package/unit-of-work/UnitOfWork.d.ts +10 -3
  130. package/unit-of-work/UnitOfWork.js +139 -96
  131. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  132. package/utils/AbstractSchemaGenerator.js +18 -16
  133. package/utils/AsyncContext.d.ts +6 -0
  134. package/utils/AsyncContext.js +42 -0
  135. package/utils/Configuration.d.ts +753 -207
  136. package/utils/Configuration.js +145 -190
  137. package/utils/ConfigurationLoader.d.ts +1 -54
  138. package/utils/ConfigurationLoader.js +1 -352
  139. package/utils/Cursor.d.ts +0 -3
  140. package/utils/Cursor.js +9 -6
  141. package/utils/DataloaderUtils.d.ts +15 -5
  142. package/utils/DataloaderUtils.js +65 -17
  143. package/utils/EntityComparator.d.ts +13 -9
  144. package/utils/EntityComparator.js +85 -43
  145. package/utils/QueryHelper.d.ts +14 -6
  146. package/utils/QueryHelper.js +87 -25
  147. package/utils/RawQueryFragment.d.ts +48 -25
  148. package/utils/RawQueryFragment.js +66 -70
  149. package/utils/RequestContext.js +2 -2
  150. package/utils/TransactionContext.js +2 -2
  151. package/utils/TransactionManager.d.ts +65 -0
  152. package/utils/TransactionManager.js +223 -0
  153. package/utils/Utils.d.ts +12 -119
  154. package/utils/Utils.js +97 -373
  155. package/utils/clone.js +8 -23
  156. package/utils/env-vars.d.ts +7 -0
  157. package/utils/env-vars.js +97 -0
  158. package/utils/fs-utils.d.ts +32 -0
  159. package/utils/fs-utils.js +178 -0
  160. package/utils/index.d.ts +2 -1
  161. package/utils/index.js +2 -1
  162. package/utils/upsert-utils.d.ts +9 -4
  163. package/utils/upsert-utils.js +55 -4
  164. package/decorators/Check.d.ts +0 -3
  165. package/decorators/Check.js +0 -13
  166. package/decorators/CreateRequestContext.d.ts +0 -3
  167. package/decorators/CreateRequestContext.js +0 -32
  168. package/decorators/Embeddable.d.ts +0 -8
  169. package/decorators/Embeddable.js +0 -11
  170. package/decorators/Embedded.d.ts +0 -18
  171. package/decorators/Embedded.js +0 -18
  172. package/decorators/Entity.d.ts +0 -18
  173. package/decorators/Entity.js +0 -12
  174. package/decorators/Enum.d.ts +0 -9
  175. package/decorators/Enum.js +0 -16
  176. package/decorators/Filter.d.ts +0 -2
  177. package/decorators/Filter.js +0 -8
  178. package/decorators/Formula.d.ts +0 -4
  179. package/decorators/Formula.js +0 -15
  180. package/decorators/Indexed.d.ts +0 -19
  181. package/decorators/Indexed.js +0 -20
  182. package/decorators/ManyToMany.d.ts +0 -40
  183. package/decorators/ManyToMany.js +0 -14
  184. package/decorators/ManyToOne.d.ts +0 -30
  185. package/decorators/ManyToOne.js +0 -14
  186. package/decorators/OneToMany.d.ts +0 -28
  187. package/decorators/OneToMany.js +0 -17
  188. package/decorators/OneToOne.d.ts +0 -24
  189. package/decorators/OneToOne.js +0 -7
  190. package/decorators/PrimaryKey.d.ts +0 -8
  191. package/decorators/PrimaryKey.js +0 -20
  192. package/decorators/Property.d.ts +0 -250
  193. package/decorators/Property.js +0 -32
  194. package/decorators/Transactional.d.ts +0 -13
  195. package/decorators/Transactional.js +0 -28
  196. package/decorators/hooks.d.ts +0 -16
  197. package/decorators/hooks.js +0 -47
  198. package/decorators/index.d.ts +0 -17
  199. package/decorators/index.js +0 -17
  200. package/entity/ArrayCollection.d.ts +0 -116
  201. package/entity/ArrayCollection.js +0 -402
  202. package/entity/EntityValidator.d.ts +0 -19
  203. package/entity/EntityValidator.js +0 -150
  204. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  205. package/metadata/ReflectMetadataProvider.js +0 -44
  206. package/utils/resolveContextProvider.d.ts +0 -10
  207. 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.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.config, this.em);
47
47
  }
48
48
  merge(entity, visited) {
49
49
  const wrapped = helper(entity);
@@ -61,10 +61,43 @@ 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
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
81
+ }
82
+ else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
83
+ for (const p of prop.targetMeta.props) {
84
+ /* v8 ignore next */
85
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
86
+ data[prefix + p.name] = data[prop.name][p.name];
87
+ }
88
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
89
+ }
90
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
91
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
92
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
93
+ }
94
+ if (forceUndefined) {
95
+ if (data[key] === null) {
96
+ data[key] = undefined;
97
+ }
98
+ }
99
+ }
100
+ }
68
101
  /**
69
102
  * @internal
70
103
  */
@@ -82,31 +115,14 @@ export class UnitOfWork {
82
115
  wrapped.__em ??= this.em;
83
116
  wrapped.__managed = true;
84
117
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
118
+ this.normalizeEntityData(wrapped.__meta, data);
85
119
  for (const key of Utils.keys(data)) {
86
120
  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
- }
121
+ if (prop) {
122
+ wrapped.__loadedProperties.add(key);
106
123
  }
107
124
  }
108
125
  wrapped.__originalEntityData = data;
109
- wrapped.__touched = false;
110
126
  }
111
127
  return entity;
112
128
  }
@@ -125,7 +141,7 @@ export class UnitOfWork {
125
141
  /**
126
142
  * 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
143
  */
128
- getById(entityName, id, schema) {
144
+ getById(entityName, id, schema, convertCustomTypes) {
129
145
  if (id == null || (Array.isArray(id) && id.length === 0)) {
130
146
  return undefined;
131
147
  }
@@ -135,7 +151,16 @@ export class UnitOfWork {
135
151
  hash = '' + id;
136
152
  }
137
153
  else {
138
- const keys = Array.isArray(id) ? Utils.flatten(id) : [id];
154
+ let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
155
+ keys = meta.getPrimaryProps(true).map((p, i) => {
156
+ if (!convertCustomTypes && p.customType) {
157
+ return p.customType.convertToDatabaseValue(keys[i], this.platform, {
158
+ key: p.name,
159
+ mode: 'hydration',
160
+ });
161
+ }
162
+ return keys[i];
163
+ });
139
164
  hash = Utils.getPrimaryKeyHash(keys);
140
165
  }
141
166
  schema ??= meta.schema ?? this.em.config.getSchema();
@@ -182,13 +207,11 @@ export class UnitOfWork {
182
207
  if (insideFlush.getStore()) {
183
208
  return false;
184
209
  }
185
- if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
210
+ if (this.queuedActions.has(meta.class) || this.queuedActions.has(meta.root.class)) {
186
211
  return true;
187
212
  }
188
- for (const entity of this.identityMap.getStore(meta).values()) {
189
- if (helper(entity).__initialized && helper(entity).isTouched()) {
190
- return true;
191
- }
213
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
214
+ return true;
192
215
  }
193
216
  return false;
194
217
  }
@@ -209,7 +232,6 @@ export class UnitOfWork {
209
232
  this.changeSets.set(entity, cs);
210
233
  this.persistStack.delete(entity);
211
234
  wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
212
- wrapped.__touched = false;
213
235
  }
214
236
  recomputeSingleChangeSet(entity) {
215
237
  const changeSet = this.changeSets.get(entity);
@@ -220,7 +242,6 @@ export class UnitOfWork {
220
242
  if (cs && !this.checkUniqueProps(cs)) {
221
243
  Object.assign(changeSet.payload, cs.payload);
222
244
  helper(entity).__originalEntityData = this.comparator.prepareEntity(entity);
223
- helper(entity).__touched = false;
224
245
  }
225
246
  }
226
247
  persist(entity, visited, options = {}) {
@@ -230,7 +251,7 @@ export class UnitOfWork {
230
251
  }
231
252
  const wrapped = helper(entity);
232
253
  this.persistStack.add(entity);
233
- this.queuedActions.add(wrapped.__meta.className);
254
+ this.queuedActions.add(wrapped.__meta.class);
234
255
  this.removeStack.delete(entity);
235
256
  if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
236
257
  this.identityMap.store(entity);
@@ -243,7 +264,7 @@ export class UnitOfWork {
243
264
  // allow removing not managed entities if they are not part of the persist stack
244
265
  if (helper(entity).__managed || !this.persistStack.has(entity)) {
245
266
  this.removeStack.add(entity);
246
- this.queuedActions.add(helper(entity).__meta.className);
267
+ this.queuedActions.add(helper(entity).__meta.class);
247
268
  }
248
269
  else {
249
270
  this.persistStack.delete(entity);
@@ -303,17 +324,21 @@ export class UnitOfWork {
303
324
  cs.entity.__helper.__processing = true;
304
325
  }
305
326
  await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
327
+ this.filterCollectionUpdates();
306
328
  // nothing to do, do not start transaction
307
329
  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 }));
330
+ await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this });
331
+ return;
309
332
  }
310
333
  const groups = this.getChangeSetGroups();
311
334
  const platform = this.em.getPlatform();
312
335
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
336
  if (runInTransaction) {
337
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
338
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
339
  ctx: oldTx,
316
340
  eventBroadcaster: new TransactionEventBroadcaster(this.em),
341
+ loggerContext,
317
342
  });
318
343
  }
319
344
  else {
@@ -330,10 +355,10 @@ export class UnitOfWork {
330
355
  }
331
356
  }
332
357
  async lock(entity, options) {
333
- if (!this.getById(entity.constructor.name, helper(entity).__primaryKeys, helper(entity).__schema)) {
358
+ if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
334
359
  throw ValidationError.entityNotManaged(entity);
335
360
  }
336
- const meta = this.metadata.find(entity.constructor.name);
361
+ const meta = this.metadata.find(entity.constructor);
337
362
  if (options.lockMode === LockMode.OPTIMISTIC) {
338
363
  await this.lockOptimistic(entity, meta, options.lockVersion);
339
364
  }
@@ -357,7 +382,7 @@ export class UnitOfWork {
357
382
  if (Utils.isCollection(rel)) {
358
383
  rel.removeWithoutPropagation(entity);
359
384
  }
360
- else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.type, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
385
+ else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
361
386
  if (prop.formula) {
362
387
  delete referrer[prop.name];
363
388
  }
@@ -369,7 +394,6 @@ export class UnitOfWork {
369
394
  }
370
395
  delete wrapped.__identifier;
371
396
  delete wrapped.__originalEntityData;
372
- wrapped.__touched = false;
373
397
  wrapped.__managed = false;
374
398
  }
375
399
  computeChangeSets() {
@@ -379,14 +403,14 @@ export class UnitOfWork {
379
403
  this.cascade(entity, Cascade.REMOVE, visited);
380
404
  }
381
405
  visited.clear();
382
- for (const entity of this.persistStack) {
383
- this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
384
- }
385
406
  for (const entity of this.identityMap) {
386
407
  if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
387
408
  this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
388
409
  }
389
410
  }
411
+ for (const entity of this.persistStack) {
412
+ this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
413
+ }
390
414
  visited.clear();
391
415
  for (const entity of this.persistStack) {
392
416
  this.findNewEntities(entity, visited);
@@ -400,24 +424,24 @@ export class UnitOfWork {
400
424
  const inserts = {};
401
425
  for (const cs of this.changeSets.values()) {
402
426
  if (cs.type === ChangeSetType.CREATE) {
403
- inserts[cs.meta.className] ??= [];
404
- inserts[cs.meta.className].push(cs);
427
+ inserts[cs.meta.uniqueName] ??= [];
428
+ inserts[cs.meta.uniqueName].push(cs);
405
429
  }
406
430
  }
407
431
  for (const cs of this.changeSets.values()) {
408
432
  if (cs.type === ChangeSetType.UPDATE) {
409
- this.findEarlyUpdates(cs, inserts[cs.meta.className]);
433
+ this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
410
434
  }
411
435
  }
412
436
  for (const entity of this.removeStack) {
413
437
  const wrapped = helper(entity);
414
- /* v8 ignore next 3 */
438
+ /* v8 ignore next */
415
439
  if (wrapped.__processing) {
416
440
  continue;
417
441
  }
418
442
  const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
419
443
  let type = ChangeSetType.DELETE;
420
- for (const cs of inserts[wrapped.__meta.className] ?? []) {
444
+ for (const cs of inserts[wrapped.__meta.uniqueName] ?? []) {
421
445
  if (deletePkHash.some(hash => hash === cs.getSerializedPrimaryKey() || this.expandUniqueProps(cs.entity).find(child => hash === child))) {
422
446
  type = ChangeSetType.DELETE_EARLY;
423
447
  }
@@ -436,7 +460,7 @@ export class UnitOfWork {
436
460
  }
437
461
  for (const cs of this.changeSets.values()) {
438
462
  for (const prop of props) {
439
- if (prop.name in cs.payload && cs.rootName === changeSet.rootName && cs.type === changeSet.type) {
463
+ if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
440
464
  conflicts = true;
441
465
  if (changeSet.payload[prop.name] == null) {
442
466
  type = ChangeSetType.UPDATE_EARLY;
@@ -455,9 +479,10 @@ export class UnitOfWork {
455
479
  }
456
480
  scheduleOrphanRemoval(entity, visited) {
457
481
  if (entity) {
458
- helper(entity).__em = this.em;
482
+ const wrapped = helper(entity);
483
+ wrapped.__em = this.em;
459
484
  this.orphanRemoveStack.add(entity);
460
- this.queuedActions.add(entity.__meta.className);
485
+ this.queuedActions.add(wrapped.__meta.class);
461
486
  this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
462
487
  }
463
488
  }
@@ -606,7 +631,7 @@ export class UnitOfWork {
606
631
  const copy = this.comparator.prepareEntity(changeSet.entity);
607
632
  await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
608
633
  const current = this.comparator.prepareEntity(changeSet.entity);
609
- const diff = this.comparator.diffEntities(changeSet.name, copy, current);
634
+ const diff = this.comparator.diffEntities(changeSet.meta.class, copy, current);
610
635
  Object.assign(changeSet.payload, diff);
611
636
  const wrapped = helper(changeSet.entity);
612
637
  if (wrapped.__identifier) {
@@ -701,7 +726,7 @@ export class UnitOfWork {
701
726
  if (!meta.versionProperty) {
702
727
  throw OptimisticLockError.notVersioned(meta);
703
728
  }
704
- if (!Utils.isDefined(version)) {
729
+ if (typeof version === 'undefined') {
705
730
  return;
706
731
  }
707
732
  const wrapped = helper(entity);
@@ -715,26 +740,26 @@ export class UnitOfWork {
715
740
  }
716
741
  fixMissingReference(entity, prop) {
717
742
  const reference = entity[prop.name];
718
- const kind = Reference.unwrapReference(reference);
719
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && kind && !prop.mapToPk) {
720
- if (!Utils.isEntity(kind)) {
721
- entity[prop.name] = this.em.getReference(prop.type, kind, { wrapped: !!prop.ref });
743
+ const target = Reference.unwrapReference(reference);
744
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
745
+ if (!Utils.isEntity(target)) {
746
+ entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, { wrapped: !!prop.ref });
722
747
  }
723
- else if (!helper(kind).__initialized && !helper(kind).__em) {
724
- const pk = helper(kind).getPrimaryKey();
725
- entity[prop.name] = this.em.getReference(prop.type, pk, { wrapped: !!prop.ref });
748
+ else if (!helper(target).__initialized && !helper(target).__em) {
749
+ const pk = helper(target).getPrimaryKey();
750
+ entity[prop.name] = this.em.getReference(prop.targetMeta.class, pk, { wrapped: !!prop.ref });
726
751
  }
727
752
  }
728
- // perf: set the `Collection._property` to skip the getter, as it can be slow when there is a lot of relations
729
- if (Utils.isCollection(kind)) {
730
- kind.property = prop;
753
+ // perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
754
+ if (Utils.isCollection(target)) {
755
+ target.property = prop;
731
756
  }
732
757
  const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
733
- if (isCollection && Array.isArray(kind)) {
758
+ if (isCollection && Array.isArray(target)) {
734
759
  const collection = new Collection(entity);
735
760
  collection.property = prop;
736
761
  entity[prop.name] = collection;
737
- collection.set(kind);
762
+ collection.set(target);
738
763
  }
739
764
  }
740
765
  async persistToDatabase(groups, ctx) {
@@ -744,30 +769,30 @@ export class UnitOfWork {
744
769
  const commitOrder = this.getCommitOrder();
745
770
  const commitOrderReversed = [...commitOrder].reverse();
746
771
  // early delete - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
747
- for (const name of commitOrderReversed) {
748
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(name) ?? [], ctx);
772
+ for (const meta of commitOrderReversed) {
773
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(meta) ?? [], ctx);
749
774
  }
750
775
  // early update - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
751
- for (const name of commitOrder) {
752
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(name) ?? [], ctx);
776
+ for (const meta of commitOrder) {
777
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(meta) ?? [], ctx);
753
778
  }
754
779
  // extra updates
755
780
  await this.commitExtraUpdates(ChangeSetType.UPDATE_EARLY, ctx);
756
781
  // create
757
- for (const name of commitOrder) {
758
- await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(name) ?? [], ctx);
782
+ for (const meta of commitOrder) {
783
+ await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(meta) ?? [], ctx);
759
784
  }
760
785
  // update
761
- for (const name of commitOrder) {
762
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(name) ?? [], ctx);
786
+ for (const meta of commitOrder) {
787
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(meta) ?? [], ctx);
763
788
  }
764
789
  // extra updates
765
790
  await this.commitExtraUpdates(ChangeSetType.UPDATE, ctx);
766
791
  // collection updates
767
792
  await this.commitCollectionUpdates(ctx);
768
793
  // delete - entity deletions need to be in reverse commit order
769
- for (const name of commitOrderReversed) {
770
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(name) ?? [], ctx);
794
+ for (const meta of commitOrderReversed) {
795
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(meta) ?? [], ctx);
771
796
  }
772
797
  // take snapshots of all persisted collections
773
798
  const visited = new Set();
@@ -841,9 +866,16 @@ export class UnitOfWork {
841
866
  }
842
867
  await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
843
868
  for (const changeSet of changeSets) {
844
- helper(changeSet.entity).__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
845
- helper(changeSet.entity).__touched = false;
846
- helper(changeSet.entity).__initialized = true;
869
+ const wrapped = helper(changeSet.entity);
870
+ wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
871
+ if (!wrapped.__initialized) {
872
+ for (const prop of changeSet.meta.relations) {
873
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
874
+ changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
875
+ }
876
+ }
877
+ wrapped.__initialized = true;
878
+ }
847
879
  await this.runHooks(EventType.afterUpdate, changeSet);
848
880
  }
849
881
  }
@@ -886,23 +918,34 @@ export class UnitOfWork {
886
918
  }
887
919
  }
888
920
  async commitCollectionUpdates(ctx) {
889
- const collectionUpdates = [];
921
+ this.filterCollectionUpdates();
922
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
923
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
924
+ ctx,
925
+ schema: this.em.schema,
926
+ loggerContext,
927
+ });
890
928
  for (const coll of this.collectionUpdates) {
929
+ coll.takeSnapshot();
930
+ }
931
+ }
932
+ filterCollectionUpdates() {
933
+ for (const coll of this.collectionUpdates) {
934
+ let skip = true;
891
935
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
892
936
  if (this.platform.usesPivotTable()) {
893
- collectionUpdates.push(coll);
937
+ skip = false;
894
938
  }
895
939
  }
896
940
  else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
897
- collectionUpdates.push(coll);
941
+ skip = false;
898
942
  }
899
943
  else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
900
- collectionUpdates.push(coll);
944
+ skip = false;
945
+ }
946
+ if (skip) {
947
+ this.collectionUpdates.delete(coll);
901
948
  }
902
- }
903
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
904
- for (const coll of this.collectionUpdates) {
905
- coll.takeSnapshot();
906
949
  }
907
950
  }
908
951
  /**
@@ -918,10 +961,10 @@ export class UnitOfWork {
918
961
  };
919
962
  for (const cs of this.changeSets.values()) {
920
963
  const group = groups[cs.type];
921
- const classGroup = group.get(cs.rootName) ?? [];
964
+ const classGroup = group.get(cs.rootMeta) ?? [];
922
965
  classGroup.push(cs);
923
- if (!group.has(cs.rootName)) {
924
- group.set(cs.rootName, classGroup);
966
+ if (!group.has(cs.rootMeta)) {
967
+ group.set(cs.rootMeta, classGroup);
925
968
  }
926
969
  }
927
970
  return groups;
@@ -929,14 +972,14 @@ export class UnitOfWork {
929
972
  getCommitOrder() {
930
973
  const calc = new CommitOrderCalculator();
931
974
  const set = new Set();
932
- this.changeSets.forEach(cs => set.add(cs.rootName));
933
- set.forEach(entityName => calc.addNode(entityName));
934
- for (const entityName of set) {
935
- for (const prop of this.metadata.find(entityName).props) {
936
- calc.discoverProperty(prop, entityName);
975
+ this.changeSets.forEach(cs => set.add(cs.rootMeta));
976
+ set.forEach(meta => calc.addNode(meta._id));
977
+ for (const meta of set) {
978
+ for (const prop of meta.relations) {
979
+ calc.discoverProperty(prop, meta._id);
937
980
  }
938
981
  }
939
- return calc.sort();
982
+ return calc.sort().map(id => this.metadata.getById(id));
940
983
  }
941
984
  resetTransaction(oldTx) {
942
985
  if (oldTx) {
@@ -10,18 +10,18 @@ export declare abstract class AbstractSchemaGenerator<D extends IDatabaseDriver>
10
10
  protected readonly platform: ReturnType<D['getPlatform']>;
11
11
  protected readonly connection: ReturnType<D['getConnection']>;
12
12
  constructor(em: D | D[typeof EntityManagerType]);
13
- createSchema(options?: CreateSchemaOptions): Promise<void>;
13
+ create(options?: CreateSchemaOptions): Promise<void>;
14
14
  /**
15
15
  * Returns true if the database was created.
16
16
  */
17
17
  ensureDatabase(options?: EnsureDatabaseOptions): Promise<boolean>;
18
- refreshDatabase(options?: RefreshDatabaseOptions): Promise<void>;
19
- clearDatabase(options?: ClearDatabaseOptions): Promise<void>;
18
+ refresh(options?: RefreshDatabaseOptions): Promise<void>;
19
+ clear(options?: ClearDatabaseOptions): Promise<void>;
20
20
  protected clearIdentityMap(): void;
21
21
  getCreateSchemaSQL(options?: CreateSchemaOptions): Promise<string>;
22
- dropSchema(options?: DropSchemaOptions): Promise<void>;
22
+ drop(options?: DropSchemaOptions): Promise<void>;
23
23
  getDropSchemaSQL(options?: Omit<DropSchemaOptions, 'dropDb'>): Promise<string>;
24
- updateSchema(options?: UpdateSchemaOptions): Promise<void>;
24
+ update(options?: UpdateSchemaOptions): Promise<void>;
25
25
  getUpdateSchemaSQL(options?: UpdateSchemaOptions): Promise<string>;
26
26
  getUpdateSchemaMigrationSQL(options?: UpdateSchemaOptions): Promise<{
27
27
  up: string;
@@ -15,7 +15,7 @@ export class AbstractSchemaGenerator {
15
15
  this.platform = this.driver.getPlatform();
16
16
  this.connection = this.driver.getConnection();
17
17
  }
18
- async createSchema(options) {
18
+ async create(options) {
19
19
  this.notImplemented();
20
20
  }
21
21
  /**
@@ -24,7 +24,7 @@ export class AbstractSchemaGenerator {
24
24
  async ensureDatabase(options) {
25
25
  this.notImplemented();
26
26
  }
27
- async refreshDatabase(options) {
27
+ async refresh(options) {
28
28
  if (options?.dropDb) {
29
29
  const name = this.config.get('dbName');
30
30
  await this.dropDatabase(name);
@@ -32,20 +32,22 @@ export class AbstractSchemaGenerator {
32
32
  }
33
33
  else {
34
34
  await this.ensureDatabase();
35
- await this.dropSchema(options);
35
+ await this.drop(options);
36
36
  }
37
37
  if (options?.createSchema !== false) {
38
- await this.createSchema(options);
38
+ await this.create(options);
39
39
  }
40
40
  }
41
- async clearDatabase(options) {
41
+ async clear(options) {
42
42
  for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
43
- await this.driver.nativeDelete(meta.className, {}, options);
43
+ await this.driver.nativeDelete(meta.class, {}, options);
44
+ }
45
+ if (options?.clearIdentityMap ?? true) {
46
+ this.clearIdentityMap();
44
47
  }
45
- this.clearIdentityMap();
46
48
  }
47
49
  clearIdentityMap() {
48
- /* v8 ignore next 3 */
50
+ /* v8 ignore next */
49
51
  if (!this.em) {
50
52
  return;
51
53
  }
@@ -57,13 +59,13 @@ export class AbstractSchemaGenerator {
57
59
  async getCreateSchemaSQL(options) {
58
60
  this.notImplemented();
59
61
  }
60
- async dropSchema(options) {
62
+ async drop(options) {
61
63
  this.notImplemented();
62
64
  }
63
65
  async getDropSchemaSQL(options) {
64
66
  this.notImplemented();
65
67
  }
66
- async updateSchema(options) {
68
+ async update(options) {
67
69
  this.notImplemented();
68
70
  }
69
71
  async getUpdateSchemaSQL(options) {
@@ -88,21 +90,21 @@ export class AbstractSchemaGenerator {
88
90
  this.notImplemented();
89
91
  }
90
92
  getOrderedMetadata(schema) {
91
- const metadata = Object.values(this.metadata.getAll()).filter(meta => {
92
- const isRootEntity = meta.root.className === meta.className;
93
+ const metadata = [...this.metadata.getAll().values()].filter(meta => {
94
+ const isRootEntity = meta.root.class === meta.class;
93
95
  return isRootEntity && !meta.embeddable && !meta.virtual;
94
96
  });
95
97
  const calc = new CommitOrderCalculator();
96
- metadata.forEach(meta => calc.addNode(meta.root.className));
98
+ metadata.forEach(meta => calc.addNode(meta.root._id));
97
99
  let meta = metadata.pop();
98
100
  while (meta) {
99
- for (const prop of meta.props) {
100
- calc.discoverProperty(prop, meta.root.className);
101
+ for (const prop of meta.relations) {
102
+ calc.discoverProperty(prop, meta.root._id);
101
103
  }
102
104
  meta = metadata.pop();
103
105
  }
104
106
  return calc.sort()
105
- .map(cls => this.metadata.find(cls))
107
+ .map(cls => this.metadata.getById(cls))
106
108
  .filter(meta => {
107
109
  const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
108
110
  return schema ? [schema, '*'].includes(targetSchema) : meta.schema !== '*';
@@ -0,0 +1,6 @@
1
+ export interface AsyncContext<T> {
2
+ getStore(): T | undefined;
3
+ run<R>(store: T, callback: () => R): R;
4
+ enterWith(store: T): void;
5
+ }
6
+ export declare function createAsyncContext<T>(): AsyncContext<T>;
@@ -0,0 +1,42 @@
1
+ function getNodeAsyncContext() {
2
+ const mod = globalThis.process?.getBuiltinModule?.('node:async_hooks');
3
+ /* v8 ignore next */
4
+ if (!mod?.AsyncLocalStorage) {
5
+ throw new Error('AsyncLocalStorage not available');
6
+ }
7
+ return new mod.AsyncLocalStorage();
8
+ }
9
+ /* v8 ignore next */
10
+ function createFallbackAsyncContext() {
11
+ let store;
12
+ // eslint-disable-next-line no-console
13
+ console.warn('AsyncLocalStorage not available');
14
+ return {
15
+ getStore: () => store,
16
+ enterWith: value => store = value,
17
+ run: (value, cb) => {
18
+ const prev = store;
19
+ store = value;
20
+ try {
21
+ return cb();
22
+ }
23
+ finally {
24
+ store = prev;
25
+ }
26
+ },
27
+ };
28
+ }
29
+ export function createAsyncContext() {
30
+ /* v8 ignore next */
31
+ const ALS = globalThis.AsyncLocalStorage;
32
+ /* v8 ignore next */
33
+ if (typeof ALS === 'function' && ALS.prototype?.run) {
34
+ return new ALS();
35
+ }
36
+ /* v8 ignore else */
37
+ if (globalThis.process?.versions?.node) {
38
+ return getNodeAsyncContext();
39
+ }
40
+ /* v8 ignore next */
41
+ return createFallbackAsyncContext();
42
+ }