@mikro-orm/core 7.0.0-dev.14 → 7.0.0-dev.15

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.
@@ -6,7 +6,7 @@ import { EntityFactory } from './entity/EntityFactory.js';
6
6
  import { type AssignOptions } from './entity/EntityAssigner.js';
7
7
  import { EntityValidator } from './entity/EntityValidator.js';
8
8
  import { type EntityRepository } from './entity/EntityRepository.js';
9
- import { type EntityLoaderOptions } from './entity/EntityLoader.js';
9
+ import { EntityLoader, type EntityLoaderOptions } from './entity/EntityLoader.js';
10
10
  import { Reference } from './entity/Reference.js';
11
11
  import { UnitOfWork } from './unit-of-work/UnitOfWork.js';
12
12
  import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers/IDatabaseDriver.js';
@@ -436,6 +436,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
436
436
  * Gets the EntityFactory used by the EntityManager.
437
437
  */
438
438
  getEntityFactory(): EntityFactory;
439
+ /**
440
+ * @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
441
+ */
442
+ getEntityLoader(): EntityLoader;
439
443
  /**
440
444
  * Gets the Hydrator used by the EntityManager.
441
445
  */
@@ -492,7 +496,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
492
496
  * @internal
493
497
  */
494
498
  tryCache<T extends object, R>(entityName: string, config: boolean | number | [string, number] | undefined, key: unknown, refresh?: boolean, merge?: boolean): Promise<{
495
- data?: R;
499
+ data?: R | null;
496
500
  key: string;
497
501
  } | undefined>;
498
502
  /**
package/EntityManager.js CHANGED
@@ -536,14 +536,16 @@ export class EntityManager {
536
536
  options.populate = await em.preparePopulate(entityName, options);
537
537
  const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
538
538
  const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
539
- if (cached?.data) {
540
- await em.entityLoader.populate(entityName, [cached.data], options.populate, {
541
- ...options,
542
- ...em.getPopulateWhere(where, options),
543
- convertCustomTypes: false,
544
- ignoreLazyScalarProperties: true,
545
- lookup: false,
546
- });
539
+ if (cached?.data !== undefined) {
540
+ if (cached.data) {
541
+ await em.entityLoader.populate(entityName, [cached.data], options.populate, {
542
+ ...options,
543
+ ...em.getPopulateWhere(where, options),
544
+ convertCustomTypes: false,
545
+ ignoreLazyScalarProperties: true,
546
+ lookup: false,
547
+ });
548
+ }
547
549
  return cached.data;
548
550
  }
549
551
  options = { ...options };
@@ -723,6 +725,7 @@ export class EntityManager {
723
725
  ctx: em.transactionContext,
724
726
  convertCustomTypes: true,
725
727
  connectionType: 'write',
728
+ schema: options.schema,
726
729
  });
727
730
  em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full', false, true);
728
731
  }
@@ -903,6 +906,7 @@ export class EntityManager {
903
906
  ctx: em.transactionContext,
904
907
  convertCustomTypes: true,
905
908
  connectionType: 'write',
909
+ schema: options.schema,
906
910
  });
907
911
  for (const [entity, cond] of loadPK.entries()) {
908
912
  const row = data2.find(row => {
@@ -1273,7 +1277,7 @@ export class EntityManager {
1273
1277
  delete options.orderBy;
1274
1278
  const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
1275
1279
  const cached = await em.tryCache(entityName, options.cache, cacheKey);
1276
- if (cached?.data) {
1280
+ if (cached?.data !== undefined) {
1277
1281
  return cached.data;
1278
1282
  }
1279
1283
  const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
@@ -1462,6 +1466,12 @@ export class EntityManager {
1462
1466
  getEntityFactory() {
1463
1467
  return this.getContext().entityFactory;
1464
1468
  }
1469
+ /**
1470
+ * @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
1471
+ */
1472
+ getEntityLoader() {
1473
+ return this.getContext().entityLoader;
1474
+ }
1465
1475
  /**
1466
1476
  * Gets the Hydrator used by the EntityManager.
1467
1477
  */
