@mikro-orm/core 7.0.0-dev.5 → 7.0.0-dev.51

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 (115) hide show
  1. package/EntityManager.d.ts +81 -27
  2. package/EntityManager.js +287 -175
  3. package/MikroORM.d.ts +6 -6
  4. package/MikroORM.js +31 -74
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +2 -1
  7. package/cache/FileCacheAdapter.js +6 -4
  8. package/connections/Connection.d.ts +9 -5
  9. package/connections/Connection.js +16 -13
  10. package/decorators/Embedded.d.ts +5 -11
  11. package/decorators/Entity.d.ts +18 -3
  12. package/decorators/Indexed.d.ts +2 -2
  13. package/decorators/ManyToMany.d.ts +2 -0
  14. package/decorators/ManyToOne.d.ts +4 -0
  15. package/decorators/OneToOne.d.ts +4 -0
  16. package/decorators/Property.d.ts +53 -9
  17. package/decorators/Transactional.d.ts +1 -0
  18. package/decorators/Transactional.js +3 -3
  19. package/decorators/index.d.ts +1 -1
  20. package/drivers/DatabaseDriver.d.ts +10 -5
  21. package/drivers/DatabaseDriver.js +4 -4
  22. package/drivers/IDatabaseDriver.d.ts +28 -4
  23. package/entity/ArrayCollection.d.ts +6 -4
  24. package/entity/ArrayCollection.js +26 -9
  25. package/entity/BaseEntity.d.ts +0 -1
  26. package/entity/BaseEntity.js +0 -3
  27. package/entity/Collection.d.ts +3 -4
  28. package/entity/Collection.js +37 -17
  29. package/entity/EntityAssigner.d.ts +1 -1
  30. package/entity/EntityAssigner.js +9 -1
  31. package/entity/EntityFactory.d.ts +7 -0
  32. package/entity/EntityFactory.js +29 -11
  33. package/entity/EntityHelper.js +25 -8
  34. package/entity/EntityLoader.d.ts +5 -4
  35. package/entity/EntityLoader.js +69 -36
  36. package/entity/EntityRepository.d.ts +1 -1
  37. package/entity/EntityValidator.js +1 -1
  38. package/entity/Reference.d.ts +9 -7
  39. package/entity/Reference.js +30 -3
  40. package/entity/WrappedEntity.d.ts +0 -2
  41. package/entity/WrappedEntity.js +1 -5
  42. package/entity/defineEntity.d.ts +555 -0
  43. package/entity/defineEntity.js +529 -0
  44. package/entity/index.d.ts +2 -0
  45. package/entity/index.js +2 -0
  46. package/entity/utils.d.ts +7 -0
  47. package/entity/utils.js +15 -3
  48. package/enums.d.ts +16 -3
  49. package/enums.js +13 -0
  50. package/errors.d.ts +6 -1
  51. package/errors.js +14 -4
  52. package/events/EventSubscriber.d.ts +3 -1
  53. package/hydration/ObjectHydrator.d.ts +4 -4
  54. package/hydration/ObjectHydrator.js +35 -24
  55. package/index.d.ts +2 -1
  56. package/index.js +1 -1
  57. package/logging/DefaultLogger.d.ts +1 -1
  58. package/logging/SimpleLogger.d.ts +1 -1
  59. package/metadata/EntitySchema.d.ts +8 -4
  60. package/metadata/EntitySchema.js +39 -19
  61. package/metadata/MetadataDiscovery.d.ts +4 -4
  62. package/metadata/MetadataDiscovery.js +139 -122
  63. package/metadata/MetadataStorage.js +1 -1
  64. package/metadata/MetadataValidator.js +4 -3
  65. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  66. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  67. package/naming-strategy/NamingStrategy.d.ts +11 -1
  68. package/package.json +5 -5
  69. package/platforms/Platform.d.ts +5 -3
  70. package/platforms/Platform.js +4 -8
  71. package/serialization/EntitySerializer.d.ts +2 -0
  72. package/serialization/EntitySerializer.js +23 -5
  73. package/serialization/EntityTransformer.js +16 -6
  74. package/serialization/SerializationContext.js +14 -11
  75. package/types/BigIntType.d.ts +9 -6
  76. package/types/BigIntType.js +3 -0
  77. package/types/BooleanType.d.ts +1 -1
  78. package/types/DecimalType.d.ts +6 -4
  79. package/types/DecimalType.js +1 -1
  80. package/types/DoubleType.js +1 -1
  81. package/types/JsonType.d.ts +1 -1
  82. package/types/JsonType.js +7 -2
  83. package/types/Type.d.ts +2 -1
  84. package/types/Type.js +1 -1
  85. package/types/index.d.ts +1 -1
  86. package/typings.d.ts +89 -49
  87. package/typings.js +31 -31
  88. package/unit-of-work/ChangeSetComputer.js +8 -3
  89. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  90. package/unit-of-work/ChangeSetPersister.js +37 -16
  91. package/unit-of-work/UnitOfWork.d.ts +8 -1
  92. package/unit-of-work/UnitOfWork.js +110 -53
  93. package/utils/AbstractSchemaGenerator.js +3 -1
  94. package/utils/Configuration.d.ts +200 -183
  95. package/utils/Configuration.js +137 -144
  96. package/utils/ConfigurationLoader.d.ts +9 -22
  97. package/utils/ConfigurationLoader.js +49 -72
  98. package/utils/Cursor.d.ts +3 -3
  99. package/utils/Cursor.js +3 -0
  100. package/utils/DataloaderUtils.d.ts +7 -2
  101. package/utils/DataloaderUtils.js +38 -7
  102. package/utils/EntityComparator.d.ts +6 -2
  103. package/utils/EntityComparator.js +104 -58
  104. package/utils/QueryHelper.d.ts +9 -1
  105. package/utils/QueryHelper.js +66 -5
  106. package/utils/RawQueryFragment.d.ts +36 -4
  107. package/utils/RawQueryFragment.js +34 -13
  108. package/utils/TransactionManager.d.ts +65 -0
  109. package/utils/TransactionManager.js +223 -0
  110. package/utils/Utils.d.ts +13 -11
  111. package/utils/Utils.js +82 -55
  112. package/utils/index.d.ts +1 -0
  113. package/utils/index.js +1 -0
  114. package/utils/upsert-utils.d.ts +7 -2
  115. package/utils/upsert-utils.js +52 -1
