@mikro-orm/core 7.0.0-dev.4 → 7.0.0-dev.40

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 (117) hide show
  1. package/EntityManager.d.ts +84 -18
  2. package/EntityManager.js +265 -172
  3. package/MikroORM.d.ts +7 -5
  4. package/MikroORM.js +0 -1
  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 +4 -2
  9. package/connections/Connection.js +2 -2
  10. package/decorators/Check.d.ts +2 -2
  11. package/decorators/Embeddable.d.ts +5 -5
  12. package/decorators/Embeddable.js +1 -1
  13. package/decorators/Embedded.d.ts +6 -12
  14. package/decorators/Entity.d.ts +18 -3
  15. package/decorators/Enum.d.ts +1 -1
  16. package/decorators/Formula.d.ts +1 -2
  17. package/decorators/Indexed.d.ts +2 -2
  18. package/decorators/ManyToMany.d.ts +4 -2
  19. package/decorators/ManyToOne.d.ts +6 -2
  20. package/decorators/OneToMany.d.ts +4 -4
  21. package/decorators/OneToOne.d.ts +5 -1
  22. package/decorators/PrimaryKey.d.ts +2 -3
  23. package/decorators/Property.d.ts +54 -4
  24. package/decorators/Transactional.d.ts +1 -0
  25. package/decorators/Transactional.js +3 -3
  26. package/decorators/index.d.ts +1 -1
  27. package/drivers/DatabaseDriver.d.ts +4 -3
  28. package/drivers/IDatabaseDriver.d.ts +22 -2
  29. package/entity/ArrayCollection.d.ts +4 -2
  30. package/entity/ArrayCollection.js +18 -6
  31. package/entity/Collection.d.ts +1 -2
  32. package/entity/Collection.js +19 -10
  33. package/entity/EntityAssigner.d.ts +1 -1
  34. package/entity/EntityAssigner.js +9 -1
  35. package/entity/EntityFactory.d.ts +7 -0
  36. package/entity/EntityFactory.js +29 -9
  37. package/entity/EntityHelper.js +25 -3
  38. package/entity/EntityLoader.d.ts +5 -4
  39. package/entity/EntityLoader.js +74 -37
  40. package/entity/EntityRepository.d.ts +1 -1
  41. package/entity/EntityValidator.js +1 -1
  42. package/entity/Reference.d.ts +9 -7
  43. package/entity/Reference.js +30 -3
  44. package/entity/WrappedEntity.js +1 -1
  45. package/entity/defineEntity.d.ts +561 -0
  46. package/entity/defineEntity.js +537 -0
  47. package/entity/index.d.ts +2 -0
  48. package/entity/index.js +2 -0
  49. package/entity/utils.d.ts +7 -0
  50. package/entity/utils.js +15 -3
  51. package/enums.d.ts +16 -3
  52. package/enums.js +13 -0
  53. package/errors.d.ts +6 -0
  54. package/errors.js +14 -0
  55. package/events/EventSubscriber.d.ts +3 -1
  56. package/hydration/ObjectHydrator.d.ts +4 -4
  57. package/hydration/ObjectHydrator.js +35 -24
  58. package/index.d.ts +2 -1
  59. package/index.js +1 -1
  60. package/logging/DefaultLogger.d.ts +1 -1
  61. package/logging/SimpleLogger.d.ts +1 -1
  62. package/metadata/EntitySchema.d.ts +8 -4
  63. package/metadata/EntitySchema.js +39 -19
  64. package/metadata/MetadataDiscovery.d.ts +1 -1
  65. package/metadata/MetadataDiscovery.js +88 -32
  66. package/metadata/MetadataStorage.js +1 -1
  67. package/metadata/MetadataValidator.js +4 -3
  68. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  69. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  70. package/naming-strategy/NamingStrategy.d.ts +11 -1
  71. package/package.json +5 -5
  72. package/platforms/Platform.d.ts +5 -3
  73. package/platforms/Platform.js +4 -8
  74. package/serialization/EntitySerializer.d.ts +2 -0
  75. package/serialization/EntitySerializer.js +2 -2
  76. package/serialization/EntityTransformer.js +1 -1
  77. package/serialization/SerializationContext.js +14 -11
  78. package/types/BigIntType.d.ts +9 -6
  79. package/types/BigIntType.js +3 -0
  80. package/types/BooleanType.d.ts +1 -1
  81. package/types/DecimalType.d.ts +6 -4
  82. package/types/DecimalType.js +1 -1
  83. package/types/DoubleType.js +1 -1
  84. package/types/JsonType.d.ts +1 -1
  85. package/types/JsonType.js +7 -2
  86. package/types/Type.d.ts +2 -1
  87. package/types/Type.js +1 -1
  88. package/types/index.d.ts +1 -1
  89. package/typings.d.ts +88 -39
  90. package/typings.js +24 -4
  91. package/unit-of-work/ChangeSetComputer.js +3 -1
  92. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  93. package/unit-of-work/ChangeSetPersister.js +37 -16
  94. package/unit-of-work/UnitOfWork.d.ts +8 -1
  95. package/unit-of-work/UnitOfWork.js +109 -41
  96. package/utils/Configuration.d.ts +23 -5
  97. package/utils/Configuration.js +17 -3
  98. package/utils/ConfigurationLoader.d.ts +0 -2
  99. package/utils/ConfigurationLoader.js +2 -24
  100. package/utils/Cursor.d.ts +3 -3
  101. package/utils/Cursor.js +3 -0
  102. package/utils/DataloaderUtils.d.ts +7 -2
  103. package/utils/DataloaderUtils.js +38 -7
  104. package/utils/EntityComparator.d.ts +6 -2
  105. package/utils/EntityComparator.js +104 -58
  106. package/utils/QueryHelper.d.ts +9 -1
  107. package/utils/QueryHelper.js +66 -5
  108. package/utils/RawQueryFragment.d.ts +36 -2
  109. package/utils/RawQueryFragment.js +35 -1
  110. package/utils/TransactionManager.d.ts +65 -0
  111. package/utils/TransactionManager.js +218 -0
  112. package/utils/Utils.d.ts +11 -5
  113. package/utils/Utils.js +76 -33
  114. package/utils/index.d.ts +1 -0
  115. package/utils/index.js +1 -0
  116. package/utils/upsert-utils.d.ts +7 -2
  117. 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);
