@mikro-orm/core 7.0.0-dev.42 → 7.0.0-dev.44

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 (41) hide show
  1. package/EntityManager.d.ts +2 -14
  2. package/EntityManager.js +24 -5
  3. package/MikroORM.d.ts +1 -2
  4. package/MikroORM.js +21 -58
  5. package/connections/Connection.d.ts +5 -3
  6. package/connections/Connection.js +14 -11
  7. package/decorators/Property.d.ts +0 -6
  8. package/drivers/DatabaseDriver.d.ts +6 -2
  9. package/drivers/DatabaseDriver.js +4 -4
  10. package/drivers/IDatabaseDriver.d.ts +6 -2
  11. package/entity/ArrayCollection.d.ts +2 -2
  12. package/entity/ArrayCollection.js +8 -3
  13. package/entity/BaseEntity.d.ts +0 -1
  14. package/entity/BaseEntity.js +0 -3
  15. package/entity/Collection.d.ts +2 -2
  16. package/entity/Collection.js +18 -7
  17. package/entity/EntityFactory.js +0 -2
  18. package/entity/EntityHelper.js +2 -7
  19. package/entity/WrappedEntity.d.ts +0 -2
  20. package/entity/WrappedEntity.js +0 -4
  21. package/entity/defineEntity.d.ts +0 -6
  22. package/entity/defineEntity.js +0 -8
  23. package/metadata/MetadataDiscovery.d.ts +1 -1
  24. package/metadata/MetadataDiscovery.js +2 -7
  25. package/package.json +2 -2
  26. package/serialization/EntitySerializer.js +21 -3
  27. package/serialization/EntityTransformer.js +15 -5
  28. package/typings.d.ts +4 -6
  29. package/typings.js +7 -27
  30. package/unit-of-work/ChangeSetComputer.js +5 -2
  31. package/unit-of-work/UnitOfWork.js +3 -14
  32. package/utils/AbstractSchemaGenerator.js +3 -1
  33. package/utils/Configuration.d.ts +2 -7
  34. package/utils/Configuration.js +2 -12
  35. package/utils/ConfigurationLoader.d.ts +2 -18
  36. package/utils/ConfigurationLoader.js +8 -32
  37. package/utils/RawQueryFragment.d.ts +0 -2
  38. package/utils/RawQueryFragment.js +0 -13
  39. package/utils/TransactionManager.js +8 -3
  40. package/utils/Utils.d.ts +0 -4
  41. package/utils/Utils.js +0 -20
@@ -10,7 +10,7 @@ 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, FilterOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers/IDatabaseDriver.js';
13
- import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings.js';
13
+ import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings.js';
14
14
  import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums.js';
15
15
  import type { MetadataStorage } from './metadata/MetadataStorage.js';
16
16
  import type { Transaction } from './connections/Connection.js';
@@ -109,19 +109,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
109
109
  /**
110
110
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
111
111
  */
112
- addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], options?: boolean | Partial<FilterDef>): void;
113
- /**
114
- * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
115
- */
116
- addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], options?: boolean | Partial<FilterDef>): void;
117
- /**
118
- * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
119
- */
120
- addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], options?: boolean | Partial<FilterDef>): void;
121
- /**
122
- * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
123
- */
124
- addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], options?: boolean | Partial<FilterDef>): void;
112
+ addFilter<T extends object>(options: FilterDef<T>): void;
125
113
  /**
126
114
  * Sets filter parameter values globally inside context defined by this entity manager.
127
115
  * If you want to set shared value for all contexts, be sure to use the root entity manager.
package/EntityManager.js CHANGED
@@ -256,15 +256,34 @@ export class EntityManager {
256
256
  }
257
257
  return { where: options.populateWhere };
258
258
  }
259
+ // /**
260
+ // * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
261
+ // */
262
+ // addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], options?: boolean | Partial<FilterDef>): void;
263
+ //
264
+ // /**
265
+ // * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
266
+ // */
267
+ // addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], options?: boolean | Partial<FilterDef>): void;
268
+ //
269
+ // /**
270
+ // * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
271
+ // */
272
+ // addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], options?: boolean | Partial<FilterDef>): void;
273
+ //
274
+ // /**
275
+ // * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
276
+ // */
277
+ // addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], options?: boolean | Partial<FilterDef>): void;
259
278
  /**
260
279
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
261
280
  */
