@mikro-orm/core 7.0.0-dev.99 → 7.0.0-rc.1

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 (107) hide show
  1. package/EntityManager.d.ts +34 -17
  2. package/EntityManager.js +95 -103
  3. package/MikroORM.d.ts +5 -5
  4. package/MikroORM.js +25 -20
  5. package/cache/FileCacheAdapter.js +11 -3
  6. package/connections/Connection.d.ts +3 -2
  7. package/connections/Connection.js +4 -3
  8. package/drivers/DatabaseDriver.d.ts +11 -11
  9. package/drivers/DatabaseDriver.js +91 -25
  10. package/drivers/IDatabaseDriver.d.ts +50 -20
  11. package/entity/BaseEntity.d.ts +61 -1
  12. package/entity/Collection.d.ts +8 -1
  13. package/entity/Collection.js +12 -13
  14. package/entity/EntityAssigner.js +9 -9
  15. package/entity/EntityFactory.d.ts +6 -1
  16. package/entity/EntityFactory.js +40 -22
  17. package/entity/EntityHelper.d.ts +2 -2
  18. package/entity/EntityHelper.js +27 -4
  19. package/entity/EntityLoader.d.ts +5 -4
  20. package/entity/EntityLoader.js +193 -80
  21. package/entity/EntityRepository.d.ts +27 -7
  22. package/entity/EntityRepository.js +8 -2
  23. package/entity/PolymorphicRef.d.ts +12 -0
  24. package/entity/PolymorphicRef.js +18 -0
  25. package/entity/WrappedEntity.d.ts +2 -2
  26. package/entity/WrappedEntity.js +1 -1
  27. package/entity/defineEntity.d.ts +89 -50
  28. package/entity/defineEntity.js +12 -0
  29. package/entity/index.d.ts +1 -0
  30. package/entity/index.js +1 -0
  31. package/entity/utils.d.ts +6 -1
  32. package/entity/utils.js +33 -0
  33. package/entity/validators.js +2 -2
  34. package/enums.d.ts +2 -2
  35. package/enums.js +1 -0
  36. package/errors.d.ts +16 -8
  37. package/errors.js +40 -13
  38. package/hydration/ObjectHydrator.js +63 -21
  39. package/index.d.ts +1 -1
  40. package/logging/colors.d.ts +1 -1
  41. package/logging/colors.js +7 -6
  42. package/logging/inspect.js +1 -6
  43. package/metadata/EntitySchema.d.ts +43 -13
  44. package/metadata/EntitySchema.js +82 -27
  45. package/metadata/MetadataDiscovery.d.ts +60 -3
  46. package/metadata/MetadataDiscovery.js +665 -154
  47. package/metadata/MetadataProvider.js +3 -1
  48. package/metadata/MetadataStorage.d.ts +13 -6
  49. package/metadata/MetadataStorage.js +64 -19
  50. package/metadata/MetadataValidator.d.ts +32 -2
  51. package/metadata/MetadataValidator.js +196 -31
  52. package/metadata/discover-entities.js +5 -5
  53. package/metadata/types.d.ts +111 -14
  54. package/naming-strategy/AbstractNamingStrategy.d.ts +11 -3
  55. package/naming-strategy/AbstractNamingStrategy.js +12 -0
  56. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  57. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  58. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  59. package/naming-strategy/MongoNamingStrategy.js +6 -6
  60. package/naming-strategy/NamingStrategy.d.ts +17 -3
  61. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  62. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  63. package/package.json +2 -2
  64. package/platforms/Platform.d.ts +4 -2
  65. package/platforms/Platform.js +5 -2
  66. package/serialization/EntitySerializer.d.ts +3 -0
  67. package/serialization/EntitySerializer.js +15 -13
  68. package/serialization/EntityTransformer.js +6 -6
  69. package/serialization/SerializationContext.d.ts +6 -6
  70. package/typings.d.ts +325 -110
  71. package/typings.js +84 -17
  72. package/unit-of-work/ChangeSet.d.ts +4 -3
  73. package/unit-of-work/ChangeSet.js +2 -3
  74. package/unit-of-work/ChangeSetComputer.d.ts +3 -6
  75. package/unit-of-work/ChangeSetComputer.js +34 -13
  76. package/unit-of-work/ChangeSetPersister.d.ts +12 -10
  77. package/unit-of-work/ChangeSetPersister.js +55 -25
  78. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  79. package/unit-of-work/CommitOrderCalculator.js +13 -13
  80. package/unit-of-work/IdentityMap.d.ts +12 -0
  81. package/unit-of-work/IdentityMap.js +39 -1
  82. package/unit-of-work/UnitOfWork.d.ts +21 -3
  83. package/unit-of-work/UnitOfWork.js +203 -56
  84. package/utils/AbstractSchemaGenerator.js +17 -8
  85. package/utils/AsyncContext.d.ts +6 -0
  86. package/utils/AsyncContext.js +42 -0
  87. package/utils/Configuration.d.ts +52 -11
  88. package/utils/Configuration.js +12 -8
  89. package/utils/Cursor.js +21 -8
  90. package/utils/DataloaderUtils.js +13 -11
  91. package/utils/EntityComparator.d.ts +14 -7
  92. package/utils/EntityComparator.js +132 -46
  93. package/utils/QueryHelper.d.ts +16 -6
  94. package/utils/QueryHelper.js +53 -18
  95. package/utils/RawQueryFragment.d.ts +28 -23
  96. package/utils/RawQueryFragment.js +34 -56
  97. package/utils/RequestContext.js +2 -2
  98. package/utils/TransactionContext.js +2 -2
  99. package/utils/TransactionManager.js +1 -1
  100. package/utils/Utils.d.ts +7 -26
  101. package/utils/Utils.js +25 -79
  102. package/utils/clone.js +7 -21
  103. package/utils/env-vars.d.ts +4 -0
  104. package/utils/env-vars.js +13 -3
  105. package/utils/fs-utils.d.ts +21 -0
  106. package/utils/fs-utils.js +106 -11
  107. package/utils/upsert-utils.d.ts +4 -4