@@ -65,6 +65,40 @@ export class UnitOfWork {
65
65
  }
66
66
  this.cascade(entity, Cascade.MERGE, visited ?? new Set());
67
67
  }
68
+ /**
69
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
70
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
71
+ * @internal
72
+ */
73
+ normalizeEntityData(meta, data) {
74
+ const forceUndefined = this.em.config.get('forceUndefined');
75
+ for (const key of Utils.keys(data)) {
76
+ const prop = meta.properties[key];
77
+ if (!prop) {
78
+ continue;
79
+ }
80
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
81
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
82
+ }
83
+ else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
84
+ for (const p of prop.targetMeta.props) {
85
+ /* v8 ignore next */
86
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
87
+ data[prefix + p.name] = data[prop.name][p.name];
88
+ }
89
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
90
+ }
91
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
92
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
93
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
94
+ }
95
+ if (forceUndefined) {
96
+ if (data[key] === null) {
97
+ data[key] = undefined;
98
+ }
99
+ }
100
+ }
101
+ }
68
102
  /**
69
103
  * @internal
70
104
  */
@@ -82,27 +116,11 @@ export class UnitOfWork {
82
116
  wrapped.__em ??= this.em;
83
117
  wrapped.__managed = true;
84
118
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
119
+ this.normalizeEntityData(wrapped.__meta, data);
85
120
  for (const key of Utils.keys(data)) {
86
121
  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
- }
122
+ if (prop) {
123
+ wrapped.__loadedProperties.add(key);
106
124
  }
107
125
  }
108
126
  wrapped.__originalEntityData = data;
