@mikro-orm/core 7.0.0-dev.6 → 7.0.0-dev.61

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 (123) hide show
  1. package/EntityManager.d.ts +85 -32
  2. package/EntityManager.js +281 -178
  3. package/MikroORM.d.ts +8 -8
  4. package/MikroORM.js +31 -74
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +2 -1
  7. package/cache/FileCacheAdapter.js +5 -4
  8. package/connections/Connection.d.ts +11 -7
  9. package/connections/Connection.js +16 -13
  10. package/decorators/Embeddable.d.ts +2 -0
  11. package/decorators/Embedded.d.ts +5 -11
  12. package/decorators/Entity.d.ts +20 -3
  13. package/decorators/Indexed.d.ts +2 -2
  14. package/decorators/ManyToMany.d.ts +2 -0
  15. package/decorators/ManyToOne.d.ts +4 -0
  16. package/decorators/OneToOne.d.ts +4 -0
  17. package/decorators/Property.d.ts +53 -9
  18. package/decorators/Transactional.d.ts +3 -1
  19. package/decorators/Transactional.js +6 -3
  20. package/decorators/index.d.ts +1 -1
  21. package/drivers/DatabaseDriver.d.ts +11 -5
  22. package/drivers/DatabaseDriver.js +13 -4
  23. package/drivers/IDatabaseDriver.d.ts +29 -5
  24. package/entity/ArrayCollection.d.ts +6 -4
  25. package/entity/ArrayCollection.js +27 -12
  26. package/entity/BaseEntity.d.ts +0 -1
  27. package/entity/BaseEntity.js +0 -3
  28. package/entity/Collection.d.ts +3 -4
  29. package/entity/Collection.js +34 -17
  30. package/entity/EntityAssigner.d.ts +1 -1
  31. package/entity/EntityAssigner.js +9 -1
  32. package/entity/EntityFactory.d.ts +7 -0
  33. package/entity/EntityFactory.js +63 -40
  34. package/entity/EntityHelper.js +26 -9
  35. package/entity/EntityLoader.d.ts +5 -4
  36. package/entity/EntityLoader.js +69 -36
  37. package/entity/EntityRepository.d.ts +1 -1
  38. package/entity/EntityValidator.js +2 -2
  39. package/entity/Reference.d.ts +9 -7
  40. package/entity/Reference.js +32 -5
  41. package/entity/WrappedEntity.d.ts +0 -2
  42. package/entity/WrappedEntity.js +1 -5
  43. package/entity/defineEntity.d.ts +555 -0
  44. package/entity/defineEntity.js +529 -0
  45. package/entity/index.d.ts +2 -0
  46. package/entity/index.js +2 -0
  47. package/entity/utils.d.ts +7 -0
  48. package/entity/utils.js +15 -3
  49. package/enums.d.ts +18 -5
  50. package/enums.js +13 -0
  51. package/errors.d.ts +6 -1
  52. package/errors.js +14 -4
  53. package/events/EventSubscriber.d.ts +3 -1
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +35 -24
  56. package/index.d.ts +2 -1
  57. package/index.js +1 -1
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/SimpleLogger.d.ts +1 -1
  60. package/metadata/EntitySchema.d.ts +8 -4
  61. package/metadata/EntitySchema.js +41 -23
  62. package/metadata/MetadataDiscovery.d.ts +5 -7
  63. package/metadata/MetadataDiscovery.js +151 -159
  64. package/metadata/MetadataStorage.js +1 -1
  65. package/metadata/MetadataValidator.js +4 -3
  66. package/metadata/discover-entities.d.ts +5 -0
  67. package/metadata/discover-entities.js +39 -0
  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 +14 -8
  72. package/platforms/Platform.d.ts +5 -8
  73. package/platforms/Platform.js +4 -17
  74. package/serialization/EntitySerializer.d.ts +2 -0
  75. package/serialization/EntitySerializer.js +29 -11
  76. package/serialization/EntityTransformer.js +22 -12
  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/BlobType.d.ts +0 -1
  81. package/types/BlobType.js +0 -3
  82. package/types/BooleanType.d.ts +2 -1
  83. package/types/BooleanType.js +3 -0
  84. package/types/DecimalType.d.ts +6 -4
  85. package/types/DecimalType.js +1 -1
  86. package/types/DoubleType.js +1 -1
  87. package/types/JsonType.d.ts +1 -1
  88. package/types/JsonType.js +7 -2
  89. package/types/Type.d.ts +2 -1
  90. package/types/Type.js +1 -1
  91. package/types/Uint8ArrayType.d.ts +0 -1
  92. package/types/Uint8ArrayType.js +0 -3
  93. package/types/index.d.ts +1 -1
  94. package/typings.d.ts +95 -52
  95. package/typings.js +31 -31
  96. package/unit-of-work/ChangeSetComputer.js +8 -3
  97. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  98. package/unit-of-work/ChangeSetPersister.js +37 -16
  99. package/unit-of-work/UnitOfWork.d.ts +8 -1
  100. package/unit-of-work/UnitOfWork.js +110 -53
  101. package/utils/AbstractSchemaGenerator.js +3 -1
  102. package/utils/Configuration.d.ts +201 -184
  103. package/utils/Configuration.js +143 -151
  104. package/utils/ConfigurationLoader.d.ts +9 -22
  105. package/utils/ConfigurationLoader.js +53 -76
  106. package/utils/Cursor.d.ts +3 -3
  107. package/utils/Cursor.js +3 -0
  108. package/utils/DataloaderUtils.d.ts +15 -5
  109. package/utils/DataloaderUtils.js +53 -7
  110. package/utils/EntityComparator.d.ts +8 -4
  111. package/utils/EntityComparator.js +105 -58
  112. package/utils/QueryHelper.d.ts +9 -1
  113. package/utils/QueryHelper.js +66 -5
  114. package/utils/RawQueryFragment.d.ts +36 -4
  115. package/utils/RawQueryFragment.js +34 -13
  116. package/utils/TransactionManager.d.ts +65 -0
  117. package/utils/TransactionManager.js +223 -0
  118. package/utils/Utils.d.ts +16 -31
  119. package/utils/Utils.js +129 -107
  120. package/utils/index.d.ts +1 -0
  121. package/utils/index.js +1 -0
  122. package/utils/upsert-utils.d.ts +7 -2
  123. package/utils/upsert-utils.js +52 -1