262
- addFilter(name, cond, entityName, options = true) {
263
- options = typeof options === 'object' ? { name, cond, default: true, ...options } : { name, cond, default: options };
264
- if (entityName) {
265
- options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
281
+ addFilter(options) {
282
+ if (options.entity) {
283
+ options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
266
284
  }
267
- this.getContext(false).filters[name] = options;
285
+ options.default ??= true;
286
+ this.getContext(false).filters[options.name] = options;
268
287
  }
269
288
  /**
270
289
  * Sets filter parameter values globally inside context defined by this entity manager.
package/MikroORM.d.ts CHANGED
@@ -21,7 +21,7 @@ export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver,
21
21
  * Initialize the ORM, load entity metadata, create EntityManager and connect to the database.
22
22
  * If you omit the `options` parameter, your CLI config will be used.
23
23
  */
24
- static init<D extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager, Entities extends (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[]>(options?: Options<D, EM, Entities>): Promise<MikroORM<D, EM, Entities>>;
24
+ static init<D extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager, Entities extends (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[]>(options: Options<D, EM, Entities>): Promise<MikroORM<D, EM, Entities>>;
25
25
  /**
26
26
  * Synchronous variant of the `init` method with some limitations:
27
27
  * - database connection will be established when you first interact with the database (or you can use `orm.connect()` explicitly)
@@ -29,7 +29,6 @@ export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver,
29
29
  * - no support for folder based discovery
30
30
  * - no check for mismatched package versions
31
31
  */
32
- static initSync<D extends IDatabaseDriver = IDatabaseDriver, EM extends EntityManager = D[typeof EntityManagerType] & EntityManager, Entities extends (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[]>(options: Options<D, EM, Entities>): MikroORM<D, EM, Entities>;
33
32
  constructor(options: Options<Driver, EM>);
34
33
  /**
35
34
  * Connects to the database.
package/MikroORM.js CHANGED
@@ -23,22 +23,14 @@ export class MikroORM {
23
23
  * If you omit the `options` parameter, your CLI config will be used.
24
24
  */
25
25
  static async init(options) {
26
- ConfigurationLoader.registerDotenv(options);
27
- const coreVersion = ConfigurationLoader.checkPackageVersion();
28
- const env = await ConfigurationLoader.loadEnvironmentVars();
26
+ /* v8 ignore next 3 */
29
27
  if (!options) {
30
- const configPathFromArg = ConfigurationLoader.configPathsFromArg();
31
- const config = (await ConfigurationLoader.getConfiguration(process.env.MIKRO_ORM_CONTEXT_NAME ?? 'default', configPathFromArg ?? ConfigurationLoader.getConfigPaths()));
32
- options = config.getAll();
33
- if (configPathFromArg) {
34
- config.getLogger().warn('deprecated', 'Path for config file was inferred from the command line arguments. Instead, you should set the MIKRO_ORM_CLI_CONFIG environment variable to specify the path, or if you really must use the command line arguments, import the config manually based on them, and pass it to init.', { label: 'D0001' });
35
- }
36
- }
37
- options = Utils.mergeConfig(options, env);
38
- if ('DRIVER' in this && !options.driver) {
39
- options.driver = this.DRIVER;
28
+ throw new Error(`options parameter is required`);
40
29
  }
41
- const orm = new MikroORM(options);
30
+ const coreVersion = ConfigurationLoader.checkPackageVersion();
31
+ options.discovery ??= {};
32
+ options.discovery.skipSyncDiscovery ??= true;
33
+ const orm = new this(options);
42
34
  orm.logger.log('info', `MikroORM version: ${colors.green(coreVersion)}`);
43
35
  // we need to allow global context here as we are not in a scope of requests yet
44
36
  const allowGlobalContext = orm.config.get('allowGlobalContext');
@@ -46,15 +38,6 @@ export class MikroORM {
46
38
  await orm.discoverEntities();
47
39
  orm.config.set('allowGlobalContext', allowGlobalContext);
48
40
  orm.driver.getPlatform().init(orm);
49
- if (orm.config.get('connect')) {
50
- await orm.connect();
51
- }
52
- for (const extension of orm.config.get('extensions')) {
53
- extension.register(orm);
54
- }
55
- if (orm.config.get('connect') && orm.config.get('ensureIndexes')) {
56
- await orm.getSchemaGenerator().ensureIndexes();
57
- }
58
41
  return orm;
59
42
  }
60
43
  /**
@@ -64,26 +47,10 @@ export class MikroORM {
64
47
  * - no support for folder based discovery
65
48
  * - no check for mismatched package versions
66
49
  */
67
- static initSync(options) {
50
+ constructor(options) {
68
51
  ConfigurationLoader.registerDotenv(options);
69
52
  const env = ConfigurationLoader.loadEnvironmentVarsSync();
70
53
  options = Utils.merge(options, env);
71
- if ('DRIVER' in this && !options.driver) {
72
- options.driver = this.DRIVER;
73
- }
74
- const orm = new MikroORM(options);
75
- // we need to allow global context here as we are not in a scope of requests yet
76
- const allowGlobalContext = orm.config.get('allowGlobalContext');
77
- orm.config.set('allowGlobalContext', true);
78
- orm.discoverEntitiesSync();
79
- orm.config.set('allowGlobalContext', allowGlobalContext);
80
- orm.driver.getPlatform().init(orm);
81
- for (const extension of orm.config.get('extensions')) {
82
- extension.register(orm);
83
- }
84
- return orm;
85
- }
86
- constructor(options) {
87
54
  this.config = new Configuration(options);
88
55
  const discovery = this.config.get('discovery');
89
56
  if (discovery.disableDynamicFileAccess) {
@@ -94,25 +61,23 @@ export class MikroORM {
94
61
  this.driver = this.config.getDriver();
95
62
  this.logger = this.config.getLogger();
96
63
  this.discovery = new MetadataDiscovery(new MetadataStorage(), this.driver.getPlatform(), this.config);
64
+ if (!discovery.skipSyncDiscovery) {
65
+ // we need to allow global context here as we are not in a scope of requests yet
66
+ const allowGlobalContext = this.config.get('allowGlobalContext');
67
+ this.config.set('allowGlobalContext', true);
68
+ this.discoverEntitiesSync();
69
+ this.config.set('allowGlobalContext', allowGlobalContext);
70
+ this.driver.getPlatform().init(this);
71
+ }
72
+ for (const extension of this.config.get('extensions')) {
73
+ extension.register(this);
74
+ }
97
75
  }
98
76
  /**
99
77
  * Connects to the database.
100
78
  */
101
79
  async connect() {
102
- const connection = await this.driver.connect();
103
- const clientUrl = connection.getClientUrl();
104
- const dbName = this.config.get('dbName');
105
- const db = dbName + (clientUrl ? ' on ' + clientUrl : '');
106
- if (this.config.get('ensureDatabase')) {
107
- const options = this.config.get('ensureDatabase');
108
- await this.schema.ensureDatabase(typeof options === 'boolean' ? {} : { ...options, forceCheck: true });
109
- }
110
- if (await this.isConnected()) {
111
- this.logger.log('info', `MikroORM successfully connected to database ${colors.green(db)}`);
112
- }
113
- else {
114
- this.logger.error('info', `MikroORM failed to connect to database ${db}`);
115
- }
80
+ await this.driver.connect();
116
81
  return this.driver;
117
82
  }
118
83
  /**
@@ -141,9 +106,7 @@ export class MikroORM {
141
106
  * Closes the database connection.
142
107
  */
143
108
  async close(force = false) {
144
- if (await this.isConnected()) {
145
- await this.driver.close(force);
146
- }
109
+ await this.driver.close(force);
147
110
  if (this.config.getMetadataCacheAdapter()?.close) {
148
111
  await this.config.getMetadataCacheAdapter().close();
149
112
  }
@@ -166,7 +129,7 @@ export class MikroORM {
166
129
  this.createEntityManager();
167
130
  }
168
131
  discoverEntitiesSync() {
169
- this.metadata = this.discovery.discoverSync(this.config.get('preferTs'));
132
+ this.metadata = this.discovery.discoverSync();
170
133
  this.createEntityManager();
171
134
  }
172
135
  createEntityManager() {
@@ -17,7 +17,9 @@ export declare abstract class Connection {
17
17
  /**
18
18
  * Establishes connection to database
19
19
  */
20
- abstract connect(): void | Promise<void>;
20
+ abstract connect(options?: {
21
+ skipOnConnect?: boolean;
22
+ }): void | Promise<void>;
21
23
  /**
22
24
  * Are we connected to the database
23
25
  */
@@ -37,9 +39,10 @@ export declare abstract class Connection {
37
39
  */
38
40
  close(force?: boolean): Promise<void>;
39
41
  /**
40
- * Ensure the connection exists, this is used to support lazy connect when using `MikroORM.initSync()`
42
+ * Ensure the connection exists, this is used to support lazy connect when using `new MikroORM()` instead of the async `init` method.
41
43
  */
42
44
  ensureConnection(): Promise<void>;
45
+ protected onConnect(): Promise<void>;
43
46
  transactional<T>(cb: (trx: Transaction) => Promise<T>, options?: {
44
47
  isolationLevel?: IsolationLevel;
45
48
  readOnly?: boolean;
@@ -58,7 +61,6 @@ export declare abstract class Connection {
58
61
  rollback(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
59
62
  abstract execute<T>(query: string, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<QueryResult<T> | any | any[]>;
60
63
  getConnectionOptions(): ConnectionConfig;
61
- getClientUrl(): string;
62
64
  setMetadata(metadata: MetadataStorage): void;
63
65
  setPlatform(platform: Platform): void;
64
66
  getPlatform(): Platform;
@@ -33,13 +33,25 @@ export class Connection {
33
33
  .forEach(k => delete this.options[k]);
34
34
  }
35
35
  /**
36
- * Ensure the connection exists, this is used to support lazy connect when using `MikroORM.initSync()`
36
+ * Ensure the connection exists, this is used to support lazy connect when using `new MikroORM()` instead of the async `init` method.
37
37
  */
38
38
  async ensureConnection() {
39
39
  if (!this.connected) {
40
40
  await this.connect();
41
41
  }
42
42
  }
43
+ async onConnect() {
44
+ const schemaGenerator = this.config.getExtension('@mikro-orm/schema-generator');
45
+ if (this.type === 'write' && schemaGenerator) {
46
+ if (this.config.get('ensureDatabase')) {
47
+ const options = this.config.get('ensureDatabase');
48
+ await schemaGenerator.ensureDatabase(typeof options === 'boolean' ? {} : { ...options, forceCheck: true });
49
+ }
50
+ if (this.config.get('ensureIndexes')) {
51
+ await schemaGenerator.ensureIndexes();
52
+ }
53
+ }
54
+ }
43
55
  async transactional(cb, options) {
44
56
  throw new Error(`Transactions are not supported by current driver`);
45
57
  }
@@ -67,7 +79,7 @@ export class Connection {
67
79
  }
68
80
  }
69
81
  else {
70
- const url = new URL(this.config.getClientUrl());
82
+ const url = new URL(this.config.get('clientUrl'));
71
83
  this.options.host = ret.host = this.options.host ?? this.config.get('host', decodeURIComponent(url.hostname));
72
84
  this.options.port = ret.port = this.options.port ?? this.config.get('port', +url.port);
73
85
  this.options.user = ret.user = this.options.user ?? this.config.get('user', decodeURIComponent(url.username));
@@ -76,15 +88,6 @@ export class Connection {
76
88
  }
77
89
  return ret;
78
90
  }
79
- getClientUrl() {
80
- const options = this.getConnectionOptions();
81
- const url = new URL(this.config.getClientUrl(true));
82
- const password = options.password ? ':*****' : '';
83
- const schema = options.schema && options.schema !== this.platform.getDefaultSchemaName()
84
- ? `?schema=${options.schema}`
85
- : '';
86
- return `${url.protocol}//${options.user}${password}@${options.host}:${options.port}${schema}`;
87
- }
88
91
  setMetadata(metadata) {
89
92
  this.metadata = metadata;
90
93
  }
@@ -113,12 +113,6 @@ export interface PropertyOptions<Owner> {
113
113
  * Enable `ScalarReference` wrapper for lazy values. Use this in combination with `lazy: true` to have a type-safe accessor object in place of the value.
114
114
  */
115
115
  ref?: boolean;
116
- /**
117
- * Set false to disable change tracking on a property level.
118
- *
119
- * @see https://mikro-orm.io/docs/unit-of-work#change-tracking-and-performance-considerations
120
- */
121
- trackChanges?: boolean;
122
116
  /**
123
117
  * Set to true to omit the property when {@link https://mikro-orm.io/docs/serializing Serializing}.
124
118
  */
@@ -36,8 +36,12 @@ export declare abstract class DatabaseDriver<C extends Connection> implements ID
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;
@@ -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') {
@@ -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
@@ -16,7 +16,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
16
16
  toArray<TT extends T>(): EntityDTO<TT>[];
17
17
  toJSON(): EntityDTO<T>[];
18
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>)[]): void;
19
+ add(entity: T | Reference<T> | Iterable<T | Reference<T>>, ...entities: (T | Reference<T>)[]): number;
20
20
  /**
21
21
  * @internal
22
22
  */
@@ -33,7 +33,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
33
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`,
34
34
  * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
35
35
  */
36
- 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;
37
37
  /**
38
38
  * Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
39
39
  * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
@@ -61,15 +61,18 @@ export class ArrayCollection {
61
61
  }
62
62
  add(entity, ...entities) {
63
63
  entities = Utils.asArray(entity).concat(entities);
64
+ let added = 0;
64
65
  for (const item of entities) {
65
66
  const entity = Reference.unwrapReference(item);
66
67
  if (!this.contains(entity, false)) {
67
68
  this.incrementCount(1);
68
69
  this[this.items.size] = entity;
69
70
  this.items.add(entity);
71
+ added++;
70
72
  this.propagate(entity, 'add');
71
73
  }
72
74
  }
75
+ return added;
73
76
  }
74
77
  /**
75
78
  * @internal
@@ -127,7 +130,7 @@ export class ArrayCollection {
127
130
  */
128
131
  remove(entity, ...entities) {
129
132
  entities = Utils.asArray(entity).concat(entities);
130
- let modified = false;
133
+ let removed = 0;
131
134
  for (const item of entities) {
132
135
  if (!item) {
133
136
  continue;
@@ -137,12 +140,13 @@ export class ArrayCollection {
137
140
  this.incrementCount(-1);
138
141
  delete this[this.items.size]; // remove last item
139
142
  this.propagate(entity, 'remove');
140
- modified = true;
143
+ removed++;
141
144
  }
142
145
  }
143
- if (modified) {
146
+ if (removed > 0) {
144
147
  Object.assign(this, [...this.items]); // reassign array access
145
148
  }
149
+ return removed;
146
150
  }
147
151
  /**
148
152
  * Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
@@ -156,6 +160,7 @@ export class ArrayCollection {
156
160
  this.snapshot = undefined;
157
161
  }
158
162
  this.remove(this.items);
163
+ this.setDirty();
159
164
  }
160
165
  /**
161
166
  * @internal
@@ -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
  }
@@ -39,11 +39,11 @@ export declare class Collection<T extends object, O extends object = object> ext
39
39
  */
40
40
  getItems(check?: boolean): T[];
41
41
  toJSON<TT extends T>(): EntityDTO<TT>[];
42
- 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;
43
43
  /**
44
44
  * @inheritDoc
45
45
  */
46
- 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;
47
47
  contains<TT extends T>(item: TT | Reference<TT>, check?: boolean): boolean;
48
48
  count(): number;
49
49
  isEmpty(): boolean;
@@ -1,7 +1,7 @@
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
7
  import { QueryHelper } from '../utils/QueryHelper.js';
@@ -117,30 +117,33 @@ export class Collection extends ArrayCollection {
117
117
  entities = Utils.asArray(entity).concat(entities);
118
118
  const unwrapped = entities.map(i => Reference.unwrapReference(i));
119
119
  unwrapped.forEach(entity => this.validateItemType(entity));
120
- this.modify('add', unwrapped);
120
+ const added = this.modify('add', unwrapped);
121
121
  this.cancelOrphanRemoval(unwrapped);
122
+ return added;
122
123
  }
123
124
  /**
124
125
  * @inheritDoc
125
126
  */
126
127
  remove(entity, ...entities) {
127
128
  if (entity instanceof Function) {
129
+ let removed = 0;
128
130
  for (const item of this.items) {
129
131
  if (entity(item)) {
130
- this.remove(item);
132
+ removed += this.remove(item);
131
133
  }
132
134
  }
133
- return;
135
+ return removed;
134
136
  }
135
137
  entities = Utils.asArray(entity).concat(entities);
136
138
  const unwrapped = entities.map(i => Reference.unwrapReference(i));
137
- this.modify('remove', unwrapped);
139
+ const removed = this.modify('remove', unwrapped);
138
140
  const em = this.getEntityManager(unwrapped, false);
139
141
  if (this.property.orphanRemoval && em) {
140
142
  for (const item of unwrapped) {
141
143
  em.getUnitOfWork().scheduleOrphanRemoval(item);
142
144
  }
143
145
  }
146
+ return removed;
144
147
  }
145
148
  contains(item, check = true) {
146
149
  if (check) {
@@ -267,8 +270,10 @@ export class Collection extends ArrayCollection {
267
270
  getEntityManager(items = [], required = true) {
268
271
  const wrapped = helper(this.owner);
269
272
  let em = wrapped.__em;
273
+ // console.log('wat 1', em, this.owner);
270
274
  if (!em) {
271
275
  for (const i of items) {
276
+ // console.log('wat 2', i, i && helper(i).__em);
272
277
  if (i && helper(i).__em) {
273
278
  em = helper(i).__em;
274
279
  break;
@@ -325,8 +330,14 @@ export class Collection extends ArrayCollection {
325
330
  this.checkInitialized();
326
331
  }
327
332
  this.validateModification(items);
328
- super[method](items);
329
- this.setDirty();
333
+ const modified = super[method](items);
334
+ if (modified > 0) {
335
+ this.setDirty();
336
+ }
337
+ if (this.property.kind === ReferenceKind.ONE_TO_MANY && (method === 'add' || !this.property.orphanRemoval)) {
338
+ this.getEntityManager(items, false)?.persist(items);
339
+ }
340
+ return modified;
330
341
  }
331
342
  checkInitialized() {
332
343
  if (!this.isInitialized()) {
@@ -84,7 +84,6 @@ export class EntityFactory {
84
84
  else {
85
85
  this.hydrate(entity, meta2, data, options);
86
86
  }
87
- wrapped.__touched = false;
88
87
  if (exists && meta.discriminatorColumn && !(entity instanceof meta2.class)) {
89
88
  Object.setPrototypeOf(entity, meta2.prototype);
90
89
  }
@@ -161,7 +160,6 @@ export class EntityFactory {
161
160
  }
162
161
  });
163
162
  this.unitOfWork.normalizeEntityData(meta, originalEntityData);
164
- helper(entity).__touched = false;
165
163
  }
166
164
  createReference(entityName, id, options = {}) {
167
165
  options.convertCustomTypes ??= true;
@@ -87,7 +87,7 @@ export class EntityHelper {
87
87
  });
88
88
  return;
89
89
  }
90
- if (prop.inherited || prop.primary || prop.accessor || prop.persist === false || prop.trackChanges === false || prop.embedded || isCollection) {
90
+ if (prop.inherited || prop.primary || prop.accessor || prop.persist === false || prop.embedded || isCollection) {
91
91
  return;
92
92
  }
93
93
  Object.defineProperty(meta.prototype, prop.name, {
@@ -98,13 +98,11 @@ export class EntityHelper {
98
98
  },
99
99
  set(val) {
100
100
  this.__helper.__data[prop.name] = val;
101
- this.__helper.__touched = !this.__helper.hydrator.isRunning();
102
101
  },
103
102
  enumerable: true,
104
103
  configurable: true,
105
104
  });
106
105
  this.__helper.__data[prop.name] = val;
107
- this.__helper.__touched = !this.__helper.hydrator.isRunning();
108
106
  },
109
107
  configurable: true,
110
108
  });
@@ -114,7 +112,7 @@ export class EntityHelper {
114
112
  // @ts-ignore
115
113
  meta.prototype[inspect.custom] ??= function (depth = 2) {
116
114
  const object = {};
117
- const keys = new Set(Utils.keys(this)); // .sort((a, b) => (meta.propertyOrder.get(a) ?? 0) - (meta.propertyOrder.get(b) ?? 0));
115
+ const keys = new Set(Utils.keys(this));
118
116
  for (const prop of meta.props) {
119
117
  if (keys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
120
118
  object[prop.name] = this[prop.name];
@@ -165,9 +163,6 @@ export class EntityHelper {
165
163
  if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
166
164
  wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta, true);
167
165
  }
168
- else {
169
- wrapped.__touched = !hydrator.isRunning();
170
- }
171
166
  EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
172
167
  },
173
168
  enumerable: true,
@@ -11,7 +11,6 @@ import { type SerializeOptions } from '../serialization/EntitySerializer.js';
11
11
  import type { FindOneOptions, LoadHint } from '../drivers/IDatabaseDriver.js';
12
12
  export declare class WrappedEntity<Entity extends object> {
13
13
  __initialized: boolean;
14
- __touched: boolean;
15
14
  __populated?: boolean;
16
15
  __managed?: boolean;
17
16
  __onLoadFired?: boolean;
@@ -41,7 +40,6 @@ export declare class WrappedEntity<Entity extends object> {
41
40
  private readonly pkGetterConverted?;
42
41
  constructor(entity: Entity, hydrator: IHydrator, pkGetter?: (e: Entity) => Primary<Entity>, pkSerializer?: (e: Entity) => string, pkGetterConverted?: (e: Entity) => Primary<Entity>);
43
42
  isInitialized(): boolean;
44
- isTouched(): boolean;
45
43
  isManaged(): boolean;
46
44
  populated(populated?: boolean | undefined): void;
47
45
  setSerializationContext<Hint extends string = never, Fields extends string = '*', Exclude extends string = never>(options: LoadHint<Entity, Hint, Fields, Exclude>): void;
@@ -15,7 +15,6 @@ export class WrappedEntity {
15
15
  this.pkSerializer = pkSerializer;
16
16
  this.pkGetterConverted = pkGetterConverted;
17
17
  this.__initialized = true;
18
- this.__touched = false;
19
18
  this.__serializationContext = {};
20
19
  this.__loadedProperties = new Set();
21
20
  this.__data = {};
@@ -24,9 +23,6 @@ export class WrappedEntity {
24
23
  isInitialized() {
25
24
  return this.__initialized;
26
25
  }
27
- isTouched() {
28
- return this.__touched;
29
- }
30
26
  isManaged() {
31
27
  return !!this.__managed;
32
28
  }