package/EntityManager.js CHANGED
@@ -4,7 +4,7 @@ import { Cursor } from './utils/Cursor.js';
4
4
  import { DataloaderUtils } from './utils/DataloaderUtils.js';
5
5
  import { QueryHelper } from './utils/QueryHelper.js';
6
6
  import { TransactionContext } from './utils/TransactionContext.js';
7
- import { isRaw, RawQueryFragment } from './utils/RawQueryFragment.js';
7
+ import { isRaw, Raw } from './utils/RawQueryFragment.js';
8
8
  import { EntityFactory } from './entity/EntityFactory.js';
9
9
  import { EntityAssigner } from './entity/EntityAssigner.js';
10
10
  import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
@@ -17,7 +17,7 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
17
17
  import { EventManager } from './events/EventManager.js';
18
18
  import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
19
19
  import { OptimisticLockError, ValidationError } from './errors.js';
20
- import { getLoadingStrategy } from './entity/utils.js';
20
+ import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
21
21
  import { TransactionManager } from './utils/TransactionManager.js';
22
22
  /**
23
23
  * The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
@@ -35,7 +35,7 @@ export class EntityManager {
35
35
  global = false;
36
36
  name;
37
37
  loaders = {};
38
- repositoryMap = {};
38
+ repositoryMap = new Map();
39
39
  entityLoader;
40
40
  comparator;
41
41
  entityFactory;
@@ -88,13 +88,12 @@ export class EntityManager {
88
88
  * Gets repository for given entity. You can pass either string name or entity class reference.
89
89
  */