@@ -1,13 +1,15 @@
1
- import type { TransactionOptions } from '../enums.js';
1
+ import { type TransactionOptions } from '../enums.js';
2
2
  import type { ContextProvider } from '../typings.js';
3
3
  type TransactionalOptions<T> = TransactionOptions & {
4
4
  context?: ContextProvider<T>;
5
+ contextName?: string;
5
6
  };
6
7
  /**
7
8
  * This decorator wraps the method with `em.transactional()`, so you can provide `TransactionOptions` just like with `em.transactional()`.
8
9
  * The difference is that you can specify the context in which the transaction begins by providing `context` option,
9
10
  * and if omitted, the transaction will begin in the current context implicitly.
10
11
  * It works on async functions and can be nested with `em.transactional()`.
12
+ * Unlike `em.transactional()`, this decorator uses `REQUIRED` propagation by default, which means it will join existing transactions.
11
13
  */
12
14
  export declare function Transactional<T extends object>(options?: TransactionalOptions<T>): MethodDecorator;
13
15
  export {};
@@ -1,3 +1,4 @@
1
+ import { TransactionPropagation } from '../enums.js';
1
2
  import { RequestContext } from '../utils/RequestContext.js';
2
3
  import { resolveContextProvider } from '../utils/resolveContextProvider.js';
3
4
  import { TransactionContext } from '../utils/TransactionContext.js';
@@ -6,6 +7,7 @@ import { TransactionContext } from '../utils/TransactionContext.js';
6
7
  * The difference is that you can specify the context in which the transaction begins by providing `context` option,
7
8
  * and if omitted, the transaction will begin in the current context implicitly.
8
9
  * It works on async functions and can be nested with `em.transactional()`.
10
+ * Unlike `em.transactional()`, this decorator uses `REQUIRED` propagation by default, which means it will join existing transactions.
9
11
  */
