@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321

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 (216) hide show
  1. package/EntityManager.d.ts +71 -63
  2. package/EntityManager.js +365 -283
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +7 -4
  6. package/cache/FileCacheAdapter.d.ts +1 -2
  7. package/cache/FileCacheAdapter.js +19 -14
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +1 -2
  11. package/cache/index.js +0 -2
  12. package/connections/Connection.d.ts +12 -5
  13. package/connections/Connection.js +37 -15
  14. package/drivers/DatabaseDriver.d.ts +25 -18
  15. package/drivers/DatabaseDriver.js +144 -45
  16. package/drivers/IDatabaseDriver.d.ts +118 -23
  17. package/entity/BaseEntity.d.ts +63 -4
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +101 -29
  20. package/entity/Collection.js +473 -115
  21. package/entity/EntityAssigner.js +37 -25
  22. package/entity/EntityFactory.d.ts +7 -1
  23. package/entity/EntityFactory.js +116 -64
  24. package/entity/EntityHelper.d.ts +2 -2
  25. package/entity/EntityHelper.js +69 -27
  26. package/entity/EntityLoader.d.ts +11 -10
  27. package/entity/EntityLoader.js +264 -102
  28. package/entity/EntityRepository.d.ts +28 -8
  29. package/entity/EntityRepository.js +8 -2
  30. package/entity/PolymorphicRef.d.ts +12 -0
  31. package/entity/PolymorphicRef.js +18 -0
  32. package/entity/Reference.d.ts +2 -6
  33. package/entity/Reference.js +52 -19
  34. package/entity/WrappedEntity.d.ts +3 -8
  35. package/entity/WrappedEntity.js +6 -7
  36. package/entity/defineEntity.d.ts +525 -311
  37. package/entity/defineEntity.js +134 -290
  38. package/entity/index.d.ts +2 -2
  39. package/entity/index.js +2 -2
  40. package/entity/utils.d.ts +6 -1
  41. package/entity/utils.js +46 -11
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +66 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +13 -17
  46. package/errors.d.ts +20 -10
  47. package/errors.js +63 -31
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +24 -13
  50. package/events/index.d.ts +1 -1
  51. package/events/index.js +0 -1
  52. package/exceptions.js +9 -2
  53. package/hydration/Hydrator.js +1 -2
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +105 -46
  56. package/index.d.ts +2 -2
  57. package/index.js +1 -2
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/DefaultLogger.js +3 -4
  60. package/logging/SimpleLogger.d.ts +1 -1
  61. package/logging/colors.d.ts +1 -1
  62. package/logging/colors.js +5 -7
  63. package/logging/index.d.ts +2 -1
  64. package/logging/index.js +1 -1
  65. package/logging/inspect.d.ts +2 -0
  66. package/logging/inspect.js +11 -0
  67. package/metadata/EntitySchema.d.ts +47 -23
  68. package/metadata/EntitySchema.js +103 -34
  69. package/metadata/MetadataDiscovery.d.ts +64 -9
  70. package/metadata/MetadataDiscovery.js +867 -354
  71. package/metadata/MetadataProvider.d.ts +11 -2
  72. package/metadata/MetadataProvider.js +71 -2
  73. package/metadata/MetadataStorage.d.ts +13 -11
  74. package/metadata/MetadataStorage.js +72 -41
  75. package/metadata/MetadataValidator.d.ts +32 -9
  76. package/metadata/MetadataValidator.js +214 -44
  77. package/metadata/discover-entities.d.ts +5 -0
  78. package/metadata/discover-entities.js +40 -0
  79. package/metadata/index.d.ts +1 -1
  80. package/metadata/index.js +0 -1
  81. package/metadata/types.d.ts +577 -0
  82. package/metadata/types.js +1 -0
  83. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  84. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  85. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  86. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  87. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/MongoNamingStrategy.js +6 -6
  89. package/naming-strategy/NamingStrategy.d.ts +28 -4
  90. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  91. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  92. package/naming-strategy/index.d.ts +1 -1
  93. package/naming-strategy/index.js +0 -1
  94. package/not-supported.d.ts +2 -0
  95. package/not-supported.js +8 -0
  96. package/package.json +47 -36
  97. package/platforms/ExceptionConverter.js +1 -1
  98. package/platforms/Platform.d.ts +33 -15
  99. package/platforms/Platform.js +125 -69
  100. package/serialization/EntitySerializer.d.ts +6 -3
  101. package/serialization/EntitySerializer.js +53 -29
  102. package/serialization/EntityTransformer.js +33 -21
  103. package/serialization/SerializationContext.d.ts +6 -6
  104. package/serialization/SerializationContext.js +4 -4
  105. package/types/ArrayType.d.ts +1 -1
  106. package/types/ArrayType.js +2 -3
  107. package/types/BigIntType.js +1 -1
  108. package/types/BlobType.d.ts +0 -1
  109. package/types/BlobType.js +0 -3
  110. package/types/BooleanType.d.ts +1 -0
  111. package/types/BooleanType.js +3 -0
  112. package/types/DecimalType.js +2 -2
  113. package/types/DoubleType.js +1 -1
  114. package/types/EnumArrayType.js +1 -2
  115. package/types/JsonType.d.ts +1 -1
  116. package/types/JsonType.js +7 -2
  117. package/types/TinyIntType.js +1 -1
  118. package/types/Type.d.ts +2 -4
  119. package/types/Type.js +3 -3
  120. package/types/Uint8ArrayType.d.ts +0 -1
  121. package/types/Uint8ArrayType.js +1 -4
  122. package/types/UuidType.d.ts +2 -0
  123. package/types/UuidType.js +14 -2
  124. package/types/index.d.ts +3 -2
  125. package/typings.d.ts +427 -170
  126. package/typings.js +100 -45
  127. package/unit-of-work/ChangeSet.d.ts +4 -6
  128. package/unit-of-work/ChangeSet.js +8 -9
  129. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  130. package/unit-of-work/ChangeSetComputer.js +49 -26
  131. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  132. package/unit-of-work/ChangeSetPersister.js +107 -44
  133. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  134. package/unit-of-work/CommitOrderCalculator.js +17 -15
  135. package/unit-of-work/IdentityMap.d.ts +12 -0
  136. package/unit-of-work/IdentityMap.js +39 -1
  137. package/unit-of-work/UnitOfWork.d.ts +34 -4
  138. package/unit-of-work/UnitOfWork.js +294 -107
  139. package/utils/AbstractMigrator.d.ts +101 -0
  140. package/utils/AbstractMigrator.js +303 -0
  141. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  142. package/utils/AbstractSchemaGenerator.js +30 -18
  143. package/utils/AsyncContext.d.ts +6 -0
  144. package/utils/AsyncContext.js +42 -0
  145. package/utils/Configuration.d.ts +796 -211
  146. package/utils/Configuration.js +160 -197
  147. package/utils/ConfigurationLoader.d.ts +1 -52
  148. package/utils/ConfigurationLoader.js +1 -330
  149. package/utils/Cursor.d.ts +0 -3
  150. package/utils/Cursor.js +29 -14
  151. package/utils/DataloaderUtils.d.ts +10 -5
  152. package/utils/DataloaderUtils.js +42 -22
  153. package/utils/EntityComparator.d.ts +16 -9
  154. package/utils/EntityComparator.js +202 -96
  155. package/utils/QueryHelper.d.ts +34 -7
  156. package/utils/QueryHelper.js +183 -72
  157. package/utils/RawQueryFragment.d.ts +28 -34
  158. package/utils/RawQueryFragment.js +37 -72
  159. package/utils/RequestContext.js +2 -2
  160. package/utils/TransactionContext.js +2 -2
  161. package/utils/TransactionManager.js +11 -7
  162. package/utils/Utils.d.ts +16 -127
  163. package/utils/Utils.js +106 -401
  164. package/utils/clone.js +13 -23
  165. package/utils/env-vars.d.ts +7 -0
  166. package/utils/env-vars.js +98 -0
  167. package/utils/fs-utils.d.ts +34 -0
  168. package/utils/fs-utils.js +193 -0
  169. package/utils/index.d.ts +1 -3
  170. package/utils/index.js +1 -3
  171. package/utils/upsert-utils.d.ts +9 -4
  172. package/utils/upsert-utils.js +51 -5
  173. package/decorators/Check.d.ts +0 -3
  174. package/decorators/Check.js +0 -13
  175. package/decorators/CreateRequestContext.d.ts +0 -3
  176. package/decorators/CreateRequestContext.js +0 -32
  177. package/decorators/Embeddable.d.ts +0 -8
  178. package/decorators/Embeddable.js +0 -11
  179. package/decorators/Embedded.d.ts +0 -12
  180. package/decorators/Embedded.js +0 -18
  181. package/decorators/Entity.d.ts +0 -33
  182. package/decorators/Entity.js +0 -12
  183. package/decorators/Enum.d.ts +0 -9
  184. package/decorators/Enum.js +0 -16
  185. package/decorators/Filter.d.ts +0 -2
  186. package/decorators/Filter.js +0 -8
  187. package/decorators/Formula.d.ts +0 -4
  188. package/decorators/Formula.js +0 -15
  189. package/decorators/Indexed.d.ts +0 -19
  190. package/decorators/Indexed.js +0 -20
  191. package/decorators/ManyToMany.d.ts +0 -42
  192. package/decorators/ManyToMany.js +0 -14
  193. package/decorators/ManyToOne.d.ts +0 -34
  194. package/decorators/ManyToOne.js +0 -14
  195. package/decorators/OneToMany.d.ts +0 -28
  196. package/decorators/OneToMany.js +0 -17
  197. package/decorators/OneToOne.d.ts +0 -28
  198. package/decorators/OneToOne.js +0 -7
  199. package/decorators/PrimaryKey.d.ts +0 -8
  200. package/decorators/PrimaryKey.js +0 -20
  201. package/decorators/Property.d.ts +0 -250
  202. package/decorators/Property.js +0 -32
  203. package/decorators/Transactional.d.ts +0 -14
  204. package/decorators/Transactional.js +0 -28
  205. package/decorators/hooks.d.ts +0 -16
  206. package/decorators/hooks.js +0 -47
  207. package/decorators/index.d.ts +0 -17
  208. package/decorators/index.js +0 -17
  209. package/entity/ArrayCollection.d.ts +0 -118
  210. package/entity/ArrayCollection.js +0 -407
  211. package/entity/EntityValidator.d.ts +0 -19
  212. package/entity/EntityValidator.js +0 -150
  213. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  214. package/metadata/ReflectMetadataProvider.js +0 -44
  215. package/utils/resolveContextProvider.d.ts +0 -10
  216. package/utils/resolveContextProvider.js +0 -28