90
90
  getRepository(entityName) {
91
- entityName = Utils.className(entityName);
92
- if (!this.repositoryMap[entityName]) {
93
- const meta = this.metadata.get(entityName);
91
+ const meta = this.metadata.get(entityName);
92
+ if (!this.repositoryMap.has(meta)) {
94
93
  const RepositoryClass = this.config.getRepositoryClass(meta.repository);
95
- this.repositoryMap[entityName] = new RepositoryClass(this, entityName);
94
+ this.repositoryMap.set(meta, new RepositoryClass(this, entityName));
96
95
  }
97
- return this.repositoryMap[entityName];
96
+ return this.repositoryMap.get(meta);
98
97
  }
99
98
  /**
100
99
  * Shortcut for `em.getRepository()`.
@@ -116,10 +115,15 @@ export class EntityManager {
116
115
  const em = this.getContext();
117
116
  em.prepareOptions(options);
118
117
  await em.tryFlush(entityName, options);
119
- entityName = Utils.className(entityName);
120
118
  where = await em.processWhere(entityName, where, options, 'read');
121
119
  validateParams(where);
122
- options.orderBy = options.orderBy || {};
120
+ const meta = this.metadata.get(entityName);
121
+ if (meta.orderBy) {
122
+ options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
123
+ }
124
+ else {
125
+ options.orderBy ??= {};
126
+ }
123
127
  options.populate = await em.preparePopulate(entityName, options);
124
128
  const populate = options.populate;
125
129
  const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
@@ -133,7 +137,6 @@ export class EntityManager {
133
137
  });
134
138
  return cached.data;
135
139
  }
136
- const meta = this.metadata.get(entityName);
137
140
  options = { ...options };
138
141
  // save the original hint value so we know it was infer/all
139
142
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
@@ -192,7 +195,6 @@ export class EntityManager {
192
195
  em.prepareOptions(options);
193
196
  options.strategy = 'joined';
194
197
  await em.tryFlush(entityName, options);
195
- entityName = Utils.className(entityName);
196
198
  const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
197
199
  validateParams(where);
198
200
  options.orderBy = options.orderBy || {};
@@ -300,18 +302,18 @@ export class EntityManager {
300
302
  // this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
301
303
  applyDiscriminatorCondition(entityName, where) {
302
304
  const meta = this.metadata.find(entityName);
303
- if (!meta?.discriminatorValue) {
305
+ if (meta?.root.inheritanceType !== 'sti' || !meta?.discriminatorValue) {
304
306
  return where;
305
307
  }
306
- const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
308
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
307
309
  const children = [];
308
310
  const lookUpChildren = (ret, type) => {
309
311
  const children = types.filter(meta2 => meta2.extends === type);
310
- children.forEach(m => lookUpChildren(ret, m.className));
312
+ children.forEach(m => lookUpChildren(ret, m.class));
311
313
  ret.push(...children.filter(c => c.discriminatorValue));
312
314
  return children;
313
315
  };
314
- lookUpChildren(children, meta.className);
316
+ lookUpChildren(children, meta.class);
315
317
  /* v8 ignore next */
316
318
  where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
317
319
  return where;
@@ -328,8 +330,10 @@ export class EntityManager {
328
330
  return ret;
329
331
  }
330
332
  async getJoinedFilters(meta, options) {
333
+ // If user provided populateFilter, merge it with computed filters
334
+ const userFilter = options.populateFilter;
331
335
  if (!this.config.get('filtersOnRelations') || !options.populate) {
332
- return undefined;
336
+ return userFilter;
333
337
  }
334
338
  const ret = {};
335
339
  for (const hint of options.populate) {
@@ -341,7 +345,7 @@ export class EntityManager {
341
345
  continue;
342
346
  }
343
347
  const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
344
- const where = await this.applyFilters(prop.type, {}, filters, 'read', {
348
+ const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
345
349
  ...options,
346
350
  populate: hint.children,
347
351
  });
@@ -363,7 +367,11 @@ export class EntityManager {
363
367
  }
364
368
  }
365
369
  }
366
- return ret;
370
+ // Merge user-provided populateFilter with computed filters
371
+ if (userFilter) {
372
+ Utils.merge(ret, userFilter);
373
+ }
374
+ return Utils.hasObjectKeys(ret) ? ret : undefined;
367
375
  }
368
376
  /**
369
377
  * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
@@ -377,11 +385,11 @@ export class EntityManager {
377
385
  if (prop.object
378
386
  || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
379
387
  || !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
380
- || (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
388
+ || (parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
381
389
  continue;
382
390
  }
383
391
  options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
384
- const cond = await this.applyFilters(prop.type, {}, options.filters, 'read', options);
392
+ const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
385
393
  if (!Utils.isEmpty(cond)) {
386
394
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
387
395
  let found = false;
@@ -404,7 +412,7 @@ export class EntityManager {
404
412
  const prop = meta?.properties[field];
405
413
  if (prop && !ref) {
406
414
  hint.children ??= [];
407
- await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
415
+ await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
408
416
  }
409
417
  }
410
418
  }
@@ -412,16 +420,13 @@ export class EntityManager {
412
420
  * @internal
413
421
  */
