@mikro-orm/core 7.0.0-rc.2 → 7.0.0

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 (114) hide show
  1. package/EntityManager.d.ts +4 -16
  2. package/EntityManager.js +248 -181
  3. package/MikroORM.d.ts +4 -6
  4. package/MikroORM.js +24 -24
  5. package/README.md +5 -4
  6. package/cache/FileCacheAdapter.d.ts +1 -5
  7. package/cache/FileCacheAdapter.js +22 -24
  8. package/cache/GeneratedCacheAdapter.d.ts +1 -1
  9. package/cache/GeneratedCacheAdapter.js +6 -6
  10. package/cache/MemoryCacheAdapter.d.ts +1 -2
  11. package/cache/MemoryCacheAdapter.js +8 -8
  12. package/cache/index.d.ts +1 -1
  13. package/cache/index.js +0 -1
  14. package/connections/Connection.d.ts +1 -0
  15. package/connections/Connection.js +43 -14
  16. package/drivers/DatabaseDriver.d.ts +0 -2
  17. package/drivers/DatabaseDriver.js +28 -12
  18. package/drivers/IDatabaseDriver.d.ts +43 -0
  19. package/entity/Collection.d.ts +1 -9
  20. package/entity/Collection.js +124 -108
  21. package/entity/EntityAssigner.js +23 -11
  22. package/entity/EntityFactory.d.ts +1 -8
  23. package/entity/EntityFactory.js +79 -59
  24. package/entity/EntityHelper.js +25 -16
  25. package/entity/EntityLoader.d.ts +1 -3
  26. package/entity/EntityLoader.js +90 -60
  27. package/entity/Reference.d.ts +2 -3
  28. package/entity/Reference.js +48 -19
  29. package/entity/WrappedEntity.d.ts +4 -2
  30. package/entity/WrappedEntity.js +5 -1
  31. package/entity/defineEntity.d.ts +42 -85
  32. package/entity/utils.js +28 -26
  33. package/entity/validators.js +2 -1
  34. package/enums.d.ts +2 -1
  35. package/enums.js +13 -17
  36. package/errors.d.ts +11 -11
  37. package/errors.js +8 -8
  38. package/events/EventManager.d.ts +1 -4
  39. package/events/EventManager.js +26 -23
  40. package/events/index.d.ts +1 -1
  41. package/events/index.js +0 -1
  42. package/exceptions.js +9 -2
  43. package/hydration/ObjectHydrator.d.ts +1 -2
  44. package/hydration/ObjectHydrator.js +41 -27
  45. package/index.d.ts +1 -1
  46. package/index.js +1 -1
  47. package/logging/DefaultLogger.js +6 -7
  48. package/logging/Logger.d.ts +2 -1
  49. package/logging/colors.js +2 -5
  50. package/logging/index.d.ts +1 -1
  51. package/logging/index.js +0 -1
  52. package/metadata/EntitySchema.d.ts +3 -3
  53. package/metadata/EntitySchema.js +12 -2
  54. package/metadata/MetadataDiscovery.d.ts +1 -9
  55. package/metadata/MetadataDiscovery.js +251 -179
  56. package/metadata/MetadataProvider.js +26 -1
  57. package/metadata/MetadataStorage.d.ts +1 -5
  58. package/metadata/MetadataStorage.js +37 -39
  59. package/metadata/MetadataValidator.js +20 -5
  60. package/metadata/discover-entities.js +1 -1
  61. package/metadata/index.d.ts +1 -1
  62. package/metadata/index.js +0 -1
  63. package/metadata/types.d.ts +2 -2
  64. package/naming-strategy/AbstractNamingStrategy.js +6 -3
  65. package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
  66. package/naming-strategy/index.d.ts +1 -1
  67. package/naming-strategy/index.js +0 -1
  68. package/not-supported.js +5 -1
  69. package/package.json +38 -38
  70. package/platforms/Platform.d.ts +24 -1
  71. package/platforms/Platform.js +106 -27
  72. package/serialization/EntitySerializer.js +8 -4
  73. package/serialization/EntityTransformer.js +4 -1
  74. package/serialization/SerializationContext.d.ts +4 -8
  75. package/serialization/SerializationContext.js +21 -16
  76. package/types/UuidType.d.ts +2 -0
  77. package/types/UuidType.js +14 -2
  78. package/types/index.d.ts +2 -1
  79. package/typings.d.ts +35 -24
  80. package/typings.js +9 -9
  81. package/unit-of-work/ChangeSet.js +4 -4
  82. package/unit-of-work/ChangeSetComputer.d.ts +1 -6
  83. package/unit-of-work/ChangeSetComputer.js +29 -27
  84. package/unit-of-work/ChangeSetPersister.d.ts +1 -9
  85. package/unit-of-work/ChangeSetPersister.js +63 -58
  86. package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
  87. package/unit-of-work/CommitOrderCalculator.js +17 -15
  88. package/unit-of-work/IdentityMap.d.ts +2 -5
  89. package/unit-of-work/IdentityMap.js +18 -18
  90. package/unit-of-work/UnitOfWork.d.ts +12 -20
  91. package/unit-of-work/UnitOfWork.js +228 -191
  92. package/utils/AbstractMigrator.d.ts +2 -2
  93. package/utils/AbstractMigrator.js +10 -12
  94. package/utils/AbstractSchemaGenerator.js +2 -1
  95. package/utils/AsyncContext.js +1 -1
  96. package/utils/Configuration.d.ts +90 -189
  97. package/utils/Configuration.js +97 -77
  98. package/utils/Cursor.d.ts +3 -3
  99. package/utils/Cursor.js +8 -6
  100. package/utils/DataloaderUtils.js +15 -12
  101. package/utils/EntityComparator.d.ts +8 -15
  102. package/utils/EntityComparator.js +100 -92
  103. package/utils/QueryHelper.d.ts +16 -1
  104. package/utils/QueryHelper.js +108 -50
  105. package/utils/RawQueryFragment.d.ts +4 -4
  106. package/utils/RawQueryFragment.js +3 -2
  107. package/utils/TransactionManager.js +3 -3
  108. package/utils/Utils.d.ts +2 -2
  109. package/utils/Utils.js +39 -32
  110. package/utils/clone.js +5 -0
  111. package/utils/env-vars.js +6 -5
  112. package/utils/fs-utils.d.ts +3 -17
  113. package/utils/fs-utils.js +2 -5
  114. package/utils/upsert-utils.js +7 -4