@@ -1705,31 +1715,31 @@ export class EntityManager {
1705
1715
  const em = this.getContext();
1706
1716
  const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
1707
1717
  const cached = await em.resultCache.get(cacheKey);
1708
- if (cached) {
1709
- let data;
1710
- if (Array.isArray(cached) && merge) {
1711
- data = cached.map(item => em.entityFactory.create(entityName, item, {
1712
- merge: true,
1713
- convertCustomTypes: true,
1714
- refresh,
1715
- recomputeSnapshot: true,
1716
- }));
1717
- }
1718
- else if (Utils.isObject(cached) && merge) {
1719
- data = em.entityFactory.create(entityName, cached, {
1720
- merge: true,
1721
- convertCustomTypes: true,
1722
- refresh,
1723
- recomputeSnapshot: true,
1724
- });
1725
- }
1726
- else {
1727
- data = cached;
1728
- }
1729
- await em.unitOfWork.dispatchOnLoadEvent();
1730
- return { key: cacheKey, data };
1718
+ if (!cached) {
1719
+ return { key: cacheKey, data: cached };
1720
+ }
1721
+ let data;
1722
+ if (Array.isArray(cached) && merge) {
1723
+ data = cached.map(item => em.entityFactory.create(entityName, item, {
1724
+ merge: true,
1725
+ convertCustomTypes: true,
1726
+ refresh,
1727
+ recomputeSnapshot: true,
1728
+ }));
1731
1729
  }
1732
- return { key: cacheKey };
1730
+ else if (Utils.isObject(cached) && merge) {
1731
+ data = em.entityFactory.create(entityName, cached, {
1732
+ merge: true,
1733
+ convertCustomTypes: true,
1734
+ refresh,
1735
+ recomputeSnapshot: true,
1736
+ });
1737
+ }
1738
+ else {
1739
+ data = cached;
1740
+ }
1741
+ await em.unitOfWork.dispatchOnLoadEvent();
1742
+ return { key: cacheKey, data };
1733
1743
  }
1734
1744
  /**
1735
1745
  * @internal
@@ -1,18 +1,12 @@
1
- import type { AnyEntity } from '../typings.js';
2
- export declare function Embedded<T extends object>(type?: EmbeddedOptions | (() => AnyEntity), options?: EmbeddedOptions): (target: AnyEntity, propertyName: string) => any;
1
+ import type { AnyEntity, EntityName } from '../typings.js';
2
+ import type { PropertyOptions } from './Property.js';
3
+ export declare function Embedded<Owner extends object, Target>(type?: EmbeddedOptions<Owner, Target> | (() => EntityName<Target> | EntityName<Target>[]), options?: EmbeddedOptions<Owner, Target>): (target: AnyEntity, propertyName: string) => any;
3
4
  /** With `absolute` the prefix is set at the root of the entity (regardless of the nesting level) */
4
5
  export type EmbeddedPrefixMode = 'absolute' | 'relative';
5
- export interface EmbeddedOptions {
6
- entity?: string | (() => AnyEntity | AnyEntity[]);
7
- type?: string;
6
+ export interface EmbeddedOptions<Owner, Target> extends PropertyOptions<Owner> {
7
+ entity?: string | (() => EntityName<Target> | EntityName<Target>[]);
8
8
  prefix?: string | boolean;
9
9
  prefixMode?: EmbeddedPrefixMode;
10
- nullable?: boolean;
11
10
  object?: boolean;
12
11
  array?: boolean;
13
- hidden?: boolean;
14
- serializer?: (value: any) => any;
15
- serializedName?: string;
16
- groups?: string[];
17
- persist?: boolean;
18
12
  }
@@ -27,4 +27,6 @@ export interface ManyToOneOptions<Owner, Target> extends ReferenceOptions<Owner,
27
27
  updateRule?: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString;
28
28
  /** Set the constraint type. Immediate constraints are checked for each statement, while deferred ones are only checked at the end of the transaction. Only for postgres unique constraints. */
29
29
  deferMode?: DeferMode | `${DeferMode}`;
30
+ /** Set a custom foreign key constraint name, overriding NamingStrategy.indexName(). */
31
+ foreignKeyName?: string;
30
32
  }
@@ -21,4 +21,6 @@ export interface OneToOneOptions<Owner, Target> extends Partial<Omit<OneToManyOp
21
21
  updateRule?: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString;
22
22
  /** Set the constraint type. Immediate constraints are checked for each statement, while deferred ones are only checked at the end of the transaction. Only for postgres unique constraints. */
23
23
  deferMode?: DeferMode | `${DeferMode}`;
24
+ /** Set a custom foreign key constraint name, overriding NamingStrategy.indexName(). */
25
+ foreignKeyName?: string;
24
26
  }