10
12
  export function Transactional(options = {}) {
11
13
  return function (target, propertyKey, descriptor) {
@@ -14,10 +16,11 @@ export function Transactional(options = {}) {
14
16
  throw new Error('@Transactional() should be use with async functions');
15
17
  }
16
18
  descriptor.value = async function (...args) {
17
- const { context, ...txOptions } = options;
19
+ const { context, contextName, ...txOptions } = options;
20
+ txOptions.propagation ??= TransactionPropagation.REQUIRED;
18
21
  const em = (await resolveContextProvider(this, context))
19
- || TransactionContext.getEntityManager()
20
- || RequestContext.getEntityManager();
22
+ || TransactionContext.getEntityManager(contextName)
23
+ || RequestContext.getEntityManager(contextName);
21
24
  if (!em) {
22
25
  throw new Error(`@Transactional() decorator can only be applied to methods of classes with \`orm: MikroORM\` property, \`em: EntityManager\` property, or with a callback parameter like \`@Transactional(() => orm)\` that returns one of those types. The parameter will contain a reference to current \`this\`. Returning an EntityRepository from it is also supported.`);
23
26
  }
@@ -3,7 +3,7 @@ export * from './Entity.js';
3
3
  export * from './OneToOne.js';
4
4
  export * from './ManyToOne.js';
5
5
  export * from './ManyToMany.js';
6
- export { OneToMany, OneToManyOptions } from './OneToMany.js';
6
+ export { OneToMany, type OneToManyOptions } from './OneToMany.js';
7
7
  export * from './Property.js';
8
8
  export * from './Check.js';
9
9
  export * from './Enum.js';
@@ -1,5 +1,5 @@
1
- import { type CountOptions, type DeleteOptions, type DriverMethodOptions, EntityManagerType, type FindOneOptions, type FindOptions, type IDatabaseDriver, type LockOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type OrderDefinition } from './IDatabaseDriver.js';
2
- import type { ConnectionType, Dictionary, EntityData, EntityDictionary, EntityMetadata, EntityProperty, FilterQuery, PopulateOptions, Primary } from '../typings.js';
1
+ import { type CountOptions, type DeleteOptions, type DriverMethodOptions, EntityManagerType, type FindOneOptions, type FindOptions, type IDatabaseDriver, type LockOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type OrderDefinition, type StreamOptions } from './IDatabaseDriver.js';
2
+ import type { ConnectionType, Dictionary, EntityData, EntityDictionary, EntityMetadata, EntityName, EntityProperty, FilterQuery, PopulateOptions, Primary } from '../typings.js';
3
3
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
4
4
  import type { Connection, QueryResult, Transaction } from '../connections/Connection.js';
5
5
  import { type Configuration, type ConnectionOptions } from '../utils/Configuration.js';
@@ -30,20 +30,25 @@ export declare abstract class DatabaseDriver<C extends Connection> implements ID
30
30
  abstract nativeDelete<T extends object>(entityName: string, where: FilterQuery<T>, options?: DeleteOptions<T>): Promise<QueryResult<T>>;
31
31
  abstract count<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: CountOptions<T, P>): Promise<number>;
32
32
  createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
33
- findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
33
+ findVirtual<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
34
34
  countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T, any>): Promise<number>;
35
35
  aggregate(entityName: string, pipeline: any[]): Promise<any[]>;
36
36
  loadFromPivotTable<T extends object, O extends object>(prop: EntityProperty, owners: Primary<O>[][], where?: FilterQuery<any>, orderBy?: OrderDefinition<T>, ctx?: Transaction, options?: FindOptions<T, any, any, any>, pivotJoin?: boolean): Promise<Dictionary<T[]>>;
37
37
  syncCollections<T extends object, O extends object>(collections: Iterable<Collection<T, O>>, options?: DriverMethodOptions): Promise<void>;
38
38
  mapResult<T extends object>(result: EntityDictionary<T>, meta?: EntityMetadata<T>, populate?: PopulateOptions<T>[]): EntityData<T> | null;
39
- connect(): Promise<C>;
40
- reconnect(): Promise<C>;
39
+ connect(options?: {
40
+ skipOnConnect?: boolean;
41
+ }): Promise<C>;
42
+ reconnect(options?: {
43
+ skipOnConnect?: boolean;
44
+ }): Promise<C>;
41
45
  getConnection(type?: ConnectionType): C;
42
46
  close(force?: boolean): Promise<void>;
43
47
  getPlatform(): Platform;
44
48
  setMetadata(metadata: MetadataStorage): void;
45
49
  getMetadata(): MetadataStorage;
46
50
  getDependencies(): string[];
51
+ protected isPopulated<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T>, hint: PopulateOptions<T>, name?: string): boolean;
47
52
  protected processCursorOptions<T extends object, P extends string>(meta: EntityMetadata<T>, options: FindOptions<T, P, any, any>, orderBy: OrderDefinition<T>): {
48
53
  orderBy: OrderDefinition<T>[];
49
54
  where: FilterQuery<T>;
@@ -55,6 +60,7 @@ export declare abstract class DatabaseDriver<C extends Connection> implements ID
55
60
  protected getPrimaryKeyFields(entityName: string): string[];
56
61
  protected createReplicas(cb: (c: ConnectionOptions) => C): C[];
57
62
  lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
63
+ abstract stream<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: StreamOptions<T>): AsyncIterableIterator<T>;
58
64
  /**
59
65
  * @inheritDoc
60
66
  */
@@ -65,14 +65,14 @@ export class DatabaseDriver {
65
65
  }
66
66
  return this.comparator.mapResult(meta.className, result);
67
67
  }