@@ -110,6 +110,22 @@ export interface FindOptions<Entity, Hint extends string = never, Fields extends
110
110
  * when nesting the condition. This is used for implementation of joined filters.
111
111
  */
112
112
  populateFilter?: ObjectQuery<Entity>;
113
+ /**
114
+ * Index-friendly alternative to `$or` for conditions that span joined relations.
115
+ * Each array element becomes an independent branch combined via `UNION ALL` subquery:
116
+ * `WHERE pk IN (branch_1 UNION ALL branch_2 ... branch_N)`.
117
+ * The database plans each branch independently, enabling per-table index usage
118
+ * (e.g. GIN trigram indexes for fuzzy search across related entities).
119
+ * sql only
120
+ */
121
+ unionWhere?: ObjectQuery<Entity>[];
122
+ /**
123
+ * Strategy for combining `unionWhere` branches.
124
+ * - `'union-all'` (default) — skips deduplication, faster for most use cases.
125
+ * - `'union'` — deduplicates rows between branches; useful when branch overlap is very high.
126
+ * sql only
127
+ */
128
+ unionWhereStrategy?: 'union-all' | 'union';
113
129
  /** Used for ordering of the populate queries. If not specified, the value of `options.orderBy` is used. */
114
130
  populateOrderBy?: OrderDefinition<Entity>;
115
131
  /** Per-relation overrides for populate loading behavior. Keys are populate paths (same as used in `populate`). */
@@ -199,6 +215,13 @@ export interface NativeInsertUpdateOptions<T> {
199
215
  /** `nativeUpdate()` only option */
200
216
  upsert?: boolean;
201
217
  loggerContext?: LogContext;
218
+ /** sql only */
219
+ unionWhere?: ObjectQuery<T>[];
220
+ /** sql only */
221
+ unionWhereStrategy?: 'union-all' | 'union';
222
+ filters?: FilterOptions;
223
+ /** @internal */
224
+ em?: EntityManager;
202
225
  }