@@ -125,7 +143,7 @@ export class UnitOfWork {
125
143
  /**
126
144
  * 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
145
  */
128
- getById(entityName, id, schema) {
146
+ getById(entityName, id, schema, convertCustomTypes) {
129
147
  if (id == null || (Array.isArray(id) && id.length === 0)) {
130
148
  return undefined;
131
149
  }
@@ -135,7 +153,16 @@ export class UnitOfWork {
135
153
  hash = '' + id;
136
154
  }
137
155
  else {
138
- const keys = Array.isArray(id) ? Utils.flatten(id) : [id];
156
+ let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
157
+ keys = meta.getPrimaryProps(true).map((p, i) => {
158
+ if (!convertCustomTypes && p.customType) {
159
+ return p.customType.convertToDatabaseValue(keys[i], this.platform, {
160
+ key: p.name,
161
+ mode: 'hydration',
162
+ });
163
+ }
164
+ return keys[i];
165
+ });
139
166
  hash = Utils.getPrimaryKeyHash(keys);
140
167
  }
141
168
  schema ??= meta.schema ?? this.em.config.getSchema();
@@ -185,6 +212,9 @@ export class UnitOfWork {
185
212
  if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
186
213
  return true;
187
214
  }
215
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
216
+ return true;
217
+ }
188
218
  for (const entity of this.identityMap.getStore(meta).values()) {
189
219
  if (helper(entity).__initialized && helper(entity).isTouched()) {
190
220
  return true;
@@ -303,6 +333,7 @@ export class UnitOfWork {
303
333
  cs.entity.__helper.__processing = true;
304
334
  }
305
335
  await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
336
+ this.filterCollectionUpdates();
306
337
  // nothing to do, do not start transaction
307
338
  if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
308
339
  return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
@@ -311,9 +342,11 @@ export class UnitOfWork {
311
342
  const platform = this.em.getPlatform();
312
343
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
344
  if (runInTransaction) {
345
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
346
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
347
  ctx: oldTx,
316
348
  eventBroadcaster: new TransactionEventBroadcaster(this.em),
349
+ loggerContext,
317
350
  });
318
351
  }
319
352
  else {
@@ -542,13 +575,22 @@ export class UnitOfWork {
542
575
  if (!wrapped || wrapped.__identifier || wrapped.hasPrimaryKey()) {
543
576
  return;
544
577
  }
545
- const pk = wrapped.__meta.getPrimaryProps()[0];
546
- if (pk.kind === ReferenceKind.SCALAR) {
547
- wrapped.__identifier = new EntityIdentifier();
578
+ const pks = wrapped.__meta.getPrimaryProps();
579
+ const idents = [];
580
+ for (const pk of pks) {
581
+ if (pk.kind === ReferenceKind.SCALAR) {
582
+ idents.push(new EntityIdentifier(entity[pk.name]));
583
+ }
584
+ else if (entity[pk.name]) {
585
+ this.initIdentifier(entity[pk.name]);
586
+ idents.push(helper(entity[pk.name])?.__identifier);
587
+ }
588
+ }
589
+ if (pks.length === 1) {
590
+ wrapped.__identifier = idents[0];
548
591
  }
549
- else if (entity[pk.name]) {
550
- this.initIdentifier(entity[pk.name]);
551
- wrapped.__identifier = helper(entity[pk.name])?.__identifier;
592
+ else {
593
+ wrapped.__identifier = idents;
552
594
  }
553
595
  }
554
596
  processReference(parent, prop, kind, visited, processed, idx) {
@@ -600,8 +642,15 @@ export class UnitOfWork {
600
642
  const diff = this.comparator.diffEntities(changeSet.name, copy, current);
601
643
  Object.assign(changeSet.payload, diff);
602
644
  const wrapped = helper(changeSet.entity);
603
- if (wrapped.__identifier && diff[wrapped.__meta.primaryKeys[0]]) {
604
- wrapped.__identifier.setValue(diff[wrapped.__meta.primaryKeys[0]]);
645
+ if (wrapped.__identifier) {
646
+ const idents = Utils.asArray(wrapped.__identifier);
647
+ let i = 0;
648
+ for (const pk of wrapped.__meta.primaryKeys) {
649
+ if (diff[pk]) {
650
+ idents[i].setValue(diff[pk]);
651
+ }
652
+ i++;
653
+ }
605
654
  }
606
655
  }
607
656
  postCommitCleanup() {
@@ -825,9 +874,17 @@ export class UnitOfWork {
825
874
  }
826
875
  await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
827
876
  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;
877
+ const wrapped = helper(changeSet.entity);
878
+ wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
879
+ wrapped.__touched = false;
880
+ if (!wrapped.__initialized) {
881
+ for (const prop of changeSet.meta.relations) {
882
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
883
+ changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
884
+ }
885
+ }
886
+ wrapped.__initialized = true;
887
+ }
831
888
  await this.runHooks(EventType.afterUpdate, changeSet);
832
889
  }
833
890
  }
@@ -870,23 +927,34 @@ export class UnitOfWork {
870
927
  }
871
928
  }
872
929
  async commitCollectionUpdates(ctx) {
873
- const collectionUpdates = [];
930
+ this.filterCollectionUpdates();
931
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
932
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
933
+ ctx,
934
+ schema: this.em.schema,
935
+ loggerContext,
936
+ });
937
+ for (const coll of this.collectionUpdates) {
938
+ coll.takeSnapshot();
939
+ }
940
+ }
941
+ filterCollectionUpdates() {
874
942
  for (const coll of this.collectionUpdates) {
943
+ let skip = true;
875
944
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
876
945
  if (this.platform.usesPivotTable()) {
877
- collectionUpdates.push(coll);
946
+ skip = false;
878
947
  }
879
948
  }
880
949
  else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
881
- collectionUpdates.push(coll);
950
+ skip = false;
882
951
  }
883
952
  else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
884
- collectionUpdates.push(coll);
953
+ skip = false;
954
+ }
955
+ if (skip) {
956
+ this.collectionUpdates.delete(coll);
885
957
  }
886
- }
887
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
888
- for (const coll of this.collectionUpdates) {
889
- coll.takeSnapshot();
890
958
  }
891
959
  }