@@ -2,7 +2,7 @@ import type { EntityDTO, EntityKey, FilterQuery, Loaded, LoadedCollection, Popul
2
2
  import { ArrayCollection } from './ArrayCollection.js';
3
3
  import { Reference } from './Reference.js';
4
4
  import type { Transaction } from '../connections/Connection.js';
5
- import type { FindOptions, CountOptions } from '../drivers/IDatabaseDriver.js';
5
+ import type { CountOptions, FindOptions } from '../drivers/IDatabaseDriver.js';
6
6
  import type { EntityLoaderOptions } from './EntityLoader.js';
7
7
  export interface MatchingOptions<T extends object, P extends string = never> extends FindOptions<T, P> {
8
8
  where?: FilterQuery<T>;
@@ -226,8 +226,13 @@ export class Collection extends ArrayCollection {
226
226
  this,
227
227
  { ...options, orderBy },
228
228
  ]);
229
- if (!customOrder) {
230
- this.reorderItems(items, order);
229
+ if (this.property.kind === ReferenceKind.MANY_TO_MANY) {
230
+ this.initialized = true;
231
+ this.dirty = false;
232
+ if (!customOrder) {
233
+ this.reorderItems(items, order);
234
+ }
235
+ return this;
231
236
  }
232
237
  this.items.clear();
233
238
  let i = 0;
@@ -1,4 +1,4 @@
1
- import type { ConnectionType, Dictionary, FilterQuery, PopulateOptions } from '../typings.js';
1
+ import type { AnyEntity, ConnectionType, Dictionary, EntityProperty, FilterQuery, PopulateOptions } from '../typings.js';
2
2
  import type { EntityManager } from '../EntityManager.js';
3
3
  import { LoadStrategy, type LockMode, type PopulateHint, PopulatePath, type QueryOrderMap } from '../enums.js';
4
4
  import type { EntityField } from '../drivers/IDatabaseDriver.js';
@@ -49,7 +49,8 @@ export declare class EntityLoader {
49
49
  private findChildren;
50
50
  private mergePrimaryCondition;
51
51
  private populateField;
52
- private findChildrenFromPivotTable;
52
+ /** @internal */
53
+ findChildrenFromPivotTable<Entity extends object>(filtered: Entity[], prop: EntityProperty<Entity>, options: Required<EntityLoaderOptions<Entity>>, orderBy?: QueryOrderMap<Entity>[], populate?: PopulateOptions<Entity>, pivotJoin?: boolean): Promise<AnyEntity[][]>;
53
54
  private extractChildCondition;
54
55
  private buildFields;
55
56
  private getChildReferences;
@@ -141,7 +141,8 @@ export class EntityLoader {
141
141
  .filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) || Utils.isObject(orderBy[prop.name]))
142
142
  .flatMap(orderBy => orderBy[prop.name]);
143
143
  if (prop.kind === ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
144
- return this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
144
+ const res = await this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
145
+ return Utils.flatten(res);
145
146
  }
146
147
  const where = await this.extractChildCondition(options, prop);
147
148
  const data = await this.findChildren(entities, prop, populate, { ...options, where, orderBy: innerOrderBy }, !!(ref || prop.mapToPk));
@@ -343,6 +344,7 @@ export class EntityLoader {
343
344
  visited: options.visited,
344
345
  });
345
346
  }
347
+ /** @internal */
346
348
  async findChildrenFromPivotTable(filtered, prop, options, orderBy, populate, pivotJoin) {
347
349
  const ids = filtered.map(e => e.__helper.__primaryKeys);
348
350
  const refresh = options.refresh;
@@ -380,7 +382,7 @@ export class EntityLoader {
380
382
  return this.em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
381
383
  });
382
384
  entity[prop.name].hydrate(items, true);
383
- children.push(...items);
385
+ children.push(items);
384
386
  }
385
387
  return children;
386
388
  }
package/enums.d.ts CHANGED
@@ -66,7 +66,7 @@ export declare enum QueryOrderNumeric {
66
66
  ASC = 1,
67
67
  DESC = -1
68
68
  }
69
- export type QueryOrderKeysFlat = QueryOrder | QueryOrderNumeric | keyof typeof QueryOrder;
69
+ export type QueryOrderKeysFlat = QueryOrder | QueryOrderNumeric | `${QueryOrder}`;
70
70
  export type QueryOrderKeys<T> = QueryOrderKeysFlat | QueryOrderMap<T>;