414
422
  async applyFilters(entityName, where, options, type, findOptions) {
415
- const meta = this.metadata.find(entityName);
423
+ const meta = this.metadata.get(entityName);
416
424
  const filters = [];
417
425
  const ret = [];
418
- if (!meta) {
419
- return where;
420
- }
421
426
  const active = new Set();
422
427
  const push = (source) => {
423
428
  const activeFilters = QueryHelper
424
- .getActiveFilters(entityName, options, source)
429
+ .getActiveFilters(meta, options, source)
425
430
  .filter(f => !active.has(f.name));
426
431
  filters.push(...activeFilters);
427
432
  activeFilters.forEach(f => active.add(f.name));
@@ -440,7 +445,7 @@ export class EntityManager {
440
445
  if (!args && filter.cond.length > 0 && filter.args !== false) {
441
446
  throw new Error(`No arguments provided for filter '${filter.name}'`);
442
447
  }
443
- cond = await filter.cond(args, type, this, findOptions, entityName);
448
+ cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
444
449
  }
445
450
  else {
446
451
  cond = filter.cond;
@@ -457,7 +462,7 @@ export class EntityManager {
457
462
  }
458
463
  ret.push(cond);
459
464
  }
460
- const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
465
+ const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
461
466
  return conds.length > 1 ? { $and: conds } : conds[0];
462
467
  }
463
468
  /**
@@ -468,12 +473,10 @@ export class EntityManager {
468
473
  const em = this.getContext(false);
469
474
  await em.tryFlush(entityName, options);
470
475
  options.flushMode = 'commit'; // do not try to auto flush again
471
- return RawQueryFragment.run(async () => {
472
- return Promise.all([
473
- em.find(entityName, where, options),
474
- em.count(entityName, where, options),
475
- ]);
476
- });
476
+ return Promise.all([
477
+ em.find(entityName, where, options),
478
+ em.count(entityName, where, options),
479
+ ]);
477
480
  }
478
481
  /**
479
482
  * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
@@ -489,21 +492,21 @@ export class EntityManager {
489
492
  * - POJO/entity instance
490
493
  *
491
494
  * ```ts
492
- * const currentCursor = await em.findByCursor(User, {}, {
495
+ * const currentCursor = await em.findByCursor(User, {
493
496
  * first: 10,
494
497
  * after: previousCursor, // cursor instance
495
498
  * orderBy: { id: 'desc' },
496
499
  * });
497
500
  *
498
501
  * // to fetch next page
499
- * const nextCursor = await em.findByCursor(User, {}, {
502
+ * const nextCursor = await em.findByCursor(User, {
500
503
  * first: 10,
501
504
  * after: currentCursor.endCursor, // opaque string
502
505
  * orderBy: { id: 'desc' },
503
506
  * });
504
507
  *
505
508
  * // to fetch next page
506
- * const nextCursor2 = await em.findByCursor(User, {}, {
509
+ * const nextCursor2 = await em.findByCursor(User, {
507
510
  * first: 10,
508
511
  * after: { id: lastSeenId }, // entity-like POJO
509
512
  * orderBy: { id: 'desc' },
@@ -531,16 +534,16 @@ export class EntityManager {
531
534
  * }
532
535
  * ```
533
536
  */
534
- async findByCursor(entityName, where, options) {
537
+ async findByCursor(entityName, options) {
535
538
  const em = this.getContext(false);
536
- entityName = Utils.className(entityName);
537
539
  options.overfetch ??= true;
538
- if (Utils.isEmpty(options.orderBy)) {
540
+ options.where ??= {};
541
+ if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
539
542
  throw new Error('Explicit `orderBy` option required');
540
543
  }
541
544
  const [entities, count] = options.includeCount !== false
542
- ? await em.findAndCount(entityName, where, options)
543
- : [await em.find(entityName, where, options)];
545
+ ? await em.findAndCount(entityName, options.where, options)
546
+ : [await em.find(entityName, options.where, options)];
544
547
  return new Cursor(entities, count, options, this.metadata.get(entityName));
545
548
  }
546
549
  /**
@@ -552,9 +555,9 @@ export class EntityManager {
552
555
  const ret = await this.refresh(entity, options);
553
556
  if (!ret) {
554
557
  options.failHandler ??= this.config.get('findOneOrFailHandler');
555
- const entityName = entity.constructor.name;
556
- const where = helper(entity).getPrimaryKey();
557
- throw options.failHandler(entityName, where);
558
+ const wrapped = helper(entity);
559
+ const where = wrapped.getPrimaryKey();
560
+ throw options.failHandler(wrapped.__meta.className, where);
558
561
  }
559
562
  return ret;
560
563
  }
@@ -565,9 +568,8 @@ export class EntityManager {
565
568
  */
566
569
  async refresh(entity, options = {}) {
567
570
  const fork = this.fork({ keepTransactionContext: true });
568
- const entityName = entity.constructor.name;
569
571
  const wrapped = helper(entity);
570
- const reloaded = await fork.findOne(entityName, entity, {
572
+ const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
571
573
  schema: wrapped.__schema,
572
574
  ...options,
573
575
  flushMode: FlushMode.COMMIT,
@@ -579,14 +581,14 @@ export class EntityManager {
579
581
  }
580
582
  let found = false;
581
583
  for (const e of fork.unitOfWork.getIdentityMap()) {
582
- const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
583
- const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
584
- em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
584
+ const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
585
+ const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
586
+ em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, false);
585
587
  Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
586
588
  found ||= ref === entity;
587
589
  }
588
590
  if (!found) {
589
- const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true });
591
+ const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: true });
590
592
  em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
591
593
  Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
592
594
  }