@@ -1,16 +1,29 @@
1
- import { ArrayCollection } from './ArrayCollection.js';
2
1
  import { Utils } from '../utils/Utils.js';
3
- import { ValidationError } from '../errors.js';
4
- import { ReferenceKind, DataloaderType } from '../enums.js';
2
+ import { MetadataError, ValidationError } from '../errors.js';
3
+ import { DataloaderType, ReferenceKind } from '../enums.js';
5
4
  import { Reference } from './Reference.js';
6
- import { helper } from './wrap.js';
7
- export class Collection extends ArrayCollection {
5
+ import { helper, wrap } from './wrap.js';
6
+ import { QueryHelper } from '../utils/QueryHelper.js';
7
+ import { inspect } from '../logging/inspect.js';
8
+ export class Collection {
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
8
15
  readonly;
16
+ _count;
17
+ _property;
9
18
  _populated;
10
- // this is for some reason needed for TS, otherwise it can fail with `Type instantiation is excessively deep and possibly infinite.`
11
- _snapshot;
12
19
  constructor(owner, items, initialized = true) {
13
- super(owner, items);
20
+ this.owner = owner;
21
+ /* v8 ignore next */
22
+ if (items) {
23
+ let i = 0;
24
+ this.items = new Set(items);
25
+ this.items.forEach(item => (this[i++] = item));
26
+ }
14
27
  this.initialized = !!items || initialized;
15
28
  }
16
29
  /**
@@ -32,6 +45,7 @@ export class Collection extends ArrayCollection {
32
45
  async load(options = {}) {
33
46
  if (this.isInitialized(true) && !options.refresh) {
34
47
  const em = this.getEntityManager(this.items, false);
48
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
35
49
  await em?.populate(this.items, options.populate, options);
36
50
  this.setSerializationContext(options);
37
51
  }
@@ -44,7 +58,7 @@ export class Collection extends ArrayCollection {
44
58
  helper(this.owner).setSerializationContext({
45
59
  populate: Array.isArray(options.populate)
46
60
  ? options.populate.map(hint => `${this.property.name}.${hint}`)
47
- : options.populate ?? [this.property.name],
61
+ : (options.populate ?? [this.property.name]),
48
62
  });
49
63
  }
50
64
  /**
@@ -52,7 +66,7 @@ export class Collection extends ArrayCollection {
52
66
  */
53
67
  async loadItems(options) {
54
68
  await this.load(options);
55
- return super.getItems();
69
+ return this.getItems(false);
56
70
  }
57
71
  /**
58
72
  * Gets the count of collection items from database instead of counting loaded items.
@@ -61,15 +75,17 @@ export class Collection extends ArrayCollection {
61
75
  async loadCount(options = {}) {
62
76
  options = typeof options === 'boolean' ? { refresh: options } : options;
63
77
  const { refresh, where, ...countOptions } = options;
64
- if (!refresh && !where && Utils.isDefined(this._count)) {
78
+ if (!refresh && !where && this._count != null) {
65
79
  return this._count;
66
80
  }
67
81
  const em = this.getEntityManager();
68
- if (!em.getPlatform().usesPivotTable() && this.property.kind === ReferenceKind.MANY_TO_MANY && this.property.owner) {
69
- 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);
70
86
  }
71
87
  const cond = this.createLoadCountCondition(where ?? {});
72
- const count = await em.count(this.property.type, cond, countOptions);
88
+ const count = await em.count(this.property.targetMeta.class, cond, countOptions);
73
89
  if (!where) {
74
90
  this._count = count;
75
91
  }
@@ -78,15 +94,22 @@ export class Collection extends ArrayCollection {
78
94
  async matching(options) {
79
95
  const em = this.getEntityManager();
80
96
  const { where, ctx, ...opts } = options;
81
- opts.orderBy = this.createOrderBy(opts.orderBy);
82
97
  let items;
83
98
  if (this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
84
- const cond = await em.applyFilters(this.property.type, where, options.filters ?? {}, 'read');
85
- const map = await em.getDriver().loadFromPivotTable(this.property, [helper(this.owner).__primaryKeys], cond, opts.orderBy, ctx, options);
86
- items = map[helper(this.owner).getSerializedPrimaryKey()].map((item) => em.merge(this.property.type, item, { convertCustomTypes: true }));
99
+ // M:N via pivot table bypasses em.find(), so merge all 3 levels here
100
+ opts.orderBy = QueryHelper.mergeOrderBy(opts.orderBy, this.property.orderBy, this.property.targetMeta?.orderBy);
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);
106
+ items = map[helper(this.owner).getSerializedPrimaryKey()].map((item) => em.merge(this.property.targetMeta.class, item, { convertCustomTypes: true }));
107
+ await em.populate(items, options.populate, options);
87
108
  }
88
109
  else {
89
- items = await em.find(this.property.type, this.createCondition(where), opts);
110
+ // em.find() merges entity-level orderBy, so only merge runtime + relation here
111
+ opts.orderBy = QueryHelper.mergeOrderBy(opts.orderBy, this.property.orderBy);
112
+ items = (await em.find(this.property.targetMeta.class, this.createCondition(where), opts));
90
113
  }
91
114
  if (options.store) {
92
115
  this.hydrate(items, true);
@@ -103,98 +126,97 @@ export class Collection extends ArrayCollection {
103
126
  if (check) {
104
127
  this.checkInitialized();
105
128
  }
106
- return super.getItems();
129
+ return [...this.items];
107
130
  }
108
131
  toJSON() {
109
132
  if (!this.isInitialized()) {
110
133
  return [];
111
134
  }
112
- return super.toJSON();
135
+ return this.toArray();
113
136
  }
114
137
  add(entity, ...entities) {
115
138
  entities = Utils.asArray(entity).concat(entities);
116
139
  const unwrapped = entities.map(i => Reference.unwrapReference(i));
117
- unwrapped.forEach(entity => this.validateItemType(entity));
118
- this.modify('add', unwrapped);
140
+ this.validateModification(unwrapped);
141
+ const em = this.getEntityManager(entities, false);
142
+ let added = 0;
143
+ for (const item of entities) {
144
+ const entity = Reference.unwrapReference(item);
145
+ if (!this.contains(entity, false)) {
146
+ this.incrementCount(1);
147
+ this[this.items.size] = entity;
148
+ this.items.add(entity);
149
+ added++;
150
+ this.dirty = true;
151
+ this.propagate(entity, 'add');
152
+ }
153
+ }
154
+ if (this.property.kind === ReferenceKind.ONE_TO_MANY && em) {
155
+ em.persist(entities);
156
+ }
119
157
  this.cancelOrphanRemoval(unwrapped);
158
+ return added;
120
159
  }
121
160
  /**
122
- * @inheritDoc
161
+ * Remove specified item(s) from the collection. Note that removing item from collection does not necessarily imply deleting the target entity,
162
+ * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
163
+ * 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`,
164
+ * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
123
165
  */
124
166
  remove(entity, ...entities) {
125
167
  if (entity instanceof Function) {
168
+ let removed = 0;
126
169
  for (const item of this.items) {
127
170
  if (entity(item)) {
128
- this.remove(item);
171
+ removed += this.remove(item);
129
172
  }
130
173
  }
131
- return;
174
+ return removed;
132
175
  }
176
+ this.checkInitialized();
133
177
  entities = Utils.asArray(entity).concat(entities);
134
178
  const unwrapped = entities.map(i => Reference.unwrapReference(i));
135
- this.modify('remove', unwrapped);
136
- const em = this.getEntityManager(unwrapped, false);
137
- if (this.property.orphanRemoval && em) {
138
- for (const item of unwrapped) {
139
- em.getUnitOfWork().scheduleOrphanRemoval(item);
179
+ this.validateModification(unwrapped);
180
+ const em = this.getEntityManager(entities, false);
181
+ let removed = 0;
182
+ for (const item of entities) {
183
+ if (!item) {
184
+ continue;
185
+ }
186
+ const entity = Reference.unwrapReference(item);
187
+ if (this.items.delete(entity)) {
188
+ this.incrementCount(-1);
189
+ delete this[this.items.size]; // remove last item
190
+ this.propagate(entity, 'remove');
191
+ removed++;
192
+ this.dirty = true;
140
193
  }
194
+ if (this.property.orphanRemoval && em) {
195
+ em.getUnitOfWork().scheduleOrphanRemoval(entity);
196
+ }
197
+ }
198
+ if (this.property.kind === ReferenceKind.ONE_TO_MANY && !this.property.orphanRemoval && em) {
199
+ em.persist(entities);
200
+ }
201
+ if (removed > 0) {
202
+ Object.assign(this, [...this.items]); // reassign array access
141
203
  }
204
+ return removed;
142
205
  }
143
206
  contains(item, check = true) {
144
207
  if (check) {
145
208
  this.checkInitialized();
146
209
  }
147
- return super.contains(item);
210
+ const entity = Reference.unwrapReference(item);
211
+ return this.items.has(entity);
148
212
  }
149
213
  count() {
150
214
  this.checkInitialized();
151
- return super.count();
215
+ return this.items.size;
152
216
  }
153
217
  isEmpty() {
154
218
  this.checkInitialized();
155
- return super.isEmpty();
156
- }
157
- /**
158
- * @inheritDoc
159
- */
160
- slice(start, end) {
161
- this.checkInitialized();
162
- return super.slice(start, end);
163
- }
164
- /**
165
- * @inheritDoc
166
- */
167
- exists(cb) {
168
- this.checkInitialized();
169
- return super.exists(cb);
170
- }
171
- /**
172
- * @inheritDoc
173
- */
174
- find(cb) {
175
- this.checkInitialized();
176
- return super.find(cb);
177
- }
178
- /**
179
- * @inheritDoc
180
- */
181
- filter(cb) {
182
- this.checkInitialized();
183
- return super.filter(cb);
184
- }
185
- /**
186
- * @inheritDoc
187
- */
188
- map(mapper) {
189
- this.checkInitialized();
190
- return super.map(mapper);
191
- }
192
- /**
193
- * @inheritDoc
194
- */
195
- indexBy(key, valueKey) {
196
- this.checkInitialized();
197
- return super.indexBy(key, valueKey);
219
+ return this.count() === 0;
198
220
  }
199
221
  shouldPopulate(populated) {
200
222
  if (!this.isInitialized(true)) {
@@ -217,16 +239,14 @@ export class Collection extends ArrayCollection {
217
239
  return this;
218
240
  }
219
241
  const em = this.getEntityManager();
242
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property.filters, options.filters) };
220
243
  if (options.dataloader ?? [DataloaderType.ALL, DataloaderType.COLLECTION].includes(em.config.getDataloaderType())) {
221
244
  const order = [...this.items]; // copy order of references
222
- const orderBy = this.createOrderBy(options.orderBy);
245
+ const orderBy = QueryHelper.mergeOrderBy(options.orderBy, this.property.orderBy, this.property.targetMeta?.orderBy);
223
246
  const customOrder = orderBy.length > 0;
224
247
  const pivotTable = this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable();
225
- const loader = pivotTable ? 'colLoaderMtoN' : 'colLoader';
226
- const items = await em[loader].load([
227
- this,
228
- { ...options, orderBy },
229
- ]);
248
+ const loader = await em.getDataLoader(pivotTable ? 'm:n' : '1:m');
249
+ const items = await loader.load([this, { ...options, orderBy }]);
230
250
  if (this.property.kind === ReferenceKind.MANY_TO_MANY) {
231
251
  this.initialized = true;
232
252
  this.dirty = false;
@@ -246,11 +266,9 @@ export class Collection extends ArrayCollection {
246
266
  return this;
247
267
  }
248
268
  const populate = Array.isArray(options.populate)
249
- ? options.populate.map(f => f === '*' ? f : `${this.property.name}.${f}`)
269
+ ? options.populate.map(f => (f === '*' ? f : `${this.property.name}.${f}`))
250
270
  : [`${this.property.name}${options.ref ? ':ref' : ''}`];
251
- const schema = this.property.targetMeta.schema === '*'
252
- ? helper(this.owner).__schema
253
- : undefined;
271
+ const schema = this.property.targetMeta.schema === '*' ? helper(this.owner).__schema : undefined;
254
272
  await em.populate(this.owner, populate, {
255
273
  refresh: true,
256
274
  ...options,
@@ -281,17 +299,12 @@ export class Collection extends ArrayCollection {
281
299
  if (this.property.kind === ReferenceKind.ONE_TO_MANY) {
282
300
  cond[this.property.mappedBy] = helper(this.owner).getPrimaryKey();
283
301
  }
284
- else { // MANY_TO_MANY
302
+ else {
303
+ // MANY_TO_MANY
285
304
  this.createManyToManyCondition(cond);
286
305
  }
287
306
  return cond;
288
307
  }
289
- createOrderBy(orderBy = []) {
290
- if (Utils.isEmpty(orderBy) && this.property.orderBy) {
291
- orderBy = this.property.orderBy;
292
- }
293
- return Utils.asArray(orderBy);
294
- }
295
308
  createManyToManyCondition(cond) {
296
309
  const dict = cond;
297
310
  if (this.property.owner || this.property.pivotTable) {
@@ -317,17 +330,9 @@ export class Collection extends ArrayCollection {
317
330
  }
318
331
  return cond;
319
332
  }
320
- modify(method, items) {
321
- if (method === 'remove') {
322
- this.checkInitialized();
323
- }
324
- this.validateModification(items);
325
- super[method](items);
326
- this.setDirty();
327
- }
328
333
  checkInitialized() {
329
334
  if (!this.isInitialized()) {
330
- throw new Error(`Collection<${this.property.type}> of entity ${this.owner.constructor.name}[${helper(this.owner).getSerializedPrimaryKey()}] not initialized`);
335
+ throw new Error(`Collection<${this.property.type}> of entity ${helper(this.owner).__meta.name}[${helper(this.owner).getSerializedPrimaryKey()}] not initialized`);
331
336
  }
332
337
  }
333
338
  /**
@@ -347,32 +352,385 @@ export class Collection extends ArrayCollection {
347
352
  em.getUnitOfWork().cancelOrphanRemoval(item);
348
353
  }
349
354
  }
350
- validateItemType(item) {
351
- if (!Utils.isEntity(item)) {
352
- throw ValidationError.notEntity(this.owner, this.property, item);
353
- }
354
- }
355
355
  validateModification(items) {
356
356
  if (this.readonly) {
357
357
  throw ValidationError.cannotModifyReadonlyCollection(this.owner, this.property);
358
358
  }
359
- // currently we allow persisting to inverse sides only in SQL drivers
360
- if (this.property.pivotTable || !this.property.mappedBy) {
361
- return;
362
- }
363
359
  const check = (item) => {
364
- if (!item || helper(item).__initialized) {
360
+ if (!item) {
361
+ return false;
362
+ }
363
+ if (!Utils.isEntity(item)) {
364
+ throw ValidationError.notEntity(this.owner, this.property, item);
365
+ }
366
+ // currently we allow persisting to inverse sides only in SQL drivers
367
+ if (this.property.pivotTable || !this.property.mappedBy) {
368
+ return false;
369
+ }
370
+ if (helper(item).__initialized) {
365
371
  return false;
366
372
  }
367
373
  return !item[this.property.mappedBy] && this.property.kind === ReferenceKind.MANY_TO_MANY;
368
374
  };
369
375
  // throw if we are modifying inverse side of M:N collection when owning side is initialized (would be ignored when persisting)
370
- if (items.find(item => check(item))) {
376
+ if (items.some(item => check(item))) {
371
377
  throw ValidationError.cannotModifyInverseCollection(this.owner, this.property);
372
378
  }
373
379
  }
380
+ toArray() {
381
+ if (this.items.size === 0) {
382
+ return [];
383
+ }
384
+ return this.map(item => wrap(item).toJSON());
385
+ }
386
+ getIdentifiers(field) {
387
+ const items = this.getItems();
388
+ const targetMeta = this.property.targetMeta;
389
+ if (items.length === 0) {
390
+ return [];
391
+ }
392
+ field ??= targetMeta.compositePK
393
+ ? targetMeta.primaryKeys
394
+ : (targetMeta.serializedPrimaryKey ?? targetMeta.primaryKeys[0]);
395
+ const cb = (i, f) => {
396
+ if (Utils.isEntity(i[f], true)) {
397
+ return wrap(i[f], true).getPrimaryKey();
398
+ }
399
+ return i[f];
400
+ };
401
+ return items.map(i => {
402
+ if (Array.isArray(field)) {
403
+ return field.map(f => cb(i, f));
404
+ }
405
+ return cb(i, field);
406
+ });
407
+ }
408
+ /**
409
+ * @internal
410
+ */
411
+ addWithoutPropagation(entity) {
412
+ if (!this.contains(entity, false)) {
413
+ this.incrementCount(1);
414
+ this[this.items.size] = entity;
415
+ this.items.add(entity);
416
+ this.dirty = true;
417
+ }
418
+ }
419
+ set(items) {
420
+ if (!this.initialized) {
421
+ this.initialized = true;
422
+ this.snapshot = undefined;
423
+ }
424
+ if (this.compare(Utils.asArray(items).map(item => Reference.unwrapReference(item)))) {
425
+ return;
426
+ }
427
+ this.remove(this.items);
428
+ this.add(items);
429
+ }
430
+ compare(items) {
431
+ if (items.length !== this.items.size) {
432
+ return false;
433
+ }
434
+ let idx = 0;
435
+ for (const item of this.items) {
436
+ if (item !== items[idx++]) {
437
+ return false;
438
+ }
439
+ }
440
+ return true;
441
+ }
442
+ /**
443
+ * @internal
444
+ */
445
+ hydrate(items, forcePropagate, partial) {
446
+ for (let i = 0; i < this.items.size; i++) {
447
+ delete this[i];
448
+ }
449
+ this.initialized = true;
450
+ this.partial = !!partial;
451
+ this.items.clear();
452
+ this._count = 0;
453
+ this.add(items);
454
+ this.takeSnapshot(forcePropagate);
455
+ }
456
+ /**
457
+ * Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
458
+ * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
459
+ * 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`,
460
+ * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
461
+ */
462
+ removeAll() {
463
+ if (!this.initialized) {
464
+ this.initialized = true;
465
+ this.snapshot = undefined;
466
+ }
467
+ this.remove(this.items);
468
+ this.dirty = true;
469
+ }
470
+ /**
471
+ * @internal
472
+ */
473
+ removeWithoutPropagation(entity) {
474
+ if (!this.items.delete(entity)) {
475
+ return;
476
+ }
477
+ this.incrementCount(-1);
478
+ delete this[this.items.size];
479
+ Object.assign(this, [...this.items]);
480
+ this.dirty = true;
481
+ }
482
+ /**
483
+ * Extracts a slice of the collection items starting at position start to end (exclusive) of the collection.
484
+ * If end is null it returns all elements from start to the end of the collection.
485
+ */
486
+ slice(start = 0, end) {
487
+ this.checkInitialized();
488
+ let index = 0;
489
+ end ??= this.items.size;
490
+ const items = [];
491
+ for (const item of this.items) {
492
+ if (index === end) {
493
+ break;
494
+ }
495
+ if (index >= start && index < end) {
496
+ items.push(item);
497
+ }
498
+ index++;
499
+ }
500
+ return items;
501
+ }
502
+ /**
503
+ * Tests for the existence of an element that satisfies the given predicate.
504
+ */
505
+ exists(cb) {
506
+ this.checkInitialized();
507
+ for (const item of this.items) {
508
+ if (cb(item)) {
509
+ return true;
510
+ }
511
+ }
512
+ return false;
513
+ }
514
+ /**
515
+ * Returns the first element of this collection that satisfies the predicate.
516
+ */
517
+ find(cb) {
518
+ this.checkInitialized();
519
+ let index = 0;
520
+ for (const item of this.items) {
521
+ if (cb(item, index++)) {
522
+ return item;
523
+ }
524
+ }
525
+ return undefined;
526
+ }
527
+ /**
528
+ * Extracts a subset of the collection items.
529
+ */
530
+ filter(cb) {
531
+ this.checkInitialized();
532
+ const items = [];
533
+ let index = 0;
534
+ for (const item of this.items) {
535
+ if (cb(item, index++)) {
536
+ items.push(item);
537
+ }
538
+ }
539
+ return items;
540
+ }
541
+ /**
542
+ * Maps the collection items based on your provided mapper function.
543
+ */
544
+ map(mapper) {
545
+ this.checkInitialized();
546
+ const items = [];
547
+ let index = 0;
548
+ for (const item of this.items) {
549
+ items.push(mapper(item, index++));
550
+ }
551
+ return items;
552
+ }
553
+ /**
554
+ * Maps the collection items based on your provided mapper function to a single object.
555
+ */
556
+ reduce(cb, initial = {}) {
557
+ this.checkInitialized();
558
+ let index = 0;
559
+ for (const item of this.items) {
560
+ initial = cb(initial, item, index++);
561
+ }
562
+ return initial;
563
+ }
564
+ /**
565
+ * Maps the collection items to a dictionary, indexed by the key you specify.
566
+ * If there are more items with the same key, only the first one will be present.
567
+ */
568
+ indexBy(key, valueKey) {
569
+ return this.reduce((obj, item) => {
570
+ obj[item[key]] ??= valueKey ? item[valueKey] : item;
571
+ return obj;
572
+ }, {});
573
+ }
574
+ isInitialized(fully = false) {
575
+ if (!this.initialized || !fully) {
576
+ return this.initialized;
577
+ }
578
+ for (const item of this.items) {
579
+ if (!helper(item).__initialized) {
580
+ return false;
581
+ }
582
+ }
583
+ return true;
584
+ }
585
+ isDirty() {
586
+ return this.dirty;
587
+ }
588
+ isPartial() {
589
+ return this.partial;
590
+ }
591
+ setDirty(dirty = true) {
592
+ this.dirty = dirty;
593
+ }
594
+ get length() {
595
+ return this.count();
596
+ }
597
+ *[Symbol.iterator]() {
598
+ for (const item of this.getItems()) {
599
+ yield item;
600
+ }
601
+ }
602
+ /**
603
+ * @internal
604
+ */
605
+ takeSnapshot(forcePropagate) {
606
+ this.snapshot = [...this.items];
607
+ this.dirty = false;
608
+ if (this.property.owner || forcePropagate) {
609
+ this.items.forEach(item => {
610
+ this.propagate(item, 'takeSnapshot');
611
+ });
612
+ }
613
+ }
614
+ /**
615
+ * @internal
616
+ */
617
+ getSnapshot() {
618
+ return this.snapshot;
619
+ }
620
+ /**
621
+ * @internal
622
+ */
623
+ get property() {
624
+ // cannot be typed to `EntityProperty<O, T>` as it causes issues in assignability of `Loaded` type
625
+ if (!this._property) {
626
+ const meta = wrap(this.owner, true).__meta;
627
+ /* v8 ignore next */
628
+ if (!meta) {
629
+ throw MetadataError.fromUnknownEntity(this.owner.constructor.name, 'Collection.property getter, maybe you just forgot to initialize the ORM?');
630
+ }
631
+ this._property = meta.relations.find(prop => this.owner[prop.name] === this);
632
+ }
633
+ return this._property;
634
+ }
635
+ /**
636
+ * @internal
637
+ */
638
+ set property(prop) {
639
+ // cannot be typed to `EntityProperty<O, T>` as it causes issues in assignability of `Loaded` type
640
+ this._property = prop;
641
+ }
642
+ propagate(item, method) {
643
+ if (this.property.owner && this.property.inversedBy) {
644
+ this.propagateToInverseSide(item, method);
645
+ }
646
+ else if (!this.property.owner && this.property.mappedBy) {
647
+ this.propagateToOwningSide(item, method);
648
+ }
649
+ }
650
+ propagateToInverseSide(item, method) {
651
+ const collection = item[this.property.inversedBy];
652
+ if (this.shouldPropagateToCollection(collection, method)) {
653
+ method = method === 'takeSnapshot' ? method : (method + 'WithoutPropagation');
654
+ collection[method](this.owner);
655
+ }
656
+ }
657
+ propagateToOwningSide(item, method) {
658
+ const mappedBy = this.property.mappedBy;
659
+ const collection = item[mappedBy];
660
+ if (this.property.kind === ReferenceKind.MANY_TO_MANY) {
661
+ if (this.shouldPropagateToCollection(collection, method)) {
662
+ collection[method](this.owner);
663
+ }
664
+ }
665
+ else if (this.property.kind === ReferenceKind.ONE_TO_MANY && method !== 'takeSnapshot') {
666
+ const prop2 = this.property.targetMeta.properties[mappedBy];
667
+ const owner = prop2.mapToPk ? helper(this.owner).getPrimaryKey() : this.owner;
668
+ const value = method === 'add' ? owner : null;
669
+ if (this.property.orphanRemoval && method === 'remove') {
670
+ // cache the PK before we propagate, as its value might be needed when flushing
671
+ helper(item).__pk = helper(item).getPrimaryKey();
672
+ }
673
+ if (!prop2.nullable && prop2.deleteRule !== 'cascade' && method === 'remove') {
674
+ if (!this.property.orphanRemoval) {
675
+ throw ValidationError.cannotRemoveFromCollectionWithoutOrphanRemoval(this.owner, this.property);
676
+ }
677
+ return;
678
+ }
679
+ // skip if already propagated
680
+ if (Reference.unwrapReference(item[mappedBy]) !== value) {
681
+ item[mappedBy] = value;
682
+ }
683
+ }
684
+ }
685
+ shouldPropagateToCollection(collection, method) {
686
+ if (!collection) {
687
+ return false;
688
+ }
689
+ switch (method) {
690
+ case 'add':
691
+ return !collection.contains(this.owner, false);
692
+ case 'remove':
693
+ return collection.isInitialized() && collection.contains(this.owner, false);
694
+ case 'takeSnapshot':
695
+ return collection.isDirty();
696
+ }
697
+ }
698
+ incrementCount(value) {
699
+ if (typeof this._count === 'number' && this.initialized) {
700
+ this._count += value;
701
+ }
702
+ }
703
+ /** @ignore */
704
+ [Symbol.for('nodejs.util.inspect.custom')](depth = 2) {
705
+ const object = { ...this };
706
+ const hidden = [
707
+ 'items',
708
+ 'owner',
709
+ '_property',
710
+ '_count',
711
+ 'snapshot',
712
+ '_populated',
713
+ '_lazyInitialized',
714
+ '_em',
715
+ 'readonly',
716
+ 'partial',
717
+ ];
718
+ hidden.forEach(k => delete object[k]);
719
+ const ret = inspect(object, { depth });
720
+ const name = `${this.constructor.name}<${this.property?.type ?? 'unknown'}>`;
721
+ return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
722
+ }
374
723
  }
375
724
  Object.defineProperties(Collection.prototype, {
376
- $: { get() { return this; } },
377
- get: { get() { return () => this; } },
725
+ $: {
726
+ get() {
727
+ return this;
728
+ },
729
+ },
730
+ get: {
731
+ get() {
732
+ return () => this;
733
+ },
734
+ },
735
+ __collection: { value: true, enumerable: false, writable: false },
378
736
  });