71
71
  export type QueryOrderMap<T> = {
72
72
  [K in EntityKey<T>]?: QueryOrderKeys<ExpandProperty<T[K]>>;
package/exports.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * @module core
4
+ */
5
+ export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings.js';
6
+ export * from './enums.js';
7
+ export * from './errors.js';
8
+ export * from './exceptions.js';
9
+ export * from './entity/index.js';
10
+ export * from './serialization/index.js';
11
+ export * from './events/index.js';
12
+ export { CreateOptions, MergeOptions, ForkOptions } from './EntityManager.js';
13
+ export * from './unit-of-work/index.js';
14
+ export * from './utils/index.js';
15
+ export * from './logging/index.js';
16
+ export * from './hydration/index.js';
17
+ export * from './drivers/index.js';
18
+ export * from './connections/index.js';
19
+ export * from './platforms/index.js';
20
+ export * from './types/index.js';
21
+ export * from './naming-strategy/index.js';
22
+ export * from './metadata/index.js';
23
+ export * from './cache/index.js';
24
+ export * from './decorators/index.js';
package/exports.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * @module core
4
+ */
5
+ export { PrimaryKeyProp, EntityMetadata, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, } from './typings.js';
6
+ export * from './enums.js';
7
+ export * from './errors.js';
8
+ export * from './exceptions.js';
9
+ export * from './entity/index.js';
10
+ export * from './serialization/index.js';
11
+ export * from './events/index.js';
12
+ export * from './unit-of-work/index.js';
13
+ export * from './utils/index.js';
14
+ export * from './logging/index.js';
15
+ export * from './hydration/index.js';
16
+ export * from './drivers/index.js';
17
+ export * from './connections/index.js';
18
+ export * from './platforms/index.js';
19
+ export * from './types/index.js';
20
+ export * from './naming-strategy/index.js';
21
+ export * from './metadata/index.js';
22
+ export * from './cache/index.js';
23
+ export * from './decorators/index.js';
@@ -31,7 +31,7 @@ export type EntitySchemaProperty<Target, Owner> = ({
31
31
  kind: ReferenceKind.MANY_TO_MANY | 'm:n';
32
32
  } & TypeDef<Target> & ManyToManyOptions<Owner, Target>) | ({
33
33
  kind: ReferenceKind.EMBEDDED | 'embedded';
34
- } & EmbeddedTypeDef<Target> & EmbeddedOptions & PropertyOptions<Owner>) | ({
34
+ } & EmbeddedTypeDef<Target> & EmbeddedOptions<Owner, Target> & PropertyOptions<Owner>) | ({
35
35
  enum: true;
36
36
  } & EnumOptions<Owner>) | (TypeDef<Target> & PropertyOptions<Owner>);
37
37
  type OmitBaseProps<Entity, Base> = IsNever<Base> extends true ? Entity : Omit<Entity, keyof Base>;
@@ -63,7 +63,7 @@ export declare class EntitySchema<Entity = any, Base = never> {
63
63
  addVersion(name: EntityKey<Entity>, type: TypeType, options?: PropertyOptions<Entity>): void;
64
64
  addPrimaryKey(name: EntityKey<Entity>, type: TypeType, options?: PrimaryKeyOptions<Entity>): void;
65
65
  addSerializedPrimaryKey(name: EntityKey<Entity>, type: TypeType, options?: SerializedPrimaryKeyOptions<Entity>): void;
66
- addEmbedded<Target = AnyEntity>(name: EntityKey<Entity>, options: EmbeddedOptions): void;
66
+ addEmbedded<Target = AnyEntity>(name: EntityKey<Entity>, options: EmbeddedOptions<Entity, Target>): void;
67
67
  addManyToOne<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: ManyToOneOptions<Entity, Target>): void;
68
68
  addManyToMany<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: ManyToManyOptions<Entity, Target>): void;
69
69
  addOneToMany<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: OneToManyOptions<Entity, Target>): void;
@@ -83,5 +83,7 @@ export declare class EntitySchema<Entity = any, Base = never> {
83
83
  private initPrimaryKeys;
84
84
  private normalizeType;
85
85
  private createProperty;
86
+ private rename;
87
+ private renameCompositeOptions;
86
88
  }
87
89
  export {};
@@ -34,23 +34,7 @@ export class EntitySchema {
34
34
  return schema;
35
35
  }