203
226
  export interface NativeInsertUpdateManyOptions<T> extends NativeInsertUpdateOptions<T> {
204
227
  processCollections?: boolean;
@@ -223,6 +246,10 @@ export interface CountOptions<T extends object, P extends string = never> {
223
246
  populate?: Populate<T, P>;
224
247
  populateWhere?: ObjectQuery<T> | PopulateHint | `${PopulateHint}`;
225
248
  populateFilter?: ObjectQuery<T>;
249
+ /** @see FindOptions.unionWhere */
250
+ unionWhere?: ObjectQuery<T>[];
251
+ /** @see FindOptions.unionWhereStrategy */
252
+ unionWhereStrategy?: 'union-all' | 'union';
226
253
  ctx?: Transaction;
227
254
  connectionType?: ConnectionType;
228
255
  flushMode?: FlushMode | `${FlushMode}`;
@@ -245,12 +272,28 @@ export interface UpdateOptions<T> {
245
272
  filters?: FilterOptions;
246
273
  schema?: string;
247
274
  ctx?: Transaction;
275
+ /** sql only */
276
+ unionWhere?: ObjectQuery<T>[];
277
+ /** sql only */
278
+ unionWhereStrategy?: 'union-all' | 'union';
248
279
  }
249
280
  export interface DeleteOptions<T> extends DriverMethodOptions {
250
281
  filters?: FilterOptions;
282
+ /** sql only */
283
+ unionWhere?: ObjectQuery<T>[];
284
+ /** sql only */
285
+ unionWhereStrategy?: 'union-all' | 'union';
286
+ /** @internal */
287
+ em?: EntityManager;
251
288
  }
252
289
  export interface NativeDeleteOptions<T> extends DriverMethodOptions {
253
290
  filters?: FilterOptions;
291
+ /** sql only */
292
+ unionWhere?: ObjectQuery<T>[];
293
+ /** sql only */
294
+ unionWhereStrategy?: 'union-all' | 'union';
295
+ /** @internal */
296
+ em?: EntityManager;
254
297
  }
255
298
  export interface LockOptions extends DriverMethodOptions {
256
299
  lockMode?: LockMode;
@@ -9,17 +9,9 @@ export interface MatchingOptions<T extends object, P extends string = never> ext
9
9
  ctx?: Transaction;
10
10
  }
11
11
  export declare class Collection<T extends object, O extends object = object> {
12
+ #private;
12
13
  readonly owner: O;
13
14
  [k: number]: T;
14
- protected readonly items: Set<T>;
15
- protected initialized: boolean;
16
- protected dirty: boolean;
17
- protected partial: boolean;
18
- protected snapshot: T[] | undefined;
19
- private readonly?;
20
- protected _count?: number;
21
- private _property?;
22
- private _populated?;
23
15
  constructor(owner: O, items?: T[], initialized?: boolean);
24
16
  /**
25
17
  * Creates new Collection instance, assigns it to the owning entity and sets the items to it (propagating them to their inverse sides)
@@ -7,24 +7,24 @@ import { QueryHelper } from '../utils/QueryHelper.js';
7
7
  import { inspect } from '../logging/inspect.js';
8
8
  export class Collection {
9
9
  owner;
10
- items = new Set();
11
- initialized = true;
12
- dirty = false;
13
- partial = false; // mark partially loaded collections, propagation is disabled for those
14
- snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
15
- readonly;
16
- _count;
17
- _property;
18
- _populated;
10
+ #items = new Set();
11
+ #initialized = true;
12
+ #dirty = false;
13
+ #partial = false; // mark partially loaded collections, propagation is disabled for those
14
+ #snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
15
+ #readonly;
16
+ #count;
17
+ #property;
18
+ #populated;
19
19
  constructor(owner, items, initialized = true) {
20
20
  this.owner = owner;
21
21
  /* v8 ignore next */
22
22
  if (items) {
23
23
  let i = 0;
24
- this.items = new Set(items);
25
- this.items.forEach(item => this[i++] = item);
24
+ this.#items = new Set(items);
25
+ this.#items.forEach(item => (this[i++] = item));
26
26
  }
27
- this.initialized = !!items || initialized;
27
+ this.#initialized = !!items || initialized;
28
28
  }
29
29
  /**
30
30
  * Creates new Collection instance, assigns it to the owning entity and sets the items to it (propagating them to their inverse sides)
@@ -44,9 +44,9 @@ export class Collection {
44
44
  */
45
45
  async load(options = {}) {
46
46
  if (this.isInitialized(true) && !options.refresh) {
47
- const em = this.getEntityManager(this.items, false);
47
+ const em = this.getEntityManager(this.#items, false);
48
48
  options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
49
- await em?.populate(this.items, options.populate, options);
49
+ await em?.populate(this.#items, options.populate, options);
50
50
  this.setSerializationContext(options);
51
51
  }
52
52
  else {
@@ -58,7 +58,7 @@ export class Collection {
58
58
  helper(this.owner).setSerializationContext({
59
59
  populate: Array.isArray(options.populate)
60
60
  ? options.populate.map(hint => `${this.property.name}.${hint}`)
61
- : options.populate ?? [this.property.name],
61
+ : (options.populate ?? [this.property.name]),
62
62
  });
63
63
  }
64
64
  /**
@@ -75,17 +75,19 @@ export class Collection {
75
75
  async loadCount(options = {}) {
76
76
  options = typeof options === 'boolean' ? { refresh: options } : options;
77
77
  const { refresh, where, ...countOptions } = options;
78
- if (!refresh && !where && this._count != null) {
79
- return this._count;
78
+ if (!refresh && !where && this.#count != null) {
79
+ return this.#count;
80
80
  }
81
81
  const em = this.getEntityManager();
82
- if (!em.getPlatform().usesPivotTable() && this.property.kind === ReferenceKind.MANY_TO_MANY && this.property.owner) {
83
- return this._count = this.length;
82
+ if (!em.getPlatform().usesPivotTable() &&
83
+ this.property.kind === ReferenceKind.MANY_TO_MANY &&
84
+ this.property.owner) {
85
+ return (this.#count = this.length);
84
86
  }
85
87
  const cond = this.createLoadCountCondition(where ?? {});
86
88
  const count = await em.count(this.property.targetMeta.class, cond, countOptions);
87
89
  if (!where) {
88
- this._count = count;
90
+ this.#count = count;
89
91
  }
90
92
  return count;
91
93
  }
@@ -96,22 +98,24 @@ export class Collection {
96
98
  if (this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
97
99
  // M:N via pivot table bypasses em.find(), so merge all 3 levels here
98
100
  opts.orderBy = QueryHelper.mergeOrderBy(opts.orderBy, this.property.orderBy, this.property.targetMeta?.orderBy);
99
- options.populate = await em.preparePopulate(this.property.targetMeta.class, options);
100
- const cond = await em.applyFilters(this.property.targetMeta.class, where, options.filters ?? {}, 'read');
101
- const map = await em.getDriver().loadFromPivotTable(this.property, [helper(this.owner).__primaryKeys], cond, opts.orderBy, ctx, options);
101
+ options.populate = (await em.preparePopulate(this.property.targetMeta.class, options));
102
+ const cond = (await em.applyFilters(this.property.targetMeta.class, where, options.filters ?? {}, 'read'));
103
+ const map = await em
104
+ .getDriver()
105
+ .loadFromPivotTable(this.property, [helper(this.owner).__primaryKeys], cond, opts.orderBy, ctx, options);
102
106
  items = map[helper(this.owner).getSerializedPrimaryKey()].map((item) => em.merge(this.property.targetMeta.class, item, { convertCustomTypes: true }));
103
107
  await em.populate(items, options.populate, options);
104
108
  }
105
109
  else {
106
110
  // em.find() merges entity-level orderBy, so only merge runtime + relation here
107
111
  opts.orderBy = QueryHelper.mergeOrderBy(opts.orderBy, this.property.orderBy);
108
- items = await em.find(this.property.targetMeta.class, this.createCondition(where), opts);
112
+ items = (await em.find(this.property.targetMeta.class, this.createCondition(where), opts));
109
113
  }
110
114
  if (options.store) {
111
115
  this.hydrate(items, true);
112
116
  this.setSerializationContext(options);
113
117
  this.populated();
114
- this.readonly = true;
118
+ this.#readonly = true;
115
119
  }
116
120
  return items;
117
121
  }
@@ -122,7 +126,7 @@ export class Collection {
122
126
  if (check) {
123
127
  this.checkInitialized();
124
128
  }
125
- return [...this.items];
129
+ return [...this.#items];
126
130
  }
127
131
  toJSON() {
128
132
  if (!this.isInitialized()) {
@@ -140,10 +144,10 @@ export class Collection {
140
144
  const entity = Reference.unwrapReference(item);
141
145
  if (!this.contains(entity, false)) {
142
146
  this.incrementCount(1);
143
- this[this.items.size] = entity;
144
- this.items.add(entity);
147
+ this[this.#items.size] = entity;
148
+ this.#items.add(entity);
145
149
  added++;
146
- this.dirty = true;
150
+ this.#dirty = true;
147
151
  this.propagate(entity, 'add');
148
152
  }
149
153
  }
@@ -162,7 +166,7 @@ export class Collection {
162
166
  remove(entity, ...entities) {
163
167
  if (entity instanceof Function) {
164
168
  let removed = 0;
165
- for (const item of this.items) {
169
+ for (const item of this.#items) {
166
170
  if (entity(item)) {
167
171
  removed += this.remove(item);
168
172
  }
@@ -180,12 +184,12 @@ export class Collection {
180
184
  continue;
181
185
  }
182
186
  const entity = Reference.unwrapReference(item);
183
- if (this.items.delete(entity)) {
187
+ if (this.#items.delete(entity)) {
184
188
  this.incrementCount(-1);
185
- delete this[this.items.size]; // remove last item
189
+ delete this[this.#items.size]; // remove last item
186
190
  this.propagate(entity, 'remove');
187
191
  removed++;
188
- this.dirty = true;
192
+ this.#dirty = true;
189
193
  }
190
194
  if (this.property.orphanRemoval && em) {
191
195
  em.getUnitOfWork().scheduleOrphanRemoval(entity);
@@ -195,7 +199,7 @@ export class Collection {
195
199
  em.persist(entities);
196
200
  }
197
201
  if (removed > 0) {
198
- Object.assign(this, [...this.items]); // reassign array access
202
+ Object.assign(this, [...this.#items]); // reassign array access
199
203
  }
200
204
  return removed;
201
205
  }
@@ -204,11 +208,11 @@ export class Collection {
204
208
  this.checkInitialized();
205
209
  }
206
210
  const entity = Reference.unwrapReference(item);
207
- return this.items.has(entity);
211
+ return this.#items.has(entity);
208
212
  }
209
213
  count() {
210
214
  this.checkInitialized();
211
- return this.items.size;
215
+ return this.#items.size;
212
216
  }
213
217
  isEmpty() {
214
218
  this.checkInitialized();
@@ -218,18 +222,18 @@ export class Collection {
218
222
  if (!this.isInitialized(true)) {
219
223
  return false;
220
224
  }
221
- if (this._populated != null) {
222
- return this._populated;
225
+ if (this.#populated != null) {
226
+ return this.#populated;
223
227
  }
224
228
  return !!populated;
225
229
  }
226
230
  populated(populated = true) {
227
- this._populated = populated;
231
+ this.#populated = populated;
228
232
  }
229
233
  async init(options = {}) {
230
- if (this.dirty) {
231
- const items = [...this.items];
232
- this.dirty = false;
234
+ if (this.#dirty) {
235
+ const items = [...this.#items];
236
+ this.#dirty = false;
233
237
  await this.init(options);
234
238
  items.forEach(i => this.add(i));
235
239
  return this;
@@ -237,36 +241,34 @@ export class Collection {
237
241
  const em = this.getEntityManager();
238
242
  options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
239
243
  if (options.dataloader ?? [DataloaderType.ALL, DataloaderType.COLLECTION].includes(em.config.getDataloaderType())) {
240
- const order = [...this.items]; // copy order of references
244
+ const order = [...this.#items]; // copy order of references
241
245
  const orderBy = QueryHelper.mergeOrderBy(options.orderBy, this.property.orderBy, this.property.targetMeta?.orderBy);
242
246
  const customOrder = orderBy.length > 0;
243
247
  const pivotTable = this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable();
244
248
  const loader = await em.getDataLoader(pivotTable ? 'm:n' : '1:m');
245
249
  const items = await loader.load([this, { ...options, orderBy }]);
246
250
  if (this.property.kind === ReferenceKind.MANY_TO_MANY) {
247
- this.initialized = true;
248
- this.dirty = false;
251
+ this.#initialized = true;
252
+ this.#dirty = false;
249
253
  if (!customOrder) {
250
254
  this.reorderItems(items, order);
251
255
  }
252
256
  return this;
253
257
  }
254
- this.items.clear();
258
+ this.#items.clear();
255
259
  let i = 0;
256
260
  for (const item of items) {
257
- this.items.add(item);
261
+ this.#items.add(item);
258
262
  this[i++] = item;
259
263
  }
260
- this.initialized = true;
261
- this.dirty = false;
264
+ this.#initialized = true;
265
+ this.#dirty = false;
262
266
  return this;
263
267
  }
264
268
  const populate = Array.isArray(options.populate)
265
- ? options.populate.map(f => f === '*' ? f : `${this.property.name}.${f}`)
269
+ ? options.populate.map(f => (f === '*' ? f : `${this.property.name}.${f}`))
266
270
  : [`${this.property.name}${options.ref ? ':ref' : ''}`];
267
- const schema = this.property.targetMeta.schema === '*'
268
- ? helper(this.owner).__schema
269
- : undefined;
271
+ const schema = this.property.targetMeta.schema === '*' ? helper(this.owner).__schema : undefined;
270
272
  await em.populate(this.owner, populate, {
271
273
  refresh: true,
272
274
  ...options,
@@ -297,7 +299,8 @@ export class Collection {
297
299
  if (this.property.kind === ReferenceKind.ONE_TO_MANY) {
298
300
  cond[this.property.mappedBy] = helper(this.owner).getPrimaryKey();
299
301
  }
300
- else { // MANY_TO_MANY
302
+ else {
303
+ // MANY_TO_MANY
301
304
  this.createManyToManyCondition(cond);
302
305
  }
303
306
  return cond;
@@ -308,7 +311,7 @@ export class Collection {
308
311
  // we know there is at least one item as it was checked in load method
309
312
  const pk = this.property.targetMeta.primaryKeys[0];
310
313
  dict[pk] = { $in: [] };
311
- this.items.forEach(item => dict[pk].$in.push(helper(item).getPrimaryKey()));
314
+ this.#items.forEach(item => dict[pk].$in.push(helper(item).getPrimaryKey()));
312
315
  }
313
316
  else {
314
317
  dict[this.property.mappedBy] = helper(this.owner).getPrimaryKey();
@@ -350,7 +353,7 @@ export class Collection {
350
353
  }
351
354
  }
352
355
  validateModification(items) {
353
- if (this.readonly) {
356
+ if (this.#readonly) {
354
357
  throw ValidationError.cannotModifyReadonlyCollection(this.owner, this.property);
355
358
  }
356
359
  const check = (item) => {
@@ -375,7 +378,7 @@ export class Collection {
375
378
  }
376
379
  }
377
380
  toArray() {
378
- if (this.items.size === 0) {
381
+ if (this.#items.size === 0) {
379
382
  return [];
380
383
  }
381
384
  return this.map(item => wrap(item).toJSON());
@@ -386,7 +389,9 @@ export class Collection {
386
389
  if (items.length === 0) {
387
390
  return [];
388
391
  }
389
- field ??= targetMeta.compositePK ? targetMeta.primaryKeys : (targetMeta.serializedPrimaryKey ?? targetMeta.primaryKeys[0]);
392
+ field ??= targetMeta.compositePK
393
+ ? targetMeta.primaryKeys
394
+ : (targetMeta.serializedPrimaryKey ?? targetMeta.primaryKeys[0]);
390
395
  const cb = (i, f) => {
391
396
  if (Utils.isEntity(i[f], true)) {
392
397
  return wrap(i[f], true).getPrimaryKey();
@@ -406,28 +411,28 @@ export class Collection {
406
411
  addWithoutPropagation(entity) {
407
412
  if (!this.contains(entity, false)) {
408
413
  this.incrementCount(1);
409
- this[this.items.size] = entity;
410
- this.items.add(entity);
411
- this.dirty = true;
414
+ this[this.#items.size] = entity;
415
+ this.#items.add(entity);
416
+ this.#dirty = true;
412
417
  }
413
418
  }
414
419
  set(items) {
415
- if (!this.initialized) {
416
- this.initialized = true;
417
- this.snapshot = undefined;
420
+ if (!this.#initialized) {
421
+ this.#initialized = true;
422
+ this.#snapshot = undefined;
418
423
  }
419
424
  if (this.compare(Utils.asArray(items).map(item => Reference.unwrapReference(item)))) {
420
425
  return;
421
426
  }
422
- this.remove(this.items);
427
+ this.remove(this.#items);
423
428
  this.add(items);
424
429
  }
425
430
  compare(items) {
426
- if (items.length !== this.items.size) {
431
+ if (items.length !== this.#items.size) {
427
432
  return false;
428
433
  }
429
434
  let idx = 0;
430
- for (const item of this.items) {
435
+ for (const item of this.#items) {
431
436
  if (item !== items[idx++]) {
432
437
  return false;
433
438
  }
@@ -438,13 +443,13 @@ export class Collection {
438
443
  * @internal
439
444
  */
440
445
  hydrate(items, forcePropagate, partial) {
441
- for (let i = 0; i < this.items.size; i++) {
446
+ for (let i = 0; i < this.#items.size; i++) {
442
447
  delete this[i];
443
448
  }
444
- this.initialized = true;
445
- this.partial = !!partial;
446
- this.items.clear();
447
- this._count = 0;
449
+ this.#initialized = true;
450
+ this.#partial = !!partial;
451
+ this.#items.clear();
452
+ this.#count = 0;
448
453
  this.add(items);
449
454
  this.takeSnapshot(forcePropagate);
450
455
  }
@@ -455,24 +460,24 @@ export class Collection {
455
460
  * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
456
461
  */
457
462
  removeAll() {
458
- if (!this.initialized) {
459
- this.initialized = true;
460
- this.snapshot = undefined;
463
+ if (!this.#initialized) {
464
+ this.#initialized = true;
465
+ this.#snapshot = undefined;
461
466
  }
462
- this.remove(this.items);
463
- this.dirty = true;
467
+ this.remove(this.#items);
468
+ this.#dirty = true;
464
469
  }
465
470
  /**
466
471
  * @internal
467
472
  */
468
473
  removeWithoutPropagation(entity) {
469
- if (!this.items.delete(entity)) {
474
+ if (!this.#items.delete(entity)) {
470
475
  return;
471
476
  }
472
477
  this.incrementCount(-1);
473
- delete this[this.items.size];
474
- Object.assign(this, [...this.items]);
475
- this.dirty = true;
478
+ delete this[this.#items.size];
479
+ Object.assign(this, [...this.#items]);
480
+ this.#dirty = true;
476
481
  }
477
482
  /**
478
483
  * Extracts a slice of the collection items starting at position start to end (exclusive) of the collection.
@@ -481,9 +486,9 @@ export class Collection {
481
486
  slice(start = 0, end) {
482
487
  this.checkInitialized();
483
488
  let index = 0;
484
- end ??= this.items.size;
489
+ end ??= this.#items.size;
485
490
  const items = [];
486
- for (const item of this.items) {
491
+ for (const item of this.#items) {
487
492
  if (index === end) {
488
493
  break;
489
494
  }
@@ -499,7 +504,7 @@ export class Collection {
499
504
  */
500
505
  exists(cb) {
501
506
  this.checkInitialized();
502
- for (const item of this.items) {
507
+ for (const item of this.#items) {
503
508
  if (cb(item)) {
504
509
  return true;
505
510
  }
@@ -512,7 +517,7 @@ export class Collection {
512
517
  find(cb) {
513
518
  this.checkInitialized();
514
519
  let index = 0;
515
- for (const item of this.items) {
520
+ for (const item of this.#items) {
516
521
  if (cb(item, index++)) {
517
522
  return item;
518
523
  }
@@ -526,7 +531,7 @@ export class Collection {
526
531
  this.checkInitialized();
527
532
  const items = [];
528
533
  let index = 0;
529
- for (const item of this.items) {
534
+ for (const item of this.#items) {
530
535
  if (cb(item, index++)) {
531
536
  items.push(item);
532
537
  }
@@ -540,7 +545,7 @@ export class Collection {
540
545
  this.checkInitialized();
541
546
  const items = [];
542
547
  let index = 0;
543
- for (const item of this.items) {
548
+ for (const item of this.#items) {
544
549
  items.push(mapper(item, index++));
545
550
  }
546
551
  return items;
@@ -551,7 +556,7 @@ export class Collection {
551
556
  reduce(cb, initial = {}) {
552
557
  this.checkInitialized();
553
558
  let index = 0;
554
- for (const item of this.items) {
559
+ for (const item of this.#items) {
555
560
  initial = cb(initial, item, index++);
556
561
  }
557
562
  return initial;
@@ -567,10 +572,10 @@ export class Collection {
567
572
  }, {});
568
573
  }
569
574
  isInitialized(fully = false) {
570
- if (!this.initialized || !fully) {
571
- return this.initialized;
575
+ if (!this.#initialized || !fully) {
576
+ return this.#initialized;
572
577
  }
573
- for (const item of this.items) {
578
+ for (const item of this.#items) {
574
579
  if (!helper(item).__initialized) {
575
580
  return false;
576
581
  }
@@ -578,13 +583,13 @@ export class Collection {
578
583
  return true;
579
584
  }
580
585
  isDirty() {
581
- return this.dirty;
586
+ return this.#dirty;
582
587
  }
583
588
  isPartial() {
584
- return this.partial;
589
+ return this.#partial;
585
590
  }
586
591
  setDirty(dirty = true) {
587
- this.dirty = dirty;
592
+ this.#dirty = dirty;
588
593
  }
589
594
  get length() {
590
595
  return this.count();
@@ -598,10 +603,10 @@ export class Collection {
598
603
  * @internal
599
604
  */
600
605
  takeSnapshot(forcePropagate) {
601
- this.snapshot = [...this.items];
602
- this.dirty = false;
606
+ this.#snapshot = [...this.#items];
607
+ this.#dirty = false;
603
608
  if (this.property.owner || forcePropagate) {
604
- this.items.forEach(item => {
609
+ this.#items.forEach(item => {
605
610
  this.propagate(item, 'takeSnapshot');
606
611
  });
607
612
  }
@@ -610,27 +615,29 @@ export class Collection {
610
615
  * @internal
611
616
  */
612
617
  getSnapshot() {
613
- return this.snapshot;
618
+ return this.#snapshot;
614
619
  }
615
620
  /**
616
621
  * @internal
617
622
  */
618
623
  get property() {
619
- if (!this._property) {
624
+ // cannot be typed to `EntityProperty<O, T>` as it causes issues in assignability of `Loaded` type
625
+ if (!this.#property) {
620
626
  const meta = wrap(this.owner, true).__meta;
621
627
  /* v8 ignore next */
622
628
  if (!meta) {
623
629
  throw MetadataError.fromUnknownEntity(this.owner.constructor.name, 'Collection.property getter, maybe you just forgot to initialize the ORM?');
624
630
  }
625
- this._property = meta.relations.find(prop => this.owner[prop.name] === this);
631
+ this.#property = meta.relations.find(prop => this.owner[prop.name] === this);
626
632
  }
627
- return this._property;
633
+ return this.#property;
628
634
  }
629
635
  /**
630
636
  * @internal
631
637
  */
632
638
  set property(prop) {
633
- this._property = prop;
639
+ // cannot be typed to `EntityProperty<O, T>` as it causes issues in assignability of `Loaded` type
640
+ this.#property = prop;
634
641
  }
635
642
  propagate(item, method) {
636
643
  if (this.property.owner && this.property.inversedBy) {
@@ -689,22 +696,31 @@ export class Collection {
689
696
  }
690
697
  }
691
698
  incrementCount(value) {
692
- if (typeof this._count === 'number' && this.initialized) {
693
- this._count += value;
699
+ if (typeof this.#count === 'number' && this.#initialized) {
700
+ this.#count += value;
694
701
  }
695
702
  }
696
703
  /** @ignore */
697
704
  [Symbol.for('nodejs.util.inspect.custom')](depth = 2) {
698
705
  const object = { ...this };
699
- const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_lazyInitialized', '_em', 'readonly', 'partial'];
700
- hidden.forEach(k => delete object[k]);
706
+ delete object.owner;
707
+ object.initialized = this.#initialized;
708
+ object.dirty = this.#dirty;
701
709
  const ret = inspect(object, { depth });
702
710
  const name = `${this.constructor.name}<${this.property?.type ?? 'unknown'}>`;
703
711
  return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
704
712
  }
705
713
  }
706
714
  Object.defineProperties(Collection.prototype, {
707
- $: { get() { return this; } },
708
- get: { get() { return () => this; } },
715
+ $: {
716
+ get() {
717
+ return this;
718
+ },
719
+ },
720
+ get: {
721
+ get() {
722
+ return () => this;
723
+ },
724
+ },
709
725
  __collection: { value: true, enumerable: false, writable: false },
710
726
  });