892
960
  /**
@@ -54,7 +54,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
54
54
  baseDir: string;
55
55
  hydrator: typeof ObjectHydrator;
56
56
  flushMode: FlushMode.AUTO;
57
- loadStrategy: LoadStrategy.JOINED;
57
+ loadStrategy: LoadStrategy.BALANCED;
58
58
  dataloader: DataloaderType.NONE;
59
59
  populateWhere: PopulateHint.ALL;
60
60
  connect: true;
@@ -62,6 +62,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
62
62
  onQuery: (sql: string) => string;
63
63
  autoJoinOneToOneOwner: true;
64
64
  autoJoinRefsForFilters: true;
65
+ filtersOnRelations: true;
65
66
  propagationOnPrototype: true;
66
67
  populateAfterFlush: true;
67
68
  serialization: {
@@ -78,9 +79,11 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
78
79
  upsertManaged: true;
79
80
  forceEntityConstructor: false;
80
81
  forceUndefined: false;
82
+ processOnCreateHooksEarly: false;
81
83
  ensureDatabase: true;
82
84
  ensureIndexes: false;
83
85
  batchSize: number;
86
+ hashAlgorithm: "md5";
84
87
  debug: false;
85
88
  ignoreDeprecations: false;
86
89
  verbose: false;
@@ -103,9 +106,11 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
103
106
  disableForeignKeys: false;
104
107
  createForeignKeyConstraints: true;
105
108
  ignoreSchema: never[];
109
+ skipTables: never[];
110
+ skipColumns: {};
106
111
  };
107
112
  embeddables: {
108
- prefixMode: "absolute";
113
+ prefixMode: "relative";
109
114
  };
110
115
  entityGenerator: {
111
116
  forceUndefined: true;
@@ -114,6 +119,8 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
114
119
  identifiedReferences: false;
115
120
  scalarTypeInDecorator: false;
116
121
  scalarPropertiesForRelations: "never";
122
+ entityDefinition: "decorators";
123
+ enumMode: "ts-enum";
117
124
  fileName: (className: string) => string;
118
125
  onlyPurePivotTables: false;
119
126
  outputPurePivotTables: false;
@@ -232,7 +239,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
232
239
  /**
233
240
  * Type helper to make it easier to use `mikro-orm.config.js`.
234
241
  */