36
36
  addProperty(name, type, options = {}) {
37
- const rename = (data, from, to) => {
38
- if (from in options && !(to in options)) {
39
- // @ts-ignore
40
- options[to] = [options[from]];
41
- // @ts-ignore
42
- delete options[from];
43
- }
44
- };
45
- if (name !== options.name) {
46
- Utils.renameKey(options, 'name', 'fieldName');
47
- }
48
- rename(options, 'fieldName', 'fieldNames');
49
- rename(options, 'ref', 'ref');
50
- rename(options, 'joinColumn', 'joinColumns');
51
- rename(options, 'inverseJoinColumn', 'inverseJoinColumns');
52
- rename(options, 'referenceColumnName', 'referencedColumnNames');
53
- rename(options, 'columnType', 'columnTypes');
37
+ this.renameCompositeOptions(name, options);
54
38
  const prop = { name, kind: ReferenceKind.SCALAR, ...options, type: this.normalizeType(options, type) };
55
39
  if (type && Type.isMappedType(type.prototype)) {
56
40
  prop.type = type;
@@ -94,6 +78,7 @@ export class EntitySchema {
94
78
  this.addProperty(name, type, options);
95
79
  }
96
80
  addEmbedded(name, options) {
81
+ this.renameCompositeOptions(name, options);
97
82
  Utils.defaultValue(options, 'prefix', true);
98
83
  if (options.array) {
99
84
  options.object = true; // force object mode for arrays
@@ -289,4 +274,25 @@ export class EntitySchema {
289
274
  ...options,
290
275
  };
291
276
  }
277
+ rename(data, from, to) {
278
+ if (from in data && !(to in data)) {
279
+ // @ts-ignore
280
+ data[to] = [data[from]];
281
+ // @ts-ignore
282
+ delete data[from];
283
+ }
284
+ }
285
+ renameCompositeOptions(name, options = {}) {
286
+ if (name !== options.name && !options.fieldNames) {
287
+ Utils.renameKey(options, 'name', 'fieldName');
288
+ }
289
+ else if (options.name && (options.fieldNames?.length ?? 0) > 1) {
290
+ delete options.name;
291
+ }
292
+ this.rename(options, 'fieldName', 'fieldNames');
293
+ this.rename(options, 'joinColumn', 'joinColumns');
294
+ this.rename(options, 'inverseJoinColumn', 'inverseJoinColumns');
295
+ this.rename(options, 'referenceColumnName', 'referencedColumnNames');
296
+ this.rename(options, 'columnType', 'columnTypes');
297
+ }
292
298
  }
@@ -264,7 +264,8 @@ export class MetadataDiscovery {
264
264
  // initialize global metadata for given entity
265
265
  MetadataStorage.getMetadata(entity.meta.className, filepath);
266
266
  }
267
- return entity;
267
+ const meta = Utils.copy(entity.meta, false);
268
+ return EntitySchema.fromMetadata(meta);
268
269
  }
269
270
  const path = entity[MetadataStorage.PATH_SYMBOL];
270
271
  if (path) {
@@ -1148,6 +1149,9 @@ export class MetadataDiscovery {
1148
1149
  prop.runtimeType ??= prop.customType.runtimeType;
1149
1150
  }
1150
1151
  }
1152
+ else if (prop.runtimeType === 'object') {
1153
+ prop.runtimeType = mappedType.runtimeType;
1154
+ }
1151
1155
  else {
1152
1156
  prop.runtimeType ??= mappedType.runtimeType;
1153
1157
  }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.14",
4
+ "version": "7.0.0-dev.15",
5
5
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
8
- ".": "./index.js"
8
+ ".": "./index.js",
9
+ "./exports.js": "./exports.js"
9
10
  },
10
11
  "repository": {
11
12
  "type": "git",
@@ -55,7 +56,7 @@
55
56
  "dotenv": "16.5.0",
56
57
  "esprima": "4.0.1",
57
58
  "globby": "11.1.0",
58
- "mikro-orm": "7.0.0-dev.14",
59
+ "mikro-orm": "7.0.0-dev.15",
59
60
  "reflect-metadata": "0.2.2"
60
61
  }
61
62
  }
package/typings.d.ts CHANGED
@@ -217,14 +217,17 @@ type ExpandRequiredEntityPropObject<T, I = never, C extends boolean = false> = {
217
217
  } & {
218
218
  [K in keyof T as OptionalKeys<T, K, I>]?: RequiredEntityDataProp<ExpandProperty<T[K]>, T, C> | EntityDataPropValue<ExpandProperty<T[K]>> | null | undefined;
219
219
  };