@@ -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,7 +45,7 @@ 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;
48
+ getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
43
49
  tryGetById<T extends object>(entityName: string, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
44
50
  /**
45
51
  * Returns map of all managed entities.
@@ -103,6 +109,7 @@ export declare class UnitOfWork {
103
109
  private commitDeleteChangeSets;
104
110
  private commitExtraUpdates;
105
111
  private commitCollectionUpdates;
112
+ private filterCollectionUpdates;
106
113
  /**
107
114
  * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
108
115
  */
@@ -43,7 +43,7 @@ export class UnitOfWork {
43
43
  this.eventManager = this.em.getEventManager();
44
44
  this.comparator = this.em.getComparator();
45
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);
46
+ this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
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();
@@ -185,10 +210,8 @@ export class UnitOfWork {
185
210
  if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
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 = {}) {
@@ -303,6 +324,7 @@ 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
330
  return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
@@ -311,9 +333,11 @@ export class UnitOfWork {
311
333
  const platform = this.em.getPlatform();
312
334
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
335
  if (runInTransaction) {
336
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
337
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
338
  ctx: oldTx,
316
339
  eventBroadcaster: new TransactionEventBroadcaster(this.em),
340
+ loggerContext,
317
341
  });
318
342
  }
319
343
  else {
@@ -369,7 +393,6 @@ export class UnitOfWork {
369
393
  }
370
394
  delete wrapped.__identifier;
371
395
  delete wrapped.__originalEntityData;
372
- wrapped.__touched = false;
373
396
  wrapped.__managed = false;
374
397
  }
375
398
  computeChangeSets() {
@@ -379,14 +402,14 @@ export class UnitOfWork {
379
402
  this.cascade(entity, Cascade.REMOVE, visited);
380
403
  }
381
404
  visited.clear();
382
- for (const entity of this.persistStack) {
383
- this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
384
- }
385
405
  for (const entity of this.identityMap) {
386
406
  if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
387
407
  this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
388
408
  }
389
409
  }
410
+ for (const entity of this.persistStack) {
411
+ this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
412
+ }
390
413
  visited.clear();
391
414
  for (const entity of this.persistStack) {
392
415
  this.findNewEntities(entity, visited);
@@ -542,13 +565,22 @@ export class UnitOfWork {
542
565
  if (!wrapped || wrapped.__identifier || wrapped.hasPrimaryKey()) {
543
566
  return;
544
567
  }
545
- const pk = wrapped.__meta.getPrimaryProps()[0];
546
- if (pk.kind === ReferenceKind.SCALAR) {
547
- wrapped.__identifier = new EntityIdentifier();
568
+ const pks = wrapped.__meta.getPrimaryProps();
569
+ const idents = [];
570
+ for (const pk of pks) {
571
+ if (pk.kind === ReferenceKind.SCALAR) {
572
+ idents.push(new EntityIdentifier(entity[pk.name]));
573
+ }
574
+ else if (entity[pk.name]) {
575
+ this.initIdentifier(entity[pk.name]);
576
+ idents.push(helper(entity[pk.name])?.__identifier);
577
+ }
578
+ }
579
+ if (pks.length === 1) {
580
+ wrapped.__identifier = idents[0];
548
581
  }
549
- else if (entity[pk.name]) {
550
- this.initIdentifier(entity[pk.name]);
551
- wrapped.__identifier = helper(entity[pk.name])?.__identifier;
582
+ else {
583
+ wrapped.__identifier = idents;
552
584
  }
553
585
  }
554
586
  processReference(parent, prop, kind, visited, processed, idx) {
@@ -600,8 +632,15 @@ export class UnitOfWork {
600
632
  const diff = this.comparator.diffEntities(changeSet.name, copy, current);
601
633
  Object.assign(changeSet.payload, diff);
602
634
  const wrapped = helper(changeSet.entity);
603
- if (wrapped.__identifier && diff[wrapped.__meta.primaryKeys[0]]) {
604
- wrapped.__identifier.setValue(diff[wrapped.__meta.primaryKeys[0]]);
635
+ if (wrapped.__identifier) {
636
+ const idents = Utils.asArray(wrapped.__identifier);
637
+ let i = 0;
638
+ for (const pk of wrapped.__meta.primaryKeys) {
639
+ if (diff[pk]) {
640
+ idents[i].setValue(diff[pk]);
641
+ }
642
+ i++;
643
+ }
605
644
  }
606
645
  }
607
646
  postCommitCleanup() {
@@ -825,9 +864,16 @@ export class UnitOfWork {
825
864
  }
826
865
  await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
827
866
  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;
867
+ const wrapped = helper(changeSet.entity);
868
+ wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
869
+ if (!wrapped.__initialized) {
870
+ for (const prop of changeSet.meta.relations) {
871
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
872
+ changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
873
+ }
874
+ }
875
+ wrapped.__initialized = true;
876
+ }
831
877
  await this.runHooks(EventType.afterUpdate, changeSet);
832
878
  }
833
879
  }
@@ -870,23 +916,34 @@ export class UnitOfWork {
870
916
  }
871
917
  }
872
918
  async commitCollectionUpdates(ctx) {
873
- const collectionUpdates = [];
919
+ this.filterCollectionUpdates();
920
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
921
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
922
+ ctx,
923
+ schema: this.em.schema,
924
+ loggerContext,
925
+ });
926
+ for (const coll of this.collectionUpdates) {
927
+ coll.takeSnapshot();
928
+ }
929
+ }
930
+ filterCollectionUpdates() {
874
931
  for (const coll of this.collectionUpdates) {
932
+ let skip = true;
875
933
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
876
934
  if (this.platform.usesPivotTable()) {
877
- collectionUpdates.push(coll);
935
+ skip = false;
878
936
  }
879
937
  }
880
938
  else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
881
- collectionUpdates.push(coll);
939
+ skip = false;
882
940
  }
883
941
  else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
884
- collectionUpdates.push(coll);
942
+ skip = false;
943
+ }
944
+ if (skip) {
945
+ this.collectionUpdates.delete(coll);
885
946
  }
886
- }
887
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
888
- for (const coll of this.collectionUpdates) {
889
- coll.takeSnapshot();
890
947
  }
891
948
  }
892
949
  /**
@@ -42,7 +42,9 @@ export class AbstractSchemaGenerator {
42
42
  for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
43
43
  await this.driver.nativeDelete(meta.className, {}, options);
44
44
  }
45
- this.clearIdentityMap();
45
+ if (options?.clearIdentityMap ?? true) {
46
+ this.clearIdentityMap();
47
+ }
46
48
  }
47
49
  clearIdentityMap() {
48
50
  /* v8 ignore next 3 */