235
- export declare function defineConfig<D extends IDatabaseDriver>(options: Options<D>): Options<D, D[typeof EntityManagerType] & EntityManager<IDatabaseDriver<import("../index.js").Connection>>>;
242
+ export declare function defineConfig<D extends IDatabaseDriver>(options: Options<D>): Options<D, D[typeof EntityManagerType] & EntityManager<IDatabaseDriver<import("../index.js").Connection>>, (string | EntityClass<Partial<any>> | EntityClassGroup<Partial<any>> | EntitySchema<any, never>)[]>;
236
243
  export interface ConnectionOptions {
237
244
  dbName?: string;
238
245
  schema?: string;
@@ -320,6 +327,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
320
327
  onQuery: (sql: string, params: readonly unknown[]) => string;
321
328
  autoJoinOneToOneOwner: boolean;
322
329
  autoJoinRefsForFilters: boolean;
330
+ filtersOnRelations: boolean;
323
331
  propagationOnPrototype: boolean;
324
332
  populateAfterFlush: boolean;
325
333
  serialization: {
@@ -332,6 +340,11 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
332
340
  upsertManaged: boolean;
333
341
  forceEntityConstructor: boolean | (Constructor<AnyEntity> | string)[];
334
342
  forceUndefined: boolean;
343
+ /**
344
+ * Property `onCreate` hooks are normally executed during `flush` operation.
345
+ * With this option, they will be processed early inside `em.create()` method.
346
+ */
347
+ processOnCreateHooksEarly: boolean;
335
348
  forceUtcTimezone?: boolean;
336
349
  timezone?: string;
337
350
  ensureDatabase: boolean | EnsureDatabaseOptions;
@@ -340,7 +353,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
340
353
  useBatchUpdates?: boolean;
341
354
  batchSize: number;
342
355
  hydrator: HydratorConstructor;
343
- loadStrategy: LoadStrategy | 'select-in' | 'joined';
356
+ loadStrategy: LoadStrategy | `${LoadStrategy}`;
344
357
  dataloader: DataloaderType | boolean;
345
358
  populateWhere?: PopulateHint | `${PopulateHint}`;
346
359
  flushMode: FlushMode | 'commit' | 'auto' | 'always';
@@ -374,6 +387,8 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
374
387
  disableForeignKeys?: boolean;
375
388
  createForeignKeyConstraints?: boolean;
376
389
  ignoreSchema?: string[];
390
+ skipTables?: (string | RegExp)[];
391
+ skipColumns?: Dictionary<(string | RegExp)[]>;
377
392
  managementDbName?: string;
378
393
  };
379
394
  embeddables: {
@@ -403,5 +418,8 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
403
418
  seeder: SeederOptions;
404
419
  preferReadReplicas: boolean;
405
420
  dynamicImportProvider: (id: string) => Promise<unknown>;
421
+ hashAlgorithm: 'md5' | 'sha256';
422
+ }
423
+ export interface Options<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager = D[typeof EntityManagerType] & EntityManager, Entities extends (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[]> extends Pick<MikroORMOptions<D, EM>, Exclude<keyof MikroORMOptions, keyof typeof Configuration.DEFAULTS>>, Partial<MikroORMOptions<D, EM>> {
424
+ entities?: Entities;
406
425
  }
407
- export type Options<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager = D[typeof EntityManagerType] & EntityManager> = Pick<MikroORMOptions<D, EM>, Exclude<keyof MikroORMOptions<D, EM>, keyof typeof Configuration.DEFAULTS>> & Partial<MikroORMOptions<D, EM>>;
@@ -44,7 +44,7 @@ export class Configuration {
44
44
  baseDir: process.cwd(),
45
45
  hydrator: ObjectHydrator,
46
46
  flushMode: FlushMode.AUTO,
47
- loadStrategy: LoadStrategy.JOINED,
47
+ loadStrategy: LoadStrategy.BALANCED,
48
48
  dataloader: DataloaderType.NONE,
49
49
  populateWhere: PopulateHint.ALL,
50
50
  connect: true,
@@ -52,6 +52,7 @@ export class Configuration {
52
52
  onQuery: sql => sql,
53
53
  autoJoinOneToOneOwner: true,
54
54
  autoJoinRefsForFilters: true,
55
+ filtersOnRelations: true,
55
56
  propagationOnPrototype: true,
56
57
  populateAfterFlush: true,
57
58
  serialization: {
@@ -68,9 +69,11 @@ export class Configuration {
68
69
  upsertManaged: true,
69
70
  forceEntityConstructor: false,
70
71
  forceUndefined: false,
72
+ processOnCreateHooksEarly: false,
71
73
  ensureDatabase: true,
72
74
  ensureIndexes: false,
73
75
  batchSize: 300,
76
+ hashAlgorithm: 'md5',
74
77
  debug: false,
75
78
  ignoreDeprecations: false,
76
79
  verbose: false,
@@ -93,9 +96,11 @@ export class Configuration {
93
96
  disableForeignKeys: false,
94
97
  createForeignKeyConstraints: true,
95
98
  ignoreSchema: [],
99
+ skipTables: [],
100
+ skipColumns: {},
96
101
  },
97
102
  embeddables: {
98
- prefixMode: 'absolute',
103
+ prefixMode: 'relative',
99
104
  },
100
105
  entityGenerator: {
101
106
  forceUndefined: true,
@@ -104,6 +109,8 @@ export class Configuration {
104
109
  identifiedReferences: false,
105
110
  scalarTypeInDecorator: false,
106
111
  scalarPropertiesForRelations: 'never',
112
+ entityDefinition: 'decorators',
113
+ enumMode: 'ts-enum',
107
114
  fileName: (className) => className,
108
115
  onlyPurePivotTables: false,
109
116
  outputPurePivotTables: false,
@@ -268,7 +275,7 @@ export class Configuration {
268
275
  * Gets instance of metadata CacheAdapter. (cached)
269
276
  */
270
277
  getMetadataCacheAdapter() {
271
- return this.getCachedService(this.options.metadataCache.adapter, this.options.metadataCache.options, this.options.baseDir, this.options.metadataCache.pretty);
278
+ return this.getCachedService(this.options.metadataCache.adapter, this.options.metadataCache.options, this.options.baseDir, this.options.metadataCache.pretty, this.options.hashAlgorithm);
272
279
  }
273
280
  /**
274
281
  * Gets instance of CacheAdapter for result cache. (cached)
@@ -338,6 +345,9 @@ export class Configuration {
338
345
  Object.keys(this.options.filters).forEach(key => {
339
346
  this.options.filters[key].default ??= true;
340
347
  });
348
+ if (!this.options.filtersOnRelations) {
349
+ this.options.autoJoinRefsForFilters ??= false;
350
+ }
341
351
  this.options.subscribers = Utils.unique(this.options.subscribers).map(subscriber => {
342
352
  return subscriber.constructor.name === 'Function' ? new subscriber() : subscriber;
343
353
  });
@@ -348,6 +358,10 @@ export class Configuration {
348
358
  }
349
359
  sync() {
350
360
  process.env.MIKRO_ORM_COLORS = '' + this.options.colors;
361
+ // FIXME remove `entityGenerator.entitySchema` option
362
+ if (this.options.entityGenerator.entitySchema) {
363
+ this.options.entityGenerator.entityDefinition = 'entitySchema';
364
+ }
351
365
  this.logger.setDebugMode(this.options.debug);
352
366
  }
353
367
  /**
@@ -40,8 +40,6 @@ export declare class ConfigurationLoader {
40
40
  static loadEnvironmentVars<D extends IDatabaseDriver>(): Promise<Partial<Options<D>>>;
41
41
  static loadEnvironmentVarsSync<D extends IDatabaseDriver>(): Partial<Options<D>>;
42
42
  static getORMPackages(): Set<string>;
43
- /** @internal */
44
- static commonJSCompat(options: Partial<Options>): void;
45
43
  static getORMPackageVersion(name: string): string | undefined;
46
44
  static checkPackageVersion(): string;
47
45
  }
@@ -1,7 +1,5 @@
1
1
  import dotenv from 'dotenv';
2
2
  import { realpathSync } from 'node:fs';
3
- import { platform } from 'node:os';
4
- import { fileURLToPath } from 'node:url';
5
3
  import { colors } from '../logging/colors.js';
6
4
  import { Configuration } from './Configuration.js';
7
5
  import { Utils } from './Utils.js';
@@ -12,7 +10,6 @@ export class ConfigurationLoader {
12
10
  static async getConfiguration(contextName = 'default', paths = ConfigurationLoader.getConfigPaths(), options = {}) {
13
11
  // Backwards compatibility layer
14
12
  if (typeof contextName === 'boolean' || !Array.isArray(paths)) {
15
- this.commonJSCompat(options);
16
13
  this.registerDotenv(options);
17
14
  const configPathFromArg = ConfigurationLoader.configPathsFromArg();
18
15
  const configPaths = configPathFromArg ?? (Array.isArray(paths) ? paths : ConfigurationLoader.getConfigPaths());
@@ -192,7 +189,7 @@ export class ConfigurationLoader {
192
189
  static registerDotenv(options) {
193
190
  const path = process.env.MIKRO_ORM_ENV ?? ((options?.baseDir ?? process.cwd()) + '/.env');
194
191
  const env = {};
195
- dotenv.config({ path, processEnv: env });
192
+ dotenv.config({ path, processEnv: env, quiet: true });
196
193
  // only propagate known env vars
197
194
  for (const key of Object.keys(env)) {
198
195
  if (key.startsWith('MIKRO_ORM_')) {
@@ -300,25 +297,6 @@ export class ConfigurationLoader {
300
297
  ...Object.keys(pkg.devDependencies ?? {}),
301
298
  ]);
302
299
  }
303
- /** @internal */
304
- static commonJSCompat(options) {
305
- if (this.isESM()) {
306
- return;
307
- }
308
- /* v8 ignore next 11 */
309
- options.dynamicImportProvider ??= id => {
310
- if (platform() === 'win32') {
311
- try {
312
- id = fileURLToPath(id);
313
- }
314
- catch {
315
- // ignore
316
- }
317
- }
318
- return Utils.requireFrom(id);
319
- };
320
- Utils.setDynamicImportProvider(options.dynamicImportProvider);
321
- }
322
300
  static getORMPackageVersion(name) {
323
301
  try {
324
302
  const pkg = Utils.requireFrom(`${name}/package.json`);
@@ -332,7 +310,7 @@ export class ConfigurationLoader {
332
310
  // inspired by https://github.com/facebook/docusaurus/pull/3386
333
311
  static checkPackageVersion() {
334
312
  const coreVersion = Utils.getORMVersion();
335
- if (process.env.MIKRO_ORM_ALLOW_VERSION_MISMATCH) {
313
+ if (process.env.MIKRO_ORM_ALLOW_VERSION_MISMATCH || coreVersion === 'N/A') {
336
314
  return coreVersion;
337
315
  }
338
316
  const deps = this.getORMPackages();
package/utils/Cursor.d.ts CHANGED
@@ -49,13 +49,13 @@ import { type QueryOrder } from '../enums.js';
49
49
  * }
50
50
  * ```
51
51
  */
52
- export declare class Cursor<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never> {
52
+ export declare class Cursor<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never, IncludeCount extends boolean = true> {
53
53
  readonly items: Loaded<Entity, Hint, Fields, Excludes>[];
54
- readonly totalCount: number;
54
+ readonly totalCount: IncludeCount extends true ? number : undefined;
55
55
  readonly hasPrevPage: boolean;
56
56
  readonly hasNextPage: boolean;
57
57
  private readonly definition;
58
- constructor(items: Loaded<Entity, Hint, Fields, Excludes>[], totalCount: number, options: FindByCursorOptions<Entity, Hint, Fields, Excludes>, meta: EntityMetadata<Entity>);
58
+ constructor(items: Loaded<Entity, Hint, Fields, Excludes>[], totalCount: IncludeCount extends true ? number : undefined, options: FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>, meta: EntityMetadata<Entity>);
59
59
  get startCursor(): string | null;
60
60
  get endCursor(): string | null;
61
61
  /**
package/utils/Cursor.js CHANGED
@@ -107,6 +107,9 @@ export class Cursor {
107
107
  if (Utils.isEntity(value, true)) {
108
108
  value = helper(value).getPrimaryKey();
109
109
  }
110
+ if (Utils.isScalarReference(value)) {
111
+ value = value.unwrap();
112
+ }
110
113
  if (object) {
111
114
  return ({ [prop]: value });
112
115
  }
@@ -1,7 +1,7 @@
1
+ import type DataLoader from 'dataloader';
1
2
  import type { Primary, Ref } from '../typings.js';
2
3
  import { Collection, type InitCollectionOptions } from '../entity/Collection.js';
3
4
  import { type EntityManager } from '../EntityManager.js';
4
- import type DataLoader from 'dataloader';
5
5
  import { type LoadReferenceOptions } from '../entity/Reference.js';
6
6
  export declare class DataloaderUtils {
7
7
  /**
@@ -34,8 +34,13 @@ export declare class DataloaderUtils {
34
34
  */
35
35
  static getColFilter<T, S extends T>(collection: Collection<any>): (result: T) => result is S;
36
36
  /**
37
- * Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
37
+ * Returns the 1:M collection dataloader batchLoadFn, which aggregates collections by entity,
38
38
  * makes one query per entity and maps each input collection to the corresponding result.
39
39
  */
40
40
  static getColBatchLoadFn(em: EntityManager): DataLoader.BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
41
+ /**
42
+ * Returns the M:N collection dataloader batchLoadFn, which aggregates collections by entity,
43
+ * makes one query per entity and maps each input collection to the corresponding result.
44
+ */
45
+ static getManyToManyColBatchLoadFn(em: EntityManager): DataLoader.BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
41
46
  }
@@ -1,7 +1,7 @@
1
1
  import { Collection } from '../entity/Collection.js';
2
2
  import { helper } from '../entity/wrap.js';
3
- import { ReferenceKind } from '../enums.js';
4
3
  import { Reference } from '../entity/Reference.js';
4
+ import { Utils } from './Utils.js';
5
5
  export class DataloaderUtils {
6
6
  /**
7
7
  * Groups identified references by entity and returns a Map with the
@@ -118,7 +118,6 @@ export class DataloaderUtils {
118
118
  // We need to populate the inverse side of the relationship in order to be able to later retrieve the PK(s) from its item(s)
119
119
  populate: [
120
120
  ...(opts.populate === false ? [] : opts.populate ?? []),
121
- ...(opts.ref ? [':ref'] : []),
122
121
  ...Array.from(filterMap.keys()).filter(
123
122
  // We need to do so only if the inverse side is a collection, because we can already retrieve the PK from a reference without having to load it
124
123
  prop => em.getMetadata(className).properties[prop]?.ref !== true),
@@ -149,15 +148,11 @@ export class DataloaderUtils {
149
148
  else if (target) {
150
149
  return target === collection.owner;
151
150
  }
152
- // FIXME https://github.com/mikro-orm/mikro-orm/issues/6031
153
- if (!target && collection.property.kind === ReferenceKind.MANY_TO_MANY) {
154
- throw new Error(`Inverse side is required for M:N relations with dataloader: ${collection.owner.constructor.name}.${collection.property.name}`);
155
- }
156
151
  return false;
157
152
  };
158
153
  }
159
154
  /**
160
- * Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
155
+ * Returns the 1:M collection dataloader batchLoadFn, which aggregates collections by entity,
161
156
  * makes one query per entity and maps each input collection to the corresponding result.
162
157
  */
163
158
  static getColBatchLoadFn(em) {
@@ -179,4 +174,40 @@ export class DataloaderUtils {
179
174
  });
180
175
  };
181
176
  }
177
+ /**
178
+ * Returns the M:N collection dataloader batchLoadFn, which aggregates collections by entity,
179
+ * makes one query per entity and maps each input collection to the corresponding result.
180
+ */
181
+ static getManyToManyColBatchLoadFn(em) {
182
+ return async (collsWithOpts) => {
183
+ const groups = new Map();
184
+ for (const [col, opts] of collsWithOpts) {
185
+ const key = `${col.property.targetMeta.className}.${col.property.name}|${JSON.stringify(opts ?? {})}`;
186
+ const value = groups.get(key) ?? [];
187
+ value.push([col, opts ?? {}]);
188
+ groups.set(key, value);
189
+ }
190
+ const ret = [];
191
+ for (const group of groups.values()) {
192
+ const prop = group[0][0].property;
193
+ const options = {};
194
+ const wrap = (cond) => ({ [prop.name]: cond });
195
+ const orderBy = Utils.asArray(group[0][1]?.orderBy).map(o => wrap(o));
196
+ const populate = wrap(group[0][1]?.populate);
197
+ const owners = group.map(c => c[0].owner);
198
+ const $or = [];
199
+ // a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
200
+ const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
201
+ const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
202
+ for (const c of group) {
203
+ $or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
204
+ options.refresh ??= c[1]?.refresh;
205
+ }
206
+ options.where = wrap({ $or });
207
+ const r = await em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, group[0][1]?.ref);
208
+ ret.push(...r);
209
+ }
210
+ return ret;
211
+ };
212
+ }
182
213
  }