220
+ type NonArrayObject = object & {
221
+ [Symbol.iterator]?: never;
222
+ };
220
223
  export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends {
221
224
  __runtime?: infer Runtime;
222
225
  __raw?: infer Raw;
223
- } ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? EntityDataNested<U, C> : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends Collection<infer U, any> ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : T extends readonly (infer U)[] ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
226
+ } ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? EntityDataNested<U, C> : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends Collection<infer U, any> ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
224
227
  export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends {
225
228
  __runtime?: infer Runtime;
226
229
  __raw?: infer Raw;
227
- } ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends Collection<infer U, any> ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : T extends readonly (infer U)[] ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
230
+ } ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends Collection<infer U, any> ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
228
231
  export type EntityDataNested<T, C extends boolean = false> = T extends undefined ? never : T extends any[] ? Readonly<T> : EntityData<T, C> | ExpandEntityProp<T, C>;
229
232
  type EntityDataItem<T, C extends boolean> = C extends false ? T | EntityDataProp<T, C> | null : EntityDataProp<T, C> | null;
230
233
  export type RequiredEntityDataNested<T, O, C extends boolean> = T extends any[] ? Readonly<T> : RequiredEntityData<T, O> | ExpandRequiredEntityProp<T, O, C>;
@@ -391,6 +394,7 @@ export interface EntityProperty<Owner = any, Target = any> {
391
394
  optional?: boolean;
392
395
  ignoreSchemaChanges?: ('type' | 'extra' | 'default')[];
393
396
  deferMode?: DeferMode;
397
+ foreignKeyName?: string;
394
398
  }
395
399
  export declare class EntityMetadata<T = any> {
396
400
  private static counter;
@@ -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
  /**
@@ -2,6 +2,7 @@ import { Collection } from '../entity/Collection.js';
2
2
  import { helper } from '../entity/wrap.js';
3
3
  import { ReferenceKind } from '../enums.js';
4
4
  import { Reference } from '../entity/Reference.js';
5
+ import { Utils } from './Utils.js';
5
6
  export class DataloaderUtils {
6
7
  /**
7
8
  * Groups identified references by entity and returns a Map with the
@@ -118,7 +119,6 @@ export class DataloaderUtils {
118
119
  // 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
120
  populate: [
120
121
  ...(opts.populate === false ? [] : opts.populate ?? []),
121
- ...(opts.ref ? [':ref'] : []),
122
122
  ...Array.from(filterMap.keys()).filter(
123
123
  // 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
124
  prop => em.getMetadata(className).properties[prop]?.ref !== true),
@@ -149,10 +149,6 @@ export class DataloaderUtils {
149
149
  else if (target) {
150
150
  return target === collection.owner;
151
151
  }
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
152
  return false;
157
153
  };
158
154
  }
@@ -162,6 +158,24 @@ export class DataloaderUtils {
162
158
  */
163
159
  static getColBatchLoadFn(em) {
164
160
  return async (collsWithOpts) => {
161
+ const prop = collsWithOpts[0][0].property;
162
+ if (prop.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
163
+ const options = {};
164
+ const wrap = (cond) => ({ [prop.name]: cond });
165
+ const orderBy = Utils.asArray(collsWithOpts[0][1]?.orderBy).map(o => wrap(o));
166
+ const populate = wrap(collsWithOpts[0][1]?.populate);
167
+ const owners = collsWithOpts.map(c => c[0].owner);
168
+ const $or = [];
169
+ // a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
170
+ const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
171
+ const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
172
+ for (const c of collsWithOpts) {
173
+ $or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
174
+ options.refresh ??= c[1]?.refresh;
175
+ }
176
+ options.where = wrap({ $or });
177
+ return em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, collsWithOpts[0][1]?.ref);
178
+ }
165
179
  const entitiesAndOptsMap = DataloaderUtils.groupInversedOrMappedKeysByEntityAndOpts(collsWithOpts);
166
180
  const promises = DataloaderUtils.entitiesAndOptsMapToQueries(entitiesAndOptsMap, em);
167
181
  const resultsMap = new Map(await Promise.all(promises));
@@ -607,7 +607,7 @@ export class EntityComparator {
607
607
  * perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
608
608
  */
609
609
  static isComparable(prop, root) {
610
- const virtual = prop.persist === false || prop.generated;
610
+ const virtual = prop.persist === false || (prop.generated && !prop.primary);
611
611
  const inverse = prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner;
612
612
  const discriminator = prop.name === root.discriminatorColumn;
613
613
  const collection = prop.kind === ReferenceKind.ONE_TO_MANY || prop.kind === ReferenceKind.MANY_TO_MANY;