@@ -604,7 +606,6 @@ export class EntityManager {
604
606
  return ret;
605
607
  }
606
608
  const em = this.getContext();
607
- entityName = Utils.className(entityName);
608
609
  em.prepareOptions(options);
609
610
  let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
610
611
  // query for a not managed entity which is already in the identity map as it
@@ -682,10 +683,10 @@ export class EntityManager {
682
683
  if (!entity || isStrictViolation) {
683
684
  const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
684
685
  options.failHandler ??= this.config.get(key);
685
- entityName = Utils.className(entityName);
686
+ const name = Utils.className(entityName);
686
687
  /* v8 ignore next */
687
688
  where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
688
- throw options.failHandler(entityName, where);
689
+ throw options.failHandler(name, where);
689
690
  }
690
691
  return entity;
691
692
  }
@@ -725,11 +726,11 @@ export class EntityManager {
725
726
  let where;
726
727
  let entity = null;
727
728
  if (data === undefined) {
728
- entityName = entityNameOrEntity.constructor.name;
729
+ entityName = entityNameOrEntity.constructor;
729
730
  data = entityNameOrEntity;
730
731
  }
731
732
  else {
732
- entityName = Utils.className(entityNameOrEntity);
733
+ entityName = entityNameOrEntity;
733
734
  }
734
735
  const meta = this.metadata.get(entityName);
735
736
  const convertCustomTypes = !Utils.isEntity(data);
@@ -792,7 +793,7 @@ export class EntityManager {
792
793
  where[meta.primaryKeys[0]] = ret.insertId;
793
794
  }
794
795
  }