68
- async connect() {
69
- await this.connection.connect();
68
+ async connect(options) {
69
+ await this.connection.connect(options);
70
70
  await Promise.all(this.replicas.map(replica => replica.connect()));
71
71
  return this.connection;
72
72
  }
73
- async reconnect() {
73
+ async reconnect(options) {
74
74
  await this.close(true);
75
- await this.connect();
75
+ await this.connect(options);
76
76
  return this.connection;
77
77
  }
78
78
  getConnection(type = 'write') {
@@ -105,6 +105,15 @@ export class DatabaseDriver {
105
105
  getDependencies() {
106
106
  return this.dependencies;
107
107
  }
108
+ isPopulated(meta, prop, hint, name) {
109
+ if (hint.field === prop.name || hint.field === name || hint.all) {
110
+ return true;
111
+ }
112
+ if (prop.embedded && hint.children && meta.properties[prop.embedded[0]].name === hint.field) {
113
+ return hint.children.some(c => this.isPopulated(meta, prop, c, prop.embedded[1]));
114
+ }
115
+ return false;
116
+ }
108
117
  processCursorOptions(meta, options, orderBy) {
109
118
  const { first, last, before, after, overfetch } = options;
110
119
  const limit = first ?? last;
@@ -1,4 +1,4 @@
1
- import type { ConnectionType, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary, Dictionary, QBFilterQuery, IPrimaryKey, PopulateOptions, EntityDictionary, AutoPath, ObjectQuery, FilterObject, Populate } from '../typings.js';
1
+ import type { ConnectionType, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary, Dictionary, QBFilterQuery, IPrimaryKey, PopulateOptions, EntityDictionary, AutoPath, ObjectQuery, FilterObject, Populate, EntityName } from '../typings.js';
2
2
  import type { Connection, QueryResult, Transaction } from '../connections/Connection.js';
3
3
  import type { FlushMode, LockMode, QueryOrderMap, QueryFlag, LoadStrategy, PopulateHint, PopulatePath } from '../enums.js';
4
4
  import type { Platform } from '../platforms/Platform.js';
@@ -14,9 +14,13 @@ export interface IDatabaseDriver<C extends Connection = Connection> {
14
14
  [EntityManagerType]: EntityManager<this>;
15
15
  readonly config: Configuration;
16
16
  createEntityManager(useContext?: boolean): this[typeof EntityManagerType];
17
- connect(): Promise<C>;
17
+ connect(options?: {
18
+ skipOnConnect?: boolean;
19
+ }): Promise<C>;
18
20
  close(force?: boolean): Promise<void>;
19
- reconnect(): Promise<C>;
21
+ reconnect(options?: {
22
+ skipOnConnect?: boolean;
23
+ }): Promise<C>;
20
24
  getConnection(type?: ConnectionType): C;
21
25
  /**
22
26
  * Finds selection of entities
@@ -27,6 +31,7 @@ export interface IDatabaseDriver<C extends Connection = Connection> {
27
31
  */
28
32
  findOne<T extends object, P extends string = never, F extends string = '*', E extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P, F, E>): Promise<EntityData<T> | null>;
29
33
  findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any, any>): Promise<EntityData<T>[]>;
34
+ stream<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, options: StreamOptions<T>): AsyncIterableIterator<T>;
30
35
  nativeInsert<T extends object>(entityName: string, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
31
36
  nativeInsertMany<T extends object>(entityName: string, data: EntityDictionary<T>[], options?: NativeInsertUpdateManyOptions<T>, transform?: (sql: string) => string): Promise<QueryResult<T>>;
32
37
  nativeUpdate<T extends object>(entityName: string, where: FilterQuery<T>, data: EntityDictionary<T>, options?: NativeInsertUpdateOptions<T>): Promise<QueryResult<T>>;
@@ -68,6 +73,18 @@ export type OrderDefinition<T> = (QueryOrderMap<T> & {
68
73
  export interface FindAllOptions<T, P extends string = never, F extends string = '*', E extends string = never> extends FindOptions<T, P, F, E> {
69
74
  where?: FilterQuery<T>;
70
75
  }
76
+ export interface StreamOptions<Entity, Populate extends string = never, Fields extends string = '*', Exclude extends string = never> extends Omit<FindAllOptions<Entity, Populate, Fields, Exclude>, 'cache' | 'before' | 'after' | 'first' | 'last' | 'overfetch' | 'strategy'> {
77
+ /**
78
+ * When populating to-many relations, the ORM streams fully merged entities instead of yielding every row.
79
+ * You can opt out of this behavior by specifying `mergeResults: false`. This will yield every row from
80
+ * the SQL result, but still mapped to entities, meaning that to-many collections will contain at most
81
+ * a single item, and you will get duplicate root entities when they have multiple items in the populated
82
+ * collection.
83
+ *
84
+ * @default true
85
+ */
86
+ mergeResults?: boolean;
87
+ }
71
88
  export type FilterOptions = Dictionary<boolean | Dictionary> | string[] | boolean;
72
89
  export interface LoadHint<Entity, Hint extends string = never, Fields extends string = PopulatePath.ALL, Excludes extends string = never> {
73
90
  populate?: Populate<Entity, Hint>;
@@ -146,10 +163,13 @@ export interface FindOptions<Entity, Hint extends string = never, Fields extends
146
163
  hintComments?: string | string[];
147
164
  loggerContext?: LogContext;
148
165
  logging?: LoggingOptions;
166
+ /** @internal used to apply filters to the auto-joined relations */
167
+ em?: EntityManager;
149
168
  }
150
- export interface FindByCursorOptions<T extends object, P extends string = never, F extends string = '*', E extends string = never> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'offset'> {
169
+ export interface FindByCursorOptions<T extends object, P extends string = never, F extends string = '*', E extends string = never, I extends boolean = true> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'offset'> {
170
+ includeCount?: I;
151
171
  }
152
- export interface FindOneOptions<T extends object, P extends string = never, F extends string = '*', E extends string = never> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'lockMode'> {
172
+ export interface FindOneOptions<T, P extends string = never, F extends string = '*', E extends string = never> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'lockMode'> {
153
173
  lockMode?: LockMode;
154
174
  lockVersion?: number | Date;
155
175
  }
@@ -163,6 +183,7 @@ export interface NativeInsertUpdateOptions<T> {
163
183
  schema?: string;
164
184
  /** `nativeUpdate()` only option */
165
185
  upsert?: boolean;
186
+ loggerContext?: LogContext;
166
187
  }
167
188
  export interface NativeInsertUpdateManyOptions<T> extends NativeInsertUpdateOptions<T> {
168
189
  processCollections?: boolean;
@@ -197,6 +218,8 @@ export interface CountOptions<T extends object, P extends string = never> {
197
218
  hintComments?: string | string[];
198
219
  loggerContext?: LogContext;
199
220
  logging?: LoggingOptions;
221
+ /** @internal used to apply filters to the auto-joined relations */
222
+ em?: EntityManager;
200
223
  }
201
224
  export interface UpdateOptions<T> {
202
225
  filters?: FilterOptions;
@@ -218,6 +241,7 @@ export interface LockOptions extends DriverMethodOptions {
218
241
  export interface DriverMethodOptions {
219
242
  ctx?: Transaction;
220
243
  schema?: string;
244
+ loggerContext?: LogContext;
221
245
  }
222
246
  export interface GetReferenceOptions {
223
247
  wrapped?: boolean;
@@ -6,6 +6,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
6
6
  protected readonly items: Set<T>;
7
7
  protected initialized: boolean;
8
8
  protected dirty: boolean;
9
+ protected partial: boolean;
9
10
  protected snapshot: T[] | undefined;
10
11
  protected _count?: number;
11
12
  private _property?;
@@ -14,8 +15,8 @@ export declare class ArrayCollection<T extends object, O extends object> {
14
15
  getItems(): T[];
15
16
  toArray<TT extends T>(): EntityDTO<TT>[];
16
17
  toJSON(): EntityDTO<T>[];
17
- getIdentifiers<U extends IPrimaryKey = Primary<T> & IPrimaryKey>(field?: string): U[];
18
- add(entity: T | Reference<T> | Iterable<T | Reference<T>>, ...entities: (T | Reference<T>)[]): void;
18
+ getIdentifiers<U extends IPrimaryKey = Primary<T> & IPrimaryKey>(field?: string | string[]): U[];
19
+ add(entity: T | Reference<T> | Iterable<T | Reference<T>>, ...entities: (T | Reference<T>)[]): number;
19
20
  /**
20
21
  * @internal
21
22
  */
@@ -25,14 +26,14 @@ export declare class ArrayCollection<T extends object, O extends object> {
25
26
  /**
26
27
  * @internal
27
28
  */
28
- hydrate(items: T[], forcePropagate?: boolean): void;
29
+ hydrate(items: T[], forcePropagate?: boolean, partial?: boolean): void;
29
30
  /**
30
31
  * Remove specified item(s) from the collection. Note that removing item from collection does not necessarily imply deleting the target entity,
31
32
  * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
32
33
  * is not the same as `em.remove()`. If we want to delete the entity by removing it from collection, we need to enable `orphanRemoval: true`,
33
34
  * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
34
35
  */
35
- remove(entity: T | Reference<T> | Iterable<T | Reference<T>>, ...entities: (T | Reference<T>)[]): void;
36
+ remove(entity: T | Reference<T> | Iterable<T | Reference<T>>, ...entities: (T | Reference<T>)[]): number;
36
37
  /**
37
38
  * Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
38
39
  * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
@@ -83,6 +84,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
83
84
  count(): number;
84
85
  isInitialized(fully?: boolean): boolean;
85
86
  isDirty(): boolean;
87
+ isPartial(): boolean;
86
88
  isEmpty(): boolean;
87
89
  setDirty(dirty?: boolean): void;
88
90
  get length(): number;
@@ -9,6 +9,7 @@ export class ArrayCollection {
9
9
  items = new Set();
10
10
  initialized = true;
11
11
  dirty = false;
12
+ partial = false; // mark partially loaded collections, propagation is disabled for those
12
13
  snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
13
14
  _count;
14
15
  _property;
@@ -31,37 +32,45 @@ export class ArrayCollection {
31
32
  if (this.items.size === 0) {
32
33
  return [];
33
34
  }
34
- const meta = this.property.targetMeta;
35
- const args = meta.toJsonParams.map(() => undefined);
36
- return this.map(item => wrap(item).toJSON(...args));
35
+ return this.map(item => wrap(item).toJSON());
37
36
  }
38
37
  toJSON() {
39
38
  return this.toArray();
40
39
  }
41
40
  getIdentifiers(field) {
42
41
  const items = this.getItems();
42
+ const targetMeta = this.property.targetMeta;
43
43
  if (items.length === 0) {
44
44
  return [];
45
45
  }
46
- field ??= this.property.targetMeta.serializedPrimaryKey;
46
+ field ??= targetMeta.compositePK ? targetMeta.primaryKeys : (targetMeta.serializedPrimaryKey ?? targetMeta.primaryKeys[0]);
47
+ const cb = (i, f) => {
48
+ if (Utils.isEntity(i[f], true)) {
49
+ return wrap(i[f], true).getPrimaryKey();
50
+ }
51
+ return i[f];
52
+ };
47
53
  return items.map(i => {
48
- if (Utils.isEntity(i[field], true)) {
49
- return wrap(i[field], true).getPrimaryKey();
54
+ if (Array.isArray(field)) {
55
+ return field.map(f => cb(i, f));
50
56
  }
51
- return i[field];
57
+ return cb(i, field);
52
58
  });
53
59
  }
54
60
  add(entity, ...entities) {
55
61
  entities = Utils.asArray(entity).concat(entities);
62
+ let added = 0;
56
63
  for (const item of entities) {
57
64
  const entity = Reference.unwrapReference(item);
58
65
  if (!this.contains(entity, false)) {
59
66
  this.incrementCount(1);
60
67
  this[this.items.size] = entity;
61
68
  this.items.add(entity);
69
+ added++;
62
70
  this.propagate(entity, 'add');
63
71
  }
64
72
  }
73
+ return added;
65
74
  }
66
75
  /**
67
76
  * @internal
@@ -100,11 +109,12 @@ export class ArrayCollection {
100
109
  /**
101
110
  * @internal
102
111
  */
103
- hydrate(items, forcePropagate) {
112
+ hydrate(items, forcePropagate, partial) {
104
113
  for (let i = 0; i < this.items.size; i++) {
105
114
  delete this[i];
106
115
  }
107
116
  this.initialized = true;
117
+ this.partial = !!partial;
108
118
  this.items.clear();
109
119
  this._count = 0;
110
120
  this.add(items);
@@ -118,7 +128,7 @@ export class ArrayCollection {
118
128
  */
119
129
  remove(entity, ...entities) {
120
130
  entities = Utils.asArray(entity).concat(entities);
121
- let modified = false;
131
+ let removed = 0;
122
132
  for (const item of entities) {
123
133
  if (!item) {
124
134
  continue;
@@ -128,12 +138,13 @@ export class ArrayCollection {
128
138
  this.incrementCount(-1);
129
139
  delete this[this.items.size]; // remove last item
130
140
  this.propagate(entity, 'remove');
131
- modified = true;
141
+ removed++;
132
142
  }
133
143
  }
134
- if (modified) {
144
+ if (removed > 0) {
135
145
  Object.assign(this, [...this.items]); // reassign array access
136
146
  }
147
+ return removed;
137
148
  }
138
149
  /**
139
150
  * Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
@@ -147,6 +158,7 @@ export class ArrayCollection {
147
158
  this.snapshot = undefined;
148
159
  }
149
160
  this.remove(this.items);
161
+ this.setDirty();
150
162
  }
151
163
  /**
152
164
  * @internal
@@ -267,6 +279,9 @@ export class ArrayCollection {
267
279
  isDirty() {
268
280
  return this.dirty;
269
281
  }
282
+ isPartial() {
283
+ return this.partial;
284
+ }
270
285
  isEmpty() {
271
286
  return this.count() === 0;
272
287
  }
@@ -383,7 +398,7 @@ export class ArrayCollection {
383
398
  /** @ignore */
384
399
  [inspect.custom](depth = 2) {
385
400
  const object = { ...this };
386
- const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_snapshot', '_lazyInitialized', '_em', 'readonly'];
401
+ const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_snapshot', '_lazyInitialized', '_em', 'readonly', 'partial'];
387
402
  hidden.forEach(k => delete object[k]);
388
403
  const ret = inspect(object, { depth });
389
404
  const name = `${this.constructor.name}<${this.property?.type ?? 'unknown'}>`;
@@ -6,7 +6,6 @@ import { type SerializeOptions } from '../serialization/EntitySerializer.js';
6
6
  import type { FindOneOptions } from '../drivers/IDatabaseDriver.js';
7
7
  export declare abstract class BaseEntity {
8
8
  isInitialized(): boolean;
9
- isTouched(): boolean;
10
9
  populated(populated?: boolean): void;
11
10
  populate<Entity extends this = this, Hint extends string = never>(populate: AutoPath<Entity, Hint>[] | false, options?: EntityLoaderOptions<Entity>): Promise<Loaded<Entity, Hint>>;
12
11
  toReference<Entity extends this = this>(): Ref<Entity> & LoadedReference<Loaded<Entity, AddEager<Entity>>>;
@@ -6,9 +6,6 @@ export class BaseEntity {
6
6
  isInitialized() {
7
7
  return helper(this).__initialized;
8
8
  }
9
- isTouched() {
10
- return helper(this).__touched;
11
- }
12
9
  populated(populated = true) {
13
10
  helper(this).populated(populated);
14
11
  }
@@ -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>;
@@ -12,7 +12,6 @@ export interface MatchingOptions<T extends object, P extends string = never> ext
12
12
  export declare class Collection<T extends object, O extends object = object> extends ArrayCollection<T, O> {
13
13
  private readonly?;
14
14
  private _populated?;
15
- private _em?;
16
15
  private _snapshot?;
17
16
  constructor(owner: O, items?: T[], initialized?: boolean);
18
17
  /**
@@ -40,11 +39,11 @@ export declare class Collection<T extends object, O extends object = object> ext
40
39
  */
41
40
  getItems(check?: boolean): T[];
42
41
  toJSON<TT extends T>(): EntityDTO<TT>[];
43
- add<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>>, ...entities: (TT | Reference<TT>)[]): void;
42
+ add<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>>, ...entities: (TT | Reference<TT>)[]): number;
44
43
  /**
45
44
  * @inheritDoc
46
45
  */
47
- remove<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>> | ((item: TT) => boolean), ...entities: (TT | Reference<TT>)[]): void;
46
+ remove<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>> | ((item: TT) => boolean), ...entities: (TT | Reference<TT>)[]): number;
48
47
  contains<TT extends T>(item: TT | Reference<TT>, check?: boolean): boolean;
49
48
  count(): number;
50
49
  isEmpty(): boolean;
@@ -1,13 +1,13 @@
1
1
  import { ArrayCollection } from './ArrayCollection.js';
2
2
  import { Utils } from '../utils/Utils.js';
3
3
  import { ValidationError } from '../errors.js';
4
- import { ReferenceKind, DataloaderType } from '../enums.js';
4
+ import { DataloaderType, ReferenceKind } from '../enums.js';
5
5
  import { Reference } from './Reference.js';
6
6
  import { helper } from './wrap.js';
7
+ import { QueryHelper } from '../utils/QueryHelper.js';
7
8
  export class Collection extends ArrayCollection {
8
9
  readonly;
9
10
  _populated;
10
- _em;
11
11
  // this is for some reason needed for TS, otherwise it can fail with `Type instantiation is excessively deep and possibly infinite.`
12
12
  _snapshot;
13
13
  constructor(owner, items, initialized = true) {
@@ -33,6 +33,7 @@ export class Collection extends ArrayCollection {
33
33
  async load(options = {}) {
34
34
  if (this.isInitialized(true) && !options.refresh) {
35
35
  const em = this.getEntityManager(this.items, false);
36
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
36
37
  await em?.populate(this.items, options.populate, options);
37
38
  this.setSerializationContext(options);
38
39
  }
@@ -116,30 +117,33 @@ export class Collection extends ArrayCollection {
116
117
  entities = Utils.asArray(entity).concat(entities);
117
118
  const unwrapped = entities.map(i => Reference.unwrapReference(i));
118
119
  unwrapped.forEach(entity => this.validateItemType(entity));
119
- this.modify('add', unwrapped);
120
+ const added = this.modify('add', unwrapped);
120
121
  this.cancelOrphanRemoval(unwrapped);
122
+ return added;
121
123
  }
122
124
  /**
123
125
  * @inheritDoc
124
126
  */
125
127
  remove(entity, ...entities) {
126
128
  if (entity instanceof Function) {
129
+ let removed = 0;
127
130
  for (const item of this.items) {
128
131
  if (entity(item)) {
129
- this.remove(item);
132
+ removed += this.remove(item);
130
133
  }
131
134
  }
132
- return;
135
+ return removed;
133
136
  }
134
137
  entities = Utils.asArray(entity).concat(entities);
135
138
  const unwrapped = entities.map(i => Reference.unwrapReference(i));
136
- this.modify('remove', unwrapped);
139
+ const removed = this.modify('remove', unwrapped);
137
140
  const em = this.getEntityManager(unwrapped, false);
138
141
  if (this.property.orphanRemoval && em) {
139
142
  for (const item of unwrapped) {
140
143
  em.getUnitOfWork().scheduleOrphanRemoval(item);
141
144
  }
142
145
  }
146
+ return removed;
143
147
  }
144
148
  contains(item, check = true) {
145
149
  if (check) {
@@ -218,13 +222,21 @@ export class Collection extends ArrayCollection {
218
222
  return this;
219
223
  }
220
224
  const em = this.getEntityManager();
225
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
221
226
  if (options.dataloader ?? [DataloaderType.ALL, DataloaderType.COLLECTION].includes(em.config.getDataloaderType())) {
222
227
  const order = [...this.items]; // copy order of references
223
- const customOrder = !!options.orderBy;
224
- // eslint-disable-next-line dot-notation
225
- const items = await em['colLoader'].load([this, options]);
226
- if (!customOrder) {
227
- this.reorderItems(items, order);
228
+ const orderBy = this.createOrderBy(options.orderBy);
229
+ const customOrder = orderBy.length > 0;
230
+ const pivotTable = this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable();
231
+ const loader = await em.getDataLoader(pivotTable ? 'm:n' : '1:m');
232
+ const items = await loader.load([this, { ...options, orderBy }]);
233
+ if (this.property.kind === ReferenceKind.MANY_TO_MANY) {
234
+ this.initialized = true;
235
+ this.dirty = false;
236
+ if (!customOrder) {
237
+ this.reorderItems(items, order);
238
+ }
239
+ return this;
228
240
  }
229
241
  this.items.clear();
230
242
  let i = 0;
@@ -254,18 +266,17 @@ export class Collection extends ArrayCollection {
254
266
  }
255
267
  getEntityManager(items = [], required = true) {
256
268
  const wrapped = helper(this.owner);
257
- let em = (this._em ?? wrapped.__em);
269
+ let em = wrapped.__em;
270
+ // console.log('wat 1', em, this.owner);
258
271
  if (!em) {
259
272
  for (const i of items) {
273
+ // console.log('wat 2', i, i && helper(i).__em);
260
274
  if (i && helper(i).__em) {
261
275
  em = helper(i).__em;
262
276
  break;
263
277
  }
264
278
  }
265
279
  }
266
- if (em) {
267
- Object.defineProperty(this, '_em', { value: em });
268
- }
269
280
  if (!em && required) {
270
281
  throw ValidationError.entityNotManaged(this.owner);
271
282
  }
@@ -316,8 +327,14 @@ export class Collection extends ArrayCollection {
316
327
  this.checkInitialized();
317
328
  }
318
329
  this.validateModification(items);
319
- super[method](items);
320
- this.setDirty();
330
+ const modified = super[method](items);
331
+ if (modified > 0) {
332
+ this.setDirty();
333
+ }
334
+ if (this.property.kind === ReferenceKind.ONE_TO_MANY && (method === 'add' || !this.property.orphanRemoval)) {
335
+ this.getEntityManager(items, false)?.persist(items);
336
+ }
337
+ return modified;
321
338
  }
322
339
  checkInitialized() {
323
340
  if (!this.isInitialized()) {
@@ -36,7 +36,7 @@ export interface AssignOptions<Convert extends boolean> {
36
36
  */
37
37
  onlyProperties?: boolean;
38
38
  /**
39
- * With `onlyOwnProperties` enabled, to-many relations are skipped, and payloads of to-one relations are converted
39
+ * With `onlyOwnProperties` enabled, inverse sides of to-many relations are skipped, and payloads of other relations are converted
40
40
  * to foreign keys. Defaults to `false`.
41
41
  */
42
42
  onlyOwnProperties?: boolean;
@@ -42,9 +42,17 @@ export class EntityAssigner {
42
42
  }
43
43
  const prop = { ...props[propName], name: propName };
44
44
  if (prop && options.onlyOwnProperties) {
45
- if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind)) {
45
+ if ([ReferenceKind.ONE_TO_MANY].includes(prop.kind)) {
46
46
  return;
47
47
  }
48
+ if ([ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
49
+ if (!prop.owner) {
50
+ return;
51
+ }
52
+ else if (value?.map) {
53
+ value = value.map((v) => Utils.extractPK(v, prop.targetMeta));
54
+ }
55
+ }
48
56
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
49
57
  value = Utils.extractPK(value, prop.targetMeta);
50
58
  }
@@ -4,12 +4,18 @@ import type { EntityComparator } from '../utils/EntityComparator.js';
4
4
  export interface FactoryOptions {
5
5
  initialized?: boolean;
6
6
  newEntity?: boolean;
7
+ /**
8
+ * Property `onCreate` hooks are normally executed during `flush` operation.
9
+ * With this option, they will be processed early inside `em.create()` method.
10
+ */
11
+ processOnCreateHooksEarly?: boolean;
7
12
  merge?: boolean;
8
13
  refresh?: boolean;
9
14
  convertCustomTypes?: boolean;
10
15
  recomputeSnapshot?: boolean;
11
16
  schema?: string;
12
17
  parentSchema?: string;
18
+ normalizeAccessors?: boolean;
13
19
  }
14
20
  export declare class EntityFactory {
15
21
  private readonly em;
@@ -27,6 +33,7 @@ export declare class EntityFactory {
27
33
  createEmbeddable<T extends object>(entityName: EntityName<T>, data: EntityData<T>, options?: Pick<FactoryOptions, 'newEntity' | 'convertCustomTypes'>): T;
28
34
  getComparator(): EntityComparator;
29
35
  private createEntity;
36
+ private assignDefaultValues;
30
37
  private hydrate;
31
38
  private findEntity;
32
39
  private processDiscriminatorColumn;