795
- const data2 = await this.driver.findOne(meta.className, where, {
796
+ const data2 = await this.driver.findOne(meta.class, where, {
796
797
  fields: returning,
797
798
  ctx: em.transactionContext,
798
799
  convertCustomTypes: true,
@@ -847,11 +848,11 @@ export class EntityManager {
847
848
  let entityName;
848
849
  let propIndex;
849
850
  if (data === undefined) {
850
- entityName = entityNameOrEntity[0].constructor.name;
851
+ entityName = entityNameOrEntity[0].constructor;
851
852
  data = entityNameOrEntity;
852
853
  }
853
854
  else {
854
- entityName = Utils.className(entityNameOrEntity);
855
+ entityName = entityNameOrEntity;
855
856
  }
856
857
  const batchSize = options.batchSize ?? this.config.get('batchSize');
857
858
  if (data.length > batchSize) {
@@ -959,7 +960,7 @@ export class EntityManager {
959
960
  where.$or[idx][prop] = item[prop];
960
961
  });
961
962
  });
962
- const data2 = await this.driver.find(meta.className, where, {
963
+ const data2 = await this.driver.find(meta.class, where, {
963
964
  fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
964
965
  ctx: em.transactionContext,
965
966
  convertCustomTypes: true,
@@ -1112,11 +1113,11 @@ export class EntityManager {
1112
1113
  em.prepareOptions(options);
1113
1114
  let entityName;
1114
1115
  if (data === undefined) {
1115
- entityName = entityNameOrEntity.constructor.name;
1116
+ entityName = entityNameOrEntity.constructor;
1116
1117
  data = entityNameOrEntity;
1117
1118
  }
1118
1119
  else {
1119
- entityName = Utils.className(entityNameOrEntity);
1120
+ entityName = entityNameOrEntity;
1120
1121
  }
1121
1122
  if (Utils.isEntity(data)) {
1122
1123
  if (options.schema && helper(data).getSchema() == null) {
@@ -1147,11 +1148,11 @@ export class EntityManager {
1147
1148
  em.prepareOptions(options);
1148
1149
  let entityName;
1149
1150
  if (data === undefined) {
1150
- entityName = entityNameOrEntities[0].constructor.name;
1151
+ entityName = entityNameOrEntities[0].constructor;
1151
1152
  data = entityNameOrEntities;
1152
1153
  }
1153
1154
  else {
1154
- entityName = Utils.className(entityNameOrEntities);
1155
+ entityName = entityNameOrEntities;
1155
1156
  }
1156
1157
  if (data.length === 0) {
1157
1158
  return [];
@@ -1188,7 +1189,6 @@ export class EntityManager {
1188
1189
  async nativeUpdate(entityName, where, data, options = {}) {
1189
1190
  const em = this.getContext(false);
1190
1191
  em.prepareOptions(options);
1191
- entityName = Utils.className(entityName);
1192
1192
  data = QueryHelper.processObjectParams(data);
1193
1193
  where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
1194
1194
  validateParams(data, 'update data');
@@ -1202,7 +1202,6 @@ export class EntityManager {
1202
1202
  async nativeDelete(entityName, where, options = {}) {
1203
1203
  const em = this.getContext(false);
1204
1204
  em.prepareOptions(options);
1205
- entityName = Utils.className(entityName);
1206
1205
  where = await em.processWhere(entityName, where, options, 'delete');
1207
1206
  validateParams(where, 'delete condition');
1208
1207
  const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
@@ -1212,7 +1211,6 @@ export class EntityManager {
1212
1211
  * Maps raw database result to an entity and merges it to this EntityManager.
1213
1212
  */
1214
1213
  map(entityName, result, options = {}) {
1215
- entityName = Utils.className(entityName);
1216
1214
  const meta = this.metadata.get(entityName);
1217
1215
  const data = this.driver.mapResult(result, meta);
1218
1216
  for (const k of Object.keys(data)) {
@@ -1234,13 +1232,12 @@ export class EntityManager {
1234
1232
  */
1235
1233
  merge(entityName, data, options = {}) {
1236
1234
  if (Utils.isEntity(entityName)) {
1237
- return this.merge(entityName.constructor.name, entityName, data);
1235
+ return this.merge(entityName.constructor, entityName, data);
1238
1236
  }
1239
1237
  const em = options.disableContextResolution ? this : this.getContext();
1240
1238
  options.schema ??= em._schema;
1241
1239
  options.validate ??= true;
1242
1240
  options.cascade ??= true;
1243
- entityName = Utils.className(entityName);
1244
1241
  validatePrimaryKey(data, em.metadata.get(entityName));
1245
1242
  let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1246
1243
  if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
@@ -1294,7 +1291,7 @@ export class EntityManager {
1294
1291
  getReference(entityName, id, options = {}) {
1295
1292
  options.schema ??= this.schema;
1296
1293
  options.convertCustomTypes ??= false;
1297
- const meta = this.metadata.get(Utils.className(entityName));
1294
+ const meta = this.metadata.get(entityName);
1298
1295
  if (Utils.isPrimaryKey(id)) {
1299
1296
  if (meta.compositePK) {
1300
1297
  throw ValidationError.invalidCompositeIdentifier(meta);
@@ -1315,7 +1312,6 @@ export class EntityManager {
1315
1312
  // Shallow copy options since the object will be modified when deleting orderBy
1316
1313
  options = { ...options };
1317
1314
  em.prepareOptions(options);
1318
- entityName = Utils.className(entityName);
1319
1315
  await em.tryFlush(entityName, options);
1320
1316
  where = await em.processWhere(entityName, where, options, 'read');
1321
1317
  options.populate = await em.preparePopulate(entityName, options);
@@ -1351,7 +1347,7 @@ export class EntityManager {
1351
1347
  for (const ent of entities) {
1352
1348
  if (!Utils.isEntity(ent, true)) {
1353
1349
  /* v8 ignore next */
1354
- const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor.name) : undefined;
1350
+ const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
1355
1351
  throw ValidationError.notDiscoveredEntity(ent, meta);
1356
1352
  }
1357
1353
  // do not cascade just yet, cascading of entities in persist stack is done when flushing
@@ -1395,7 +1391,6 @@ export class EntityManager {
1395
1391
  async tryFlush(entityName, options) {
1396
1392
  const em = this.getContext();
1397
1393
  const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
1398
- entityName = Utils.className(entityName);
1399
1394
  const meta = em.metadata.get(entityName);
1400
1395
  if (flushMode === FlushMode.COMMIT) {
1401
1396
  return;
@@ -1414,7 +1409,6 @@ export class EntityManager {
1414
1409
  * Checks whether given property can be populated on the entity.
1415
1410
  */
1416
1411
  canPopulate(entityName, property) {
1417
- entityName = Utils.className(entityName);
1418
1412
  // eslint-disable-next-line prefer-const
1419
1413
  let [p, ...parts] = property.split('.');
1420
1414
  const meta = this.metadata.find(entityName);
@@ -1424,12 +1418,11 @@ export class EntityManager {
1424
1418
  if (p.includes(':')) {
1425
1419
  p = p.split(':', 2)[0];
1426
1420
  }
1427
- const ret = p in meta.root.properties;
1428
- if (!ret) {
1429
- return !!this.metadata.find(property)?.pivotTable;
1430
- }
1421
+ // For TPT inheritance, check the entity's own properties, not just the root's
1422
+ // For STI, meta.properties includes all properties anyway
1423
+ const ret = p in meta.properties;
1431
1424
  if (parts.length > 0) {
1432
- return this.canPopulate((meta.root.properties)[p].type, parts.join('.'));
1425
+ return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
1433
1426
  }
1434
1427
  return ret;
1435
1428
  }
@@ -1443,7 +1436,7 @@ export class EntityManager {
1443
1436
  }
1444
1437
  const em = this.getContext();
1445
1438
  em.prepareOptions(options);
1446
- const entityName = arr[0].constructor.name;
1439
+ const entityName = arr[0].constructor;
1447
1440
  const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
1448
1441
  await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1449
1442
  return entities;
@@ -1572,7 +1565,6 @@ export class EntityManager {
1572
1565
  */
1573
1566
  getMetadata(entityName) {
1574
1567
  if (entityName) {
1575
- entityName = Utils.className(entityName);
1576
1568
  return this.metadata.get(entityName);
1577
1569
  }
1578
1570
  return this.metadata;
@@ -1601,8 +1593,8 @@ export class EntityManager {
1601
1593
  lockTableAliases: options.lockTableAliases,
1602
1594
  });
1603
1595
  }
1604
- const preparedPopulate = await this.preparePopulate(meta.className, options);
1605
- await this.entityLoader.populate(meta.className, [entity], preparedPopulate, {
1596
+ const preparedPopulate = await this.preparePopulate(meta.class, options);
1597
+ await this.entityLoader.populate(meta.class, [entity], preparedPopulate, {
1606
1598
  ...options,
1607
1599
  ...this.getPopulateWhere(where, options),
1608
1600
  orderBy: options.populateOrderBy ?? options.orderBy,
@@ -1622,6 +1614,7 @@ export class EntityManager {
1622
1614
  return ret;
1623
1615
  }, []);
1624
1616
  }
1617
+ /** @internal */
1625
1618
  async preparePopulate(entityName, options, validate = true) {
1626
1619
  if (options.populate === false) {
1627
1620
  return [];
@@ -1662,7 +1655,7 @@ export class EntityManager {
1662
1655
  options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
1663
1656
  }
1664
1657
  if (!options.populate) {
1665
- const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
1658
+ const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
1666
1659
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1667
1660
  return populate;
1668
1661
  }
@@ -1684,18 +1677,21 @@ export class EntityManager {
1684
1677
  return [field];
1685
1678
  }).flat();
1686
1679
  }
1687
- const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
1680
+ const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
1688
1681
  const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
1689
1682
  if (validate && invalid) {
1690
1683
  throw ValidationError.invalidPropertyName(entityName, invalid.field);
1691
1684
  }
1692
1685
  await this.autoJoinRefsForFilters(meta, { ...options, populate });
1693
- return populate.map(field => {
1686
+ for (const field of populate) {
1694
1687
  // force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
1695
1688
  const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
1696
1689
  field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
1697
- return field;
1698
- });
1690
+ }
1691
+ if (options.populateHints) {
1692
+ applyPopulateHints(populate, options.populateHints);
1693
+ }
1694
+ return populate;
1699
1695
  }
1700
1696
  /**
1701
1697
  * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
@@ -1715,7 +1711,7 @@ export class EntityManager {
1715
1711
  return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
1716
1712
  });
1717
1713
  }
1718
- if (autoRefresh) {
1714
+ if (autoRefresh || options.filters) {
1719
1715
  return true;
1720
1716
  }
1721
1717
  if (Array.isArray(options.populate)) {
@@ -1739,7 +1735,7 @@ export class EntityManager {
1739
1735
  for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
1740
1736
  delete opts[k];
1741
1737
  }
1742
- return [entityName, method, opts, where];
1738
+ return [Utils.className(entityName), method, opts, where];
1743
1739
  }
1744
1740
  /**
1745
1741
  * @internal
@@ -1756,21 +1752,17 @@ export class EntityManager {
1756
1752
  return { key: cacheKey, data: cached };
1757
1753
  }
1758
1754
  let data;
1755
+ const createOptions = {
1756
+ merge: true,
1757
+ convertCustomTypes: false,
1758
+ refresh,
1759
+ recomputeSnapshot: true,
1760
+ };
1759
1761
  if (Array.isArray(cached) && merge) {
1760
- data = cached.map(item => em.entityFactory.create(entityName, item, {
1761
- merge: true,
1762
- convertCustomTypes: true,
1763
- refresh,
1764
- recomputeSnapshot: true,
1765
- }));
1762
+ data = cached.map(item => em.entityFactory.create(entityName, item, createOptions));
1766
1763
  }
1767
1764
  else if (Utils.isObject(cached) && merge) {
1768
- data = em.entityFactory.create(entityName, cached, {
1769
- merge: true,
1770
- convertCustomTypes: true,
1771
- refresh,
1772
- recomputeSnapshot: true,
1773
- });
1765
+ data = em.entityFactory.create(entityName, cached, createOptions);
1774
1766
  }
1775
1767
  else {
1776
1768
  data = cached;
package/MikroORM.d.ts CHANGED
@@ -5,7 +5,7 @@ import { Configuration, type Options } from './utils/Configuration.js';
5
5
  import type { EntityManager } from './EntityManager.js';
6
6
  import type { AnyEntity, Constructor, EntityClass, EntityMetadata, EntityName, IEntityGenerator, IMigrator, ISeedManager } from './typings.js';
7
7
  /** @internal */
8
- export declare function lookupExtensions(options: Options): Promise<void>;
8
+ export declare function loadOptionalDependencies(options: Options): Promise<void>;
9
9
  /**
10
10
  * The main class used to configure and bootstrap the ORM.
11
11
  *
@@ -51,9 +51,9 @@ export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver,
51
51
  static init<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager<D> = D[typeof EntityManagerType] & EntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Options<D, EM, Entities>): Promise<MikroORM<D, EM, Entities>>;
52
52
  /**
53
53
  * Synchronous variant of the `init` method with some limitations:
54
- * - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly)
55
- * - no loading of the `config` file, `options` parameter is mandatory
56
- * - no support for folder based discovery
54
+ * - folder-based discovery not supported
55
+ * - ORM extensions are not autoloaded
56
+ * - when metadata cache is enabled, `FileCacheAdapter` needs to be explicitly set in the config
57
57
  */
58
58
  constructor(options: Options<Driver, EM, Entities>);
59
59
  /**
@@ -94,7 +94,7 @@ export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver,
94
94
  /**
95
95
  * Allows dynamically discovering new entity by reference, handy for testing schema diffing.
96
96
  */
97
- discoverEntity<T extends Constructor | EntitySchema>(entities: T | T[], reset?: string | string[]): void;
97
+ discoverEntity<T extends Constructor | EntitySchema>(entities: T | T[], reset?: EntityName | EntityName[]): void;
98
98
  /**
99
99
  * Gets the SchemaGenerator.
100
100
  */