@yandjin-mikro-orm/core 6.1.4-rc-sti-changes-1

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 (264) hide show
  1. package/EntityManager.d.ts +553 -0
  2. package/EntityManager.js +1736 -0
  3. package/LICENSE +21 -0
  4. package/MikroORM.d.ts +102 -0
  5. package/MikroORM.js +258 -0
  6. package/README.md +383 -0
  7. package/cache/CacheAdapter.d.ts +40 -0
  8. package/cache/CacheAdapter.js +2 -0
  9. package/cache/FileCacheAdapter.d.ts +31 -0
  10. package/cache/FileCacheAdapter.js +90 -0
  11. package/cache/GeneratedCacheAdapter.d.ts +25 -0
  12. package/cache/GeneratedCacheAdapter.js +38 -0
  13. package/cache/MemoryCacheAdapter.d.ts +24 -0
  14. package/cache/MemoryCacheAdapter.js +44 -0
  15. package/cache/NullCacheAdapter.d.ts +19 -0
  16. package/cache/NullCacheAdapter.js +30 -0
  17. package/cache/index.d.ts +5 -0
  18. package/cache/index.js +21 -0
  19. package/connections/Connection.d.ts +85 -0
  20. package/connections/Connection.js +128 -0
  21. package/connections/index.d.ts +1 -0
  22. package/connections/index.js +17 -0
  23. package/decorators/Check.d.ts +3 -0
  24. package/decorators/Check.js +17 -0
  25. package/decorators/CreateRequestContext.d.ts +2 -0
  26. package/decorators/CreateRequestContext.js +31 -0
  27. package/decorators/Embeddable.d.ts +8 -0
  28. package/decorators/Embeddable.js +15 -0
  29. package/decorators/Embedded.d.ts +13 -0
  30. package/decorators/Embedded.js +21 -0
  31. package/decorators/EnsureRequestContext.d.ts +2 -0
  32. package/decorators/EnsureRequestContext.js +26 -0
  33. package/decorators/Entity.d.ts +18 -0
  34. package/decorators/Entity.js +17 -0
  35. package/decorators/Enum.d.ts +9 -0
  36. package/decorators/Enum.js +20 -0
  37. package/decorators/Filter.d.ts +2 -0
  38. package/decorators/Filter.js +12 -0
  39. package/decorators/Formula.d.ts +5 -0
  40. package/decorators/Formula.js +19 -0
  41. package/decorators/Indexed.d.ts +12 -0
  42. package/decorators/Indexed.js +25 -0
  43. package/decorators/ManyToMany.d.ts +21 -0
  44. package/decorators/ManyToMany.js +17 -0
  45. package/decorators/ManyToOne.d.ts +15 -0
  46. package/decorators/ManyToOne.js +17 -0
  47. package/decorators/OneToMany.d.ts +18 -0
  48. package/decorators/OneToMany.js +21 -0
  49. package/decorators/OneToOne.d.ts +12 -0
  50. package/decorators/OneToOne.js +11 -0
  51. package/decorators/PrimaryKey.d.ts +8 -0
  52. package/decorators/PrimaryKey.js +24 -0
  53. package/decorators/Property.d.ts +218 -0
  54. package/decorators/Property.js +35 -0
  55. package/decorators/Subscriber.d.ts +1 -0
  56. package/decorators/Subscriber.js +2 -0
  57. package/decorators/hooks.d.ts +16 -0
  58. package/decorators/hooks.js +60 -0
  59. package/decorators/index.d.ts +17 -0
  60. package/decorators/index.js +36 -0
  61. package/drivers/DatabaseDriver.d.ts +72 -0
  62. package/drivers/DatabaseDriver.js +358 -0
  63. package/drivers/IDatabaseDriver.d.ts +193 -0
  64. package/drivers/IDatabaseDriver.js +4 -0
  65. package/drivers/index.d.ts +2 -0
  66. package/drivers/index.js +18 -0
  67. package/entity/ArrayCollection.d.ts +113 -0
  68. package/entity/ArrayCollection.js +386 -0
  69. package/entity/BaseEntity.d.ts +22 -0
  70. package/entity/BaseEntity.js +47 -0
  71. package/entity/Collection.d.ts +104 -0
  72. package/entity/Collection.js +373 -0
  73. package/entity/EntityAssigner.d.ts +28 -0
  74. package/entity/EntityAssigner.js +226 -0
  75. package/entity/EntityFactory.d.ts +41 -0
  76. package/entity/EntityFactory.js +302 -0
  77. package/entity/EntityHelper.d.ts +29 -0
  78. package/entity/EntityHelper.js +250 -0
  79. package/entity/EntityIdentifier.d.ts +10 -0
  80. package/entity/EntityIdentifier.js +19 -0
  81. package/entity/EntityLoader.d.ts +65 -0
  82. package/entity/EntityLoader.js +579 -0
  83. package/entity/EntityRepository.d.ts +161 -0
  84. package/entity/EntityRepository.js +207 -0
  85. package/entity/EntityValidator.d.ts +19 -0
  86. package/entity/EntityValidator.js +152 -0
  87. package/entity/Reference.d.ts +89 -0
  88. package/entity/Reference.js +242 -0
  89. package/entity/WrappedEntity.d.ts +67 -0
  90. package/entity/WrappedEntity.js +146 -0
  91. package/entity/index.d.ts +13 -0
  92. package/entity/index.js +29 -0
  93. package/entity/wrap.d.ts +15 -0
  94. package/entity/wrap.js +26 -0
  95. package/enums.d.ts +160 -0
  96. package/enums.js +169 -0
  97. package/errors.d.ts +65 -0
  98. package/errors.js +222 -0
  99. package/events/EventManager.d.ts +17 -0
  100. package/events/EventManager.js +77 -0
  101. package/events/EventSubscriber.d.ts +39 -0
  102. package/events/EventSubscriber.js +2 -0
  103. package/events/TransactionEventBroadcaster.d.ts +11 -0
  104. package/events/TransactionEventBroadcaster.js +17 -0
  105. package/events/index.d.ts +3 -0
  106. package/events/index.js +19 -0
  107. package/exceptions.d.ts +104 -0
  108. package/exceptions.js +130 -0
  109. package/hydration/Hydrator.d.ts +23 -0
  110. package/hydration/Hydrator.js +51 -0
  111. package/hydration/ObjectHydrator.d.ts +24 -0
  112. package/hydration/ObjectHydrator.js +332 -0
  113. package/hydration/index.d.ts +2 -0
  114. package/hydration/index.js +18 -0
  115. package/index.d.ts +25 -0
  116. package/index.js +50 -0
  117. package/index.mjs +192 -0
  118. package/logging/DefaultLogger.d.ts +32 -0
  119. package/logging/DefaultLogger.js +90 -0
  120. package/logging/Logger.d.ts +56 -0
  121. package/logging/Logger.js +2 -0
  122. package/logging/SimpleLogger.d.ts +17 -0
  123. package/logging/SimpleLogger.js +31 -0
  124. package/logging/colors.d.ts +9 -0
  125. package/logging/colors.js +20 -0
  126. package/logging/index.d.ts +4 -0
  127. package/logging/index.js +20 -0
  128. package/metadata/EntitySchema.d.ts +74 -0
  129. package/metadata/EntitySchema.js +293 -0
  130. package/metadata/MetadataDiscovery.d.ts +71 -0
  131. package/metadata/MetadataDiscovery.js +1244 -0
  132. package/metadata/MetadataProvider.d.ts +11 -0
  133. package/metadata/MetadataProvider.js +23 -0
  134. package/metadata/MetadataStorage.d.ts +22 -0
  135. package/metadata/MetadataStorage.js +87 -0
  136. package/metadata/MetadataValidator.d.ts +24 -0
  137. package/metadata/MetadataValidator.js +213 -0
  138. package/metadata/ReflectMetadataProvider.d.ts +8 -0
  139. package/metadata/ReflectMetadataProvider.js +48 -0
  140. package/metadata/index.d.ts +6 -0
  141. package/metadata/index.js +22 -0
  142. package/naming-strategy/AbstractNamingStrategy.d.ts +18 -0
  143. package/naming-strategy/AbstractNamingStrategy.js +48 -0
  144. package/naming-strategy/EntityCaseNamingStrategy.d.ts +12 -0
  145. package/naming-strategy/EntityCaseNamingStrategy.js +32 -0
  146. package/naming-strategy/MongoNamingStrategy.d.ts +9 -0
  147. package/naming-strategy/MongoNamingStrategy.js +25 -0
  148. package/naming-strategy/NamingStrategy.d.ts +52 -0
  149. package/naming-strategy/NamingStrategy.js +2 -0
  150. package/naming-strategy/UnderscoreNamingStrategy.d.ts +10 -0
  151. package/naming-strategy/UnderscoreNamingStrategy.js +28 -0
  152. package/naming-strategy/index.d.ts +5 -0
  153. package/naming-strategy/index.js +21 -0
  154. package/package.json +70 -0
  155. package/platforms/ExceptionConverter.d.ts +5 -0
  156. package/platforms/ExceptionConverter.js +11 -0
  157. package/platforms/Platform.d.ts +201 -0
  158. package/platforms/Platform.js +452 -0
  159. package/platforms/index.d.ts +2 -0
  160. package/platforms/index.js +18 -0
  161. package/serialization/EntitySerializer.d.ts +34 -0
  162. package/serialization/EntitySerializer.js +206 -0
  163. package/serialization/EntityTransformer.d.ts +8 -0
  164. package/serialization/EntityTransformer.js +192 -0
  165. package/serialization/SerializationContext.d.ts +30 -0
  166. package/serialization/SerializationContext.js +111 -0
  167. package/serialization/index.d.ts +3 -0
  168. package/serialization/index.js +19 -0
  169. package/types/ArrayType.d.ts +13 -0
  170. package/types/ArrayType.js +47 -0
  171. package/types/BigIntType.d.ts +16 -0
  172. package/types/BigIntType.js +45 -0
  173. package/types/BlobType.d.ts +10 -0
  174. package/types/BlobType.js +27 -0
  175. package/types/BooleanType.d.ts +8 -0
  176. package/types/BooleanType.js +16 -0
  177. package/types/DateTimeType.d.ts +8 -0
  178. package/types/DateTimeType.js +16 -0
  179. package/types/DateType.d.ts +8 -0
  180. package/types/DateType.js +16 -0
  181. package/types/DecimalType.d.ts +11 -0
  182. package/types/DecimalType.js +22 -0
  183. package/types/DoubleType.d.ts +11 -0
  184. package/types/DoubleType.js +22 -0
  185. package/types/EnumArrayType.d.ts +9 -0
  186. package/types/EnumArrayType.js +32 -0
  187. package/types/EnumType.d.ts +8 -0
  188. package/types/EnumType.js +16 -0
  189. package/types/FloatType.d.ts +8 -0
  190. package/types/FloatType.js +16 -0
  191. package/types/IntegerType.d.ts +8 -0
  192. package/types/IntegerType.js +16 -0
  193. package/types/IntervalType.d.ts +8 -0
  194. package/types/IntervalType.js +16 -0
  195. package/types/JsonType.d.ts +13 -0
  196. package/types/JsonType.js +34 -0
  197. package/types/MediumIntType.d.ts +6 -0
  198. package/types/MediumIntType.js +10 -0
  199. package/types/SmallIntType.d.ts +8 -0
  200. package/types/SmallIntType.js +16 -0
  201. package/types/StringType.d.ts +8 -0
  202. package/types/StringType.js +16 -0
  203. package/types/TextType.d.ts +8 -0
  204. package/types/TextType.js +16 -0
  205. package/types/TimeType.d.ts +9 -0
  206. package/types/TimeType.js +23 -0
  207. package/types/TinyIntType.d.ts +8 -0
  208. package/types/TinyIntType.js +16 -0
  209. package/types/Type.d.ts +64 -0
  210. package/types/Type.js +84 -0
  211. package/types/Uint8ArrayType.d.ts +11 -0
  212. package/types/Uint8ArrayType.js +37 -0
  213. package/types/UnknownType.d.ts +7 -0
  214. package/types/UnknownType.js +13 -0
  215. package/types/UuidType.d.ts +7 -0
  216. package/types/UuidType.js +13 -0
  217. package/types/index.d.ts +75 -0
  218. package/types/index.js +77 -0
  219. package/typings.d.ts +767 -0
  220. package/typings.js +198 -0
  221. package/unit-of-work/ChangeSet.d.ts +34 -0
  222. package/unit-of-work/ChangeSet.js +62 -0
  223. package/unit-of-work/ChangeSetComputer.d.ts +26 -0
  224. package/unit-of-work/ChangeSetComputer.js +153 -0
  225. package/unit-of-work/ChangeSetPersister.d.ts +50 -0
  226. package/unit-of-work/ChangeSetPersister.js +361 -0
  227. package/unit-of-work/CommitOrderCalculator.d.ts +62 -0
  228. package/unit-of-work/CommitOrderCalculator.js +113 -0
  229. package/unit-of-work/IdentityMap.d.ts +17 -0
  230. package/unit-of-work/IdentityMap.js +84 -0
  231. package/unit-of-work/UnitOfWork.d.ts +124 -0
  232. package/unit-of-work/UnitOfWork.js +1013 -0
  233. package/unit-of-work/index.d.ts +6 -0
  234. package/unit-of-work/index.js +22 -0
  235. package/utils/AbstractSchemaGenerator.d.ts +38 -0
  236. package/utils/AbstractSchemaGenerator.js +101 -0
  237. package/utils/Configuration.d.ts +390 -0
  238. package/utils/Configuration.js +357 -0
  239. package/utils/ConfigurationLoader.d.ts +29 -0
  240. package/utils/ConfigurationLoader.js +282 -0
  241. package/utils/Cursor.d.ts +77 -0
  242. package/utils/Cursor.js +169 -0
  243. package/utils/DataloaderUtils.d.ts +43 -0
  244. package/utils/DataloaderUtils.js +194 -0
  245. package/utils/EntityComparator.d.ts +73 -0
  246. package/utils/EntityComparator.js +568 -0
  247. package/utils/NullHighlighter.d.ts +4 -0
  248. package/utils/NullHighlighter.js +9 -0
  249. package/utils/QueryHelper.d.ts +28 -0
  250. package/utils/QueryHelper.js +228 -0
  251. package/utils/RawQueryFragment.d.ts +96 -0
  252. package/utils/RawQueryFragment.js +188 -0
  253. package/utils/RequestContext.d.ts +34 -0
  254. package/utils/RequestContext.js +54 -0
  255. package/utils/TransactionContext.d.ts +19 -0
  256. package/utils/TransactionContext.js +34 -0
  257. package/utils/Utils.d.ts +274 -0
  258. package/utils/Utils.js +1073 -0
  259. package/utils/clone.d.ts +6 -0
  260. package/utils/clone.js +127 -0
  261. package/utils/index.d.ts +13 -0
  262. package/utils/index.js +29 -0
  263. package/utils/upsert-utils.d.ts +6 -0
  264. package/utils/upsert-utils.js +33 -0
@@ -0,0 +1,1736 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EntityManager = void 0;
7
+ const util_1 = require("util");
8
+ const dataloader_1 = __importDefault(require("dataloader"));
9
+ const utils_1 = require("./utils");
10
+ const entity_1 = require("./entity");
11
+ const unit_of_work_1 = require("./unit-of-work");
12
+ const enums_1 = require("./enums");
13
+ const events_1 = require("./events");
14
+ const errors_1 = require("./errors");
15
+ /**
16
+ * The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
17
+ * such as UnitOfWork, Query Language, and Repository API.
18
+ * @template {IDatabaseDriver} Driver current driver type
19
+ */
20
+ class EntityManager {
21
+ config;
22
+ driver;
23
+ metadata;
24
+ useContext;
25
+ eventManager;
26
+ static counter = 1;
27
+ _id = EntityManager.counter++;
28
+ global = false;
29
+ name;
30
+ refLoader = new dataloader_1.default(utils_1.DataloaderUtils.getRefBatchLoadFn(this));
31
+ colLoader = new dataloader_1.default(utils_1.DataloaderUtils.getColBatchLoadFn(this));
32
+ validator;
33
+ repositoryMap = {};
34
+ entityLoader;
35
+ comparator;
36
+ entityFactory;
37
+ unitOfWork;
38
+ resultCache;
39
+ filters = {};
40
+ filterParams = {};
41
+ loggerContext;
42
+ transactionContext;
43
+ disableTransactions;
44
+ flushMode;
45
+ _schema;
46
+ /**
47
+ * @internal
48
+ */
49
+ constructor(config, driver, metadata, useContext = true, eventManager = new events_1.EventManager(config.get('subscribers'))) {
50
+ this.config = config;
51
+ this.driver = driver;
52
+ this.metadata = metadata;
53
+ this.useContext = useContext;
54
+ this.eventManager = eventManager;
55
+ this.entityLoader = new entity_1.EntityLoader(this);
56
+ this.name = this.config.get('contextName');
57
+ this.validator = new entity_1.EntityValidator(this.config.get('strict'));
58
+ this.comparator = this.config.getComparator(this.metadata);
59
+ this.resultCache = this.config.getResultCacheAdapter();
60
+ this.disableTransactions = this.config.get('disableTransactions');
61
+ this.entityFactory = new entity_1.EntityFactory(this);
62
+ this.unitOfWork = new unit_of_work_1.UnitOfWork(this);
63
+ }
64
+ /**
65
+ * Gets the Driver instance used by this EntityManager.
66
+ * Driver is singleton, for one MikroORM instance, only one driver is created.
67
+ */
68
+ getDriver() {
69
+ return this.driver;
70
+ }
71
+ /**
72
+ * Gets the Connection instance, by default returns write connection
73
+ */
74
+ getConnection(type) {
75
+ return this.driver.getConnection(type);
76
+ }
77
+ /**
78
+ * Gets the platform instance. Just like the driver, platform is singleton, one for a MikroORM instance.
79
+ */
80
+ getPlatform() {
81
+ return this.driver.getPlatform();
82
+ }
83
+ /**
84
+ * Gets repository for given entity. You can pass either string name or entity class reference.
85
+ */
86
+ getRepository(entityName) {
87
+ entityName = utils_1.Utils.className(entityName);
88
+ if (!this.repositoryMap[entityName]) {
89
+ const meta = this.metadata.get(entityName);
90
+ const RepositoryClass = this.config.getRepositoryClass(meta.repository);
91
+ this.repositoryMap[entityName] = new RepositoryClass(this.getContext(false), entityName);
92
+ }
93
+ return this.repositoryMap[entityName];
94
+ }
95
+ /**
96
+ * Shortcut for `em.getRepository()`.
97
+ */
98
+ repo(entityName) {
99
+ return this.getRepository(entityName);
100
+ }
101
+ /**
102
+ * Gets EntityValidator instance
103
+ */
104
+ getValidator() {
105
+ return this.validator;
106
+ }
107
+ /**
108
+ * Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
109
+ */
110
+ async find(entityName, where, options = {}) {
111
+ if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) {
112
+ const em = this.getContext(false);
113
+ const fork = em.fork();
114
+ const ret = await fork.find(entityName, where, { ...options, disableIdentityMap: false });
115
+ fork.clear();
116
+ return ret;
117
+ }
118
+ const em = this.getContext();
119
+ em.prepareOptions(options);
120
+ await em.tryFlush(entityName, options);
121
+ entityName = utils_1.Utils.className(entityName);
122
+ where = await em.processWhere(entityName, where, options, 'read');
123
+ em.validator.validateParams(where);
124
+ options.orderBy = options.orderBy || {};
125
+ options.populate = await em.preparePopulate(entityName, options);
126
+ const populate = options.populate;
127
+ const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
128
+ const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
129
+ if (cached?.data) {
130
+ await em.entityLoader.populate(entityName, cached.data, populate, {
131
+ ...options,
132
+ ...em.getPopulateWhere(where, options),
133
+ convertCustomTypes: false,
134
+ ignoreLazyScalarProperties: true,
135
+ lookup: false,
136
+ });
137
+ return cached.data;
138
+ }
139
+ const meta = this.metadata.get(entityName);
140
+ options = { ...options };
141
+ // save the original hint value so we know it was infer/all
142
+ options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
143
+ options.populateWhere = await this.applyJoinedFilters(meta, { ...where }, options);
144
+ const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, ...options });
145
+ if (results.length === 0) {
146
+ await em.storeCache(options.cache, cached, []);
147
+ return [];
148
+ }
149
+ const ret = [];
150
+ for (const data of results) {
151
+ const entity = em.entityFactory.create(entityName, data, {
152
+ merge: true,
153
+ refresh: options.refresh,
154
+ schema: options.schema,
155
+ convertCustomTypes: true,
156
+ });
157
+ ret.push(entity);
158
+ }
159
+ const unique = utils_1.Utils.unique(ret);
160
+ await em.entityLoader.populate(entityName, unique, populate, {
161
+ ...options,
162
+ ...em.getPopulateWhere(where, options),
163
+ convertCustomTypes: false,
164
+ ignoreLazyScalarProperties: true,
165
+ lookup: false,
166
+ });
167
+ await em.unitOfWork.dispatchOnLoadEvent();
168
+ if (meta.virtual) {
169
+ await em.storeCache(options.cache, cached, () => ret);
170
+ }
171
+ else {
172
+ await em.storeCache(options.cache, cached, () => unique.map(e => (0, entity_1.helper)(e).toPOJO()));
173
+ }
174
+ return unique;
175
+ }
176
+ /**
177
+ * Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
178
+ */
179
+ async findAll(entityName, options) {
180
+ return this.find(entityName, options?.where ?? {}, options);
181
+ }
182
+ getPopulateWhere(where, options) {
183
+ if (options.populateWhere === undefined) {
184
+ options.populateWhere = this.config.get('populateWhere');
185
+ }
186
+ if (options.populateWhere === enums_1.PopulateHint.ALL) {
187
+ return { where: {}, populateWhere: options.populateWhere };
188
+ }
189
+ /* istanbul ignore next */
190
+ if (options.populateWhere === enums_1.PopulateHint.INFER) {
191
+ return { where, populateWhere: options.populateWhere };
192
+ }
193
+ return { where: options.populateWhere };
194
+ }
195
+ /**
196
+ * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
197
+ */
198
+ addFilter(name, cond, entityName, enabled = true) {
199
+ const options = { name, cond, default: enabled };
200
+ if (entityName) {
201
+ options.entity = utils_1.Utils.asArray(entityName).map(n => utils_1.Utils.className(n));
202
+ }
203
+ this.getContext(false).filters[name] = options;
204
+ }
205
+ /**
206
+ * Sets filter parameter values globally inside context defined by this entity manager.
207
+ * If you want to set shared value for all contexts, be sure to use the root entity manager.
208
+ */
209
+ setFilterParams(name, args) {
210
+ this.getContext().filterParams[name] = args;
211
+ }
212
+ /**
213
+ * Returns filter parameters for given filter set in this context.
214
+ */
215
+ getFilterParams(name) {
216
+ return this.getContext().filterParams[name];
217
+ }
218
+ /**
219
+ * Sets logger context for this entity manager.
220
+ */
221
+ setLoggerContext(context) {
222
+ this.getContext().loggerContext = context;
223
+ }
224
+ /**
225
+ * Gets logger context for this entity manager.
226
+ */
227
+ getLoggerContext() {
228
+ const em = this.getContext();
229
+ em.loggerContext ??= {};
230
+ return em.loggerContext;
231
+ }
232
+ setFlushMode(flushMode) {
233
+ this.getContext(false).flushMode = flushMode;
234
+ }
235
+ async processWhere(entityName, where, options, type) {
236
+ where = utils_1.QueryHelper.processWhere({
237
+ where,
238
+ entityName,
239
+ metadata: this.metadata,
240
+ platform: this.driver.getPlatform(),
241
+ convertCustomTypes: options.convertCustomTypes,
242
+ aliased: type === 'read',
243
+ });
244
+ where = (await this.applyFilters(entityName, where, options.filters ?? {}, type, options));
245
+ where = await this.applyDiscriminatorCondition(entityName, where);
246
+ return where;
247
+ }
248
+ applyDiscriminatorCondition(entityName, where) {
249
+ const meta = this.metadata.find(entityName);
250
+ if (!meta?.discriminatorValue) {
251
+ return where;
252
+ }
253
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
254
+ const children = [];
255
+ const lookUpChildren = (ret, type) => {
256
+ const children = types.filter(meta2 => meta2.extends === type);
257
+ children.forEach(m => lookUpChildren(ret, m.className));
258
+ ret.push(...children.filter(c => c.discriminatorValue));
259
+ return children;
260
+ };
261
+ lookUpChildren(children, meta.className);
262
+ /* istanbul ignore next */
263
+ where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
264
+ return where;
265
+ }
266
+ async applyJoinedFilters(meta, cond, options) {
267
+ const ret = {};
268
+ const populateWhere = options.populateWhere ?? this.config.get('populateWhere');
269
+ if (populateWhere === enums_1.PopulateHint.INFER) {
270
+ utils_1.Utils.merge(ret, cond);
271
+ }
272
+ else if (typeof populateWhere === 'object') {
273
+ utils_1.Utils.merge(ret, populateWhere);
274
+ }
275
+ if (options.populate) {
276
+ for (const hint of options.populate) {
277
+ const field = hint.field.split(':')[0];
278
+ const prop = meta.properties[field];
279
+ const joined = (prop.strategy || options.strategy || hint.strategy || this.config.get('loadStrategy')) === enums_1.LoadStrategy.JOINED && prop.kind !== enums_1.ReferenceKind.SCALAR;
280
+ if (!joined && !hint.filter) {
281
+ continue;
282
+ }
283
+ const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', { ...options, populate: hint.children });
284
+ const where2 = await this.applyJoinedFilters(prop.targetMeta, {}, { ...options, populate: hint.children, populateWhere: enums_1.PopulateHint.ALL });
285
+ if (utils_1.Utils.hasObjectKeys(where)) {
286
+ ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
287
+ }
288
+ if (utils_1.Utils.hasObjectKeys(where2)) {
289
+ if (ret[field]) {
290
+ utils_1.Utils.merge(ret[field], where2);
291
+ }
292
+ else {
293
+ ret[field] = where2;
294
+ }
295
+ }
296
+ }
297
+ }
298
+ return ret;
299
+ }
300
+ /**
301
+ * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
302
+ */
303
+ async autoJoinRefsForFilters(meta, options) {
304
+ if (!meta || !this.config.get('autoJoinRefsForFilters')) {
305
+ return;
306
+ }
307
+ const props = meta.relations.filter(prop => {
308
+ return [enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
309
+ && ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
310
+ });
311
+ const ret = options.populate;
312
+ for (const prop of props) {
313
+ const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
314
+ if (!utils_1.Utils.isEmpty(cond)) {
315
+ const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
316
+ if (populated.length > 0) {
317
+ populated.forEach(hint => hint.filter = true);
318
+ }
319
+ else {
320
+ ret.push({ field: `${prop.name}:ref`, strategy: enums_1.LoadStrategy.JOINED, filter: true });
321
+ }
322
+ }
323
+ }
324
+ }
325
+ /**
326
+ * @internal
327
+ */
328
+ async applyFilters(entityName, where, options, type, findOptions) {
329
+ const meta = this.metadata.find(entityName);
330
+ const filters = [];
331
+ const ret = [];
332
+ if (!meta) {
333
+ return where;
334
+ }
335
+ const active = new Set();
336
+ const push = (source) => {
337
+ const activeFilters = utils_1.QueryHelper
338
+ .getActiveFilters(entityName, options, source)
339
+ .filter(f => !active.has(f.name));
340
+ filters.push(...activeFilters);
341
+ activeFilters.forEach(f => active.add(f.name));
342
+ };
343
+ push(this.config.get('filters'));
344
+ push(this.filters);
345
+ push(meta.filters);
346
+ if (filters.length === 0) {
347
+ return where;
348
+ }
349
+ for (const filter of filters) {
350
+ let cond;
351
+ if (filter.cond instanceof Function) {
352
+ // @ts-ignore
353
+ const args = utils_1.Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
354
+ if (!args && filter.cond.length > 0 && filter.args !== false) {
355
+ throw new Error(`No arguments provided for filter '${filter.name}'`);
356
+ }
357
+ cond = await filter.cond(args, type, this, findOptions);
358
+ }
359
+ else {
360
+ cond = filter.cond;
361
+ }
362
+ ret.push(utils_1.QueryHelper.processWhere({
363
+ where: cond,
364
+ entityName,
365
+ metadata: this.metadata,
366
+ platform: this.driver.getPlatform(),
367
+ aliased: type === 'read',
368
+ }));
369
+ }
370
+ const conds = [...ret, where].filter(c => utils_1.Utils.hasObjectKeys(c));
371
+ return conds.length > 1 ? { $and: conds } : conds[0];
372
+ }
373
+ /**
374
+ * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as tuple
375
+ * where the first element is the array of entities, and the second is the count.
376
+ */
377
+ async findAndCount(entityName, where, options = {}) {
378
+ const em = this.getContext(false);
379
+ const copy = utils_1.Utils.copy(where);
380
+ const [entities, count] = await Promise.all([
381
+ em.find(entityName, where, options),
382
+ em.count(entityName, copy, options),
383
+ ]);
384
+ return [entities, count];
385
+ }
386
+ /**
387
+ * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
388
+ * Supports `before`, `after`, `first` and `last` options while disallowing `limit` and `offset`. Explicit `orderBy` option
389
+ * is required.
390
+ *
391
+ * Use `first` and `after` for forward pagination, or `last` and `before` for backward pagination.
392
+ *
393
+ * - `first` and `last` are numbers and serve as an alternative to `offset`, those options are mutually exclusive, use only one at a time
394
+ * - `before` and `after` specify the previous cursor value, it can be one of the:
395
+ * - `Cursor` instance
396
+ * - opaque string provided by `startCursor/endCursor` properties
397
+ * - POJO/entity instance
398
+ *
399
+ * ```ts
400
+ * const currentCursor = await em.findByCursor(User, {}, {
401
+ * first: 10,
402
+ * after: previousCursor, // cursor instance
403
+ * orderBy: { id: 'desc' },
404
+ * });
405
+ *
406
+ * // to fetch next page
407
+ * const nextCursor = await em.findByCursor(User, {}, {
408
+ * first: 10,
409
+ * after: currentCursor.endCursor, // opaque string
410
+ * orderBy: { id: 'desc' },
411
+ * });
412
+ *
413
+ * // to fetch next page
414
+ * const nextCursor2 = await em.findByCursor(User, {}, {
415
+ * first: 10,
416
+ * after: { id: lastSeenId }, // entity-like POJO
417
+ * orderBy: { id: 'desc' },
418
+ * });
419
+ * ```
420
+ *
421
+ * The `Cursor` object provides the following interface:
422
+ *
423
+ * ```ts
424
+ * Cursor<User> {
425
+ * items: [
426
+ * User { ... },
427
+ * User { ... },
428
+ * User { ... },
429
+ * ],
430
+ * totalCount: 50,
431
+ * startCursor: 'WzRd',
432
+ * endCursor: 'WzZd',
433
+ * hasPrevPage: true,
434
+ * hasNextPage: true,
435
+ * }
436
+ * ```
437
+ */
438
+ async findByCursor(entityName, where, options) {
439
+ const em = this.getContext(false);
440
+ entityName = utils_1.Utils.className(entityName);
441
+ options.overfetch ??= true;
442
+ if (utils_1.Utils.isEmpty(options.orderBy)) {
443
+ throw new Error('Explicit `orderBy` option required');
444
+ }
445
+ const [entities, count] = await em.findAndCount(entityName, where, options);
446
+ return new utils_1.Cursor(entities, count, options, this.metadata.get(entityName));
447
+ }
448
+ /**
449
+ * Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
450
+ * persisted. Returns the same entity instance (same object reference), but re-hydrated. If the entity is no longer
451
+ * in database, the method throws an error just like `em.findOneOrFail()` (and respects the same config options).
452
+ */
453
+ async refreshOrFail(entity, options = {}) {
454
+ const ret = await this.refresh(entity, options);
455
+ if (!ret) {
456
+ options.failHandler ??= this.config.get('findOneOrFailHandler');
457
+ const entityName = entity.constructor.name;
458
+ const where = (0, entity_1.helper)(entity).getPrimaryKey();
459
+ throw options.failHandler(entityName, where);
460
+ }
461
+ return ret;
462
+ }
463
+ /**
464
+ * Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
465
+ * persisted. Returns the same entity instance (same object reference), but re-hydrated. If the entity is no longer
466
+ * in database, the method returns `null`.
467
+ */
468
+ async refresh(entity, options = {}) {
469
+ const fork = this.fork();
470
+ const entityName = entity.constructor.name;
471
+ const reloaded = await fork.findOne(entityName, entity, {
472
+ schema: (0, entity_1.helper)(entity).__schema,
473
+ ...options,
474
+ flushMode: enums_1.FlushMode.COMMIT,
475
+ });
476
+ if (reloaded) {
477
+ this.config.getHydrator(this.metadata).hydrate(entity, (0, entity_1.helper)(entity).__meta, (0, entity_1.helper)(reloaded).toPOJO(), this.getEntityFactory(), 'full');
478
+ }
479
+ else {
480
+ this.getUnitOfWork().unsetIdentity(entity);
481
+ }
482
+ return reloaded ? entity : reloaded;
483
+ }
484
+ /**
485
+ * Finds first entity matching your `where` query.
486
+ */
487
+ async findOne(entityName, where, options = {}) {
488
+ if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) {
489
+ const em = this.getContext(false);
490
+ const fork = em.fork();
491
+ const ret = await fork.findOne(entityName, where, { ...options, disableIdentityMap: false });
492
+ fork.clear();
493
+ return ret;
494
+ }
495
+ const em = this.getContext();
496
+ entityName = utils_1.Utils.className(entityName);
497
+ em.prepareOptions(options);
498
+ let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
499
+ // query for a not managed entity which is already in the identity map as it
500
+ // was provided with a PK this entity does not exist in the db, there can't
501
+ // be any relations to it, so no need to deal with the populate hint
502
+ if (entity && !(0, entity_1.helper)(entity).__managed) {
503
+ return entity;
504
+ }
505
+ await em.tryFlush(entityName, options);
506
+ const meta = em.metadata.get(entityName);
507
+ where = await em.processWhere(entityName, where, options, 'read');
508
+ em.validator.validateEmptyWhere(where);
509
+ em.checkLockRequirements(options.lockMode, meta);
510
+ entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
511
+ const isOptimisticLocking = !utils_1.Utils.isDefined(options.lockMode) || options.lockMode === enums_1.LockMode.OPTIMISTIC;
512
+ if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
513
+ return em.lockAndPopulate(meta, entity, where, options);
514
+ }
515
+ em.validator.validateParams(where);
516
+ options.populate = await em.preparePopulate(entityName, options);
517
+ const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
518
+ const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
519
+ if (cached?.data) {
520
+ await em.entityLoader.populate(entityName, [cached.data], options.populate, {
521
+ ...options,
522
+ ...em.getPopulateWhere(where, options),
523
+ convertCustomTypes: false,
524
+ ignoreLazyScalarProperties: true,
525
+ lookup: false,
526
+ });
527
+ return cached.data;
528
+ }
529
+ options = { ...options };
530
+ // save the original hint value so we know it was infer/all
531
+ options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
532
+ options.populateWhere = await this.applyJoinedFilters(meta, { ...where }, options);
533
+ const data = await em.driver.findOne(entityName, where, {
534
+ ctx: em.transactionContext,
535
+ ...options,
536
+ });
537
+ if (!data) {
538
+ await em.storeCache(options.cache, cached, null);
539
+ return null;
540
+ }
541
+ entity = em.entityFactory.create(entityName, data, {
542
+ merge: true,
543
+ refresh: options.refresh,
544
+ schema: options.schema,
545
+ convertCustomTypes: true,
546
+ });
547
+ await em.lockAndPopulate(meta, entity, where, options);
548
+ await em.unitOfWork.dispatchOnLoadEvent();
549
+ await em.storeCache(options.cache, cached, () => (0, entity_1.helper)(entity).toPOJO());
550
+ return entity;
551
+ }
552
+ /**
553
+ * Finds first entity matching your `where` query. If nothing found, it will throw an error.
554
+ * If the `strict` option is specified and nothing is found or more than one matching entity is found, it will throw an error.
555
+ * You can override the factory for creating this method via `options.failHandler` locally
556
+ * or via `Configuration.findOneOrFailHandler` (`findExactlyOneOrFailHandler` when specifying `strict`) globally.
557
+ */
558
+ async findOneOrFail(entityName, where, options = {}) {
559
+ let entity;
560
+ let isStrictViolation = false;
561
+ if (options.strict) {
562
+ const ret = await this.find(entityName, where, { ...options, limit: 2 });
563
+ isStrictViolation = ret.length !== 1;
564
+ entity = ret[0];
565
+ }
566
+ else {
567
+ entity = await this.findOne(entityName, where, options);
568
+ }
569
+ if (!entity || isStrictViolation) {
570
+ const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
571
+ options.failHandler ??= this.config.get(key);
572
+ entityName = utils_1.Utils.className(entityName);
573
+ /* istanbul ignore next */
574
+ where = utils_1.Utils.isEntity(where) ? (0, entity_1.helper)(where).getPrimaryKey() : where;
575
+ throw options.failHandler(entityName, where);
576
+ }
577
+ return entity;
578
+ }
579
+ /**
580
+ * Creates or updates the entity, based on whether it is already present in the database.
581
+ * This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed
582
+ * entity instance. The method accepts either `entityName` together with the entity `data`, or just entity instance.
583
+ *
584
+ * ```ts
585
+ * // insert into "author" ("age", "email") values (33, 'foo@bar.com') on conflict ("email") do update set "age" = 41
586
+ * const author = await em.upsert(Author, { email: 'foo@bar.com', age: 33 });
587
+ * ```
588
+ *
589
+ * The entity data needs to contain either the primary key, or any other unique property. Let's consider the following example, where `Author.email` is a unique property:
590
+ *
591
+ * ```ts
592
+ * // insert into "author" ("age", "email") values (33, 'foo@bar.com') on conflict ("email") do update set "age" = 41
593
+ * // select "id" from "author" where "email" = 'foo@bar.com'
594
+ * const author = await em.upsert(Author, { email: 'foo@bar.com', age: 33 });
595
+ * ```
596
+ *
597
+ * Depending on the driver support, this will either use a returning query, or a separate select query, to fetch the primary key if it's missing from the `data`.
598
+ *
599
+ * If the entity is already present in current context, there won't be any queries - instead, the entity data will be assigned and an explicit `flush` will be required for those changes to be persisted.
600
+ */
601
+ async upsert(entityNameOrEntity, data, options = {}) {
602
+ const em = this.getContext(false);
603
+ em.prepareOptions(options);
604
+ let entityName;
605
+ let where;
606
+ let entity;
607
+ if (data === undefined) {
608
+ entityName = entityNameOrEntity.constructor.name;
609
+ data = entityNameOrEntity;
610
+ }
611
+ else {
612
+ entityName = utils_1.Utils.className(entityNameOrEntity);
613
+ }
614
+ const meta = this.metadata.get(entityName);
615
+ const convertCustomTypes = !utils_1.Utils.isEntity(data);
616
+ if (utils_1.Utils.isEntity(data)) {
617
+ entity = data;
618
+ if ((0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__em === em) {
619
+ em.entityFactory.mergeData(meta, entity, data, { initialized: true });
620
+ return entity;
621
+ }
622
+ where = (0, entity_1.helper)(entity).getPrimaryKey();
623
+ data = em.comparator.prepareEntity(entity);
624
+ }
625
+ else {
626
+ data = utils_1.Utils.copy(utils_1.QueryHelper.processParams(data));
627
+ where = utils_1.Utils.extractPK(data, meta);
628
+ if (where) {
629
+ const exists = em.unitOfWork.getById(entityName, where, options.schema);
630
+ if (exists) {
631
+ return em.assign(exists, data);
632
+ }
633
+ }
634
+ }
635
+ const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
636
+ const propIndex = unique.findIndex(p => data[p] != null);
637
+ if (options.onConflictFields || where == null) {
638
+ if (propIndex >= 0) {
639
+ where = { [unique[propIndex]]: data[unique[propIndex]] };
640
+ }
641
+ else if (meta.uniques.length > 0) {
642
+ for (const u of meta.uniques) {
643
+ if (utils_1.Utils.asArray(u.properties).every(p => data[p] != null)) {
644
+ where = utils_1.Utils.asArray(u.properties).reduce((o, key) => {
645
+ o[key] = data[key];
646
+ return o;
647
+ }, {});
648
+ break;
649
+ }
650
+ }
651
+ }
652
+ }
653
+ if (where == null) {
654
+ const compositeUniqueProps = meta.uniques.map(u => utils_1.Utils.asArray(u.properties).join(' + '));
655
+ const uniqueProps = meta.primaryKeys.concat(...unique).concat(compositeUniqueProps);
656
+ throw new Error(`Unique property value required for upsert, provide one of: ${uniqueProps.join(', ')}`);
657
+ }
658
+ data = utils_1.QueryHelper.processObjectParams(data);
659
+ em.validator.validateParams(data, 'insert data');
660
+ if (em.eventManager.hasListeners(enums_1.EventType.beforeUpsert, meta)) {
661
+ await em.eventManager.dispatchEvent(enums_1.EventType.beforeUpsert, { entity: data, em, meta }, meta);
662
+ }
663
+ const ret = await em.driver.nativeUpdate(entityName, where, data, {
664
+ ctx: em.transactionContext,
665
+ upsert: true,
666
+ convertCustomTypes,
667
+ ...options,
668
+ });
669
+ entity ??= em.entityFactory.create(entityName, data, {
670
+ refresh: true,
671
+ initialized: true,
672
+ schema: options.schema,
673
+ convertCustomTypes: true,
674
+ });
675
+ em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data, ret.row, meta);
676
+ const uniqueFields = options.onConflictFields ?? (utils_1.Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
677
+ const returning = (0, utils_1.getOnConflictReturningFields)(meta, data, uniqueFields, options);
678
+ if (options.onConflictAction === 'ignore' || !(0, entity_1.helper)(entity).hasPrimaryKey() || (returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
679
+ const where = {};
680
+ uniqueFields.forEach(prop => where[prop] = data[prop]);
681
+ const data2 = await this.driver.findOne(meta.className, where, {
682
+ fields: returning,
683
+ ctx: em.transactionContext,
684
+ convertCustomTypes: true,
685
+ connectionType: 'write',
686
+ });
687
+ em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full');
688
+ }
689
+ // recompute the data as there might be some values missing (e.g. those with db column defaults)
690
+ const snapshot = this.comparator.prepareEntity(entity);
691
+ em.unitOfWork.register(entity, snapshot, { refresh: true });
692
+ if (em.eventManager.hasListeners(enums_1.EventType.afterUpsert, meta)) {
693
+ await em.eventManager.dispatchEvent(enums_1.EventType.afterUpsert, { entity, em, meta }, meta);
694
+ }
695
+ return entity;
696
+ }
697
+ /**
698
+ * Creates or updates the entity, based on whether it is already present in the database.
699
+ * This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed
700
+ * entity instance. The method accepts either `entityName` together with the entity `data`, or just entity instance.
701
+ *
702
+ * ```ts
703
+ * // insert into "author" ("age", "email") values (33, 'foo@bar.com') on conflict ("email") do update set "age" = 41
704
+ * const authors = await em.upsertMany(Author, [{ email: 'foo@bar.com', age: 33 }, ...]);
705
+ * ```
706
+ *
707
+ * The entity data needs to contain either the primary key, or any other unique property. Let's consider the following example, where `Author.email` is a unique property:
708
+ *
709
+ * ```ts
710
+ * // insert into "author" ("age", "email") values (33, 'foo@bar.com'), (666, 'lol@lol.lol') on conflict ("email") do update set "age" = excluded."age"
711
+ * // select "id" from "author" where "email" = 'foo@bar.com'
712
+ * const author = await em.upsertMany(Author, [
713
+ * { email: 'foo@bar.com', age: 33 },
714
+ * { email: 'lol@lol.lol', age: 666 },
715
+ * ]);
716
+ * ```
717
+ *
718
+ * Depending on the driver support, this will either use a returning query, or a separate select query, to fetch the primary key if it's missing from the `data`.
719
+ *
720
+ * If the entity is already present in current context, there won't be any queries - instead, the entity data will be assigned and an explicit `flush` will be required for those changes to be persisted.
721
+ */
722
+ async upsertMany(entityNameOrEntity, data, options = {}) {
723
+ const em = this.getContext(false);
724
+ em.prepareOptions(options);
725
+ let entityName;
726
+ let propIndex;
727
+ if (data === undefined) {
728
+ entityName = entityNameOrEntity[0].constructor.name;
729
+ data = entityNameOrEntity;
730
+ }
731
+ else {
732
+ entityName = utils_1.Utils.className(entityNameOrEntity);
733
+ }
734
+ const batchSize = options.batchSize ?? this.config.get('batchSize');
735
+ if (data.length > batchSize) {
736
+ const ret = [];
737
+ for (let i = 0; i < data.length; i += batchSize) {
738
+ const chunk = data.slice(i, i + batchSize);
739
+ ret.push(...await this.upsertMany(entityName, chunk, options));
740
+ }
741
+ return ret;
742
+ }
743
+ const meta = this.metadata.get(entityName);
744
+ const convertCustomTypes = !utils_1.Utils.isEntity(data[0]);
745
+ const allData = [];
746
+ const allWhere = [];
747
+ const entities = new Map();
748
+ const entitiesByData = new Map();
749
+ for (let i = 0; i < data.length; i++) {
750
+ let row = data[i];
751
+ let where;
752
+ if (utils_1.Utils.isEntity(row)) {
753
+ const entity = row;
754
+ if ((0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__em === em) {
755
+ em.entityFactory.mergeData(meta, entity, row, { initialized: true });
756
+ entities.set(entity, row);
757
+ entitiesByData.set(row, entity);
758
+ continue;
759
+ }
760
+ where = (0, entity_1.helper)(entity).getPrimaryKey();
761
+ row = em.comparator.prepareEntity(entity);
762
+ }
763
+ else {
764
+ row = data[i] = utils_1.Utils.copy(utils_1.QueryHelper.processParams(row));
765
+ where = utils_1.Utils.extractPK(row, meta);
766
+ if (where) {
767
+ const exists = em.unitOfWork.getById(entityName, where, options.schema);
768
+ if (exists) {
769
+ em.assign(exists, row);
770
+ entities.set(exists, row);
771
+ entitiesByData.set(row, exists);
772
+ continue;
773
+ }
774
+ }
775
+ }
776
+ const unique = meta.props.filter(p => p.unique).map(p => p.name);
777
+ propIndex = unique.findIndex(p => row[p] != null);
778
+ if (options.onConflictFields || where == null) {
779
+ if (propIndex >= 0) {
780
+ where = { [unique[propIndex]]: row[unique[propIndex]] };
781
+ }
782
+ else if (meta.uniques.length > 0) {
783
+ for (const u of meta.uniques) {
784
+ if (utils_1.Utils.asArray(u.properties).every(p => row[p] != null)) {
785
+ where = utils_1.Utils.asArray(u.properties).reduce((o, key) => {
786
+ o[key] = row[key];
787
+ return o;
788
+ }, {});
789
+ break;
790
+ }
791
+ }
792
+ }
793
+ }
794
+ if (where == null) {
795
+ const compositeUniqueProps = meta.uniques.map(u => utils_1.Utils.asArray(u.properties).join(' + '));
796
+ const uniqueProps = meta.primaryKeys.concat(...unique).concat(compositeUniqueProps);
797
+ throw new Error(`Unique property value required for upsert, provide one of: ${uniqueProps.join(', ')}`);
798
+ }
799
+ row = utils_1.QueryHelper.processObjectParams(row);
800
+ where = utils_1.QueryHelper.processWhere({
801
+ where,
802
+ entityName,
803
+ metadata: this.metadata,
804
+ platform: this.getPlatform(),
805
+ });
806
+ em.validator.validateParams(row, 'insert data');
807
+ allData.push(row);
808
+ allWhere.push(where);
809
+ }
810
+ if (entities.size === data.length) {
811
+ return [...entities.keys()];
812
+ }
813
+ if (em.eventManager.hasListeners(enums_1.EventType.beforeUpsert, meta)) {
814
+ for (const dto of data) {
815
+ const entity = entitiesByData.get(dto) ?? dto;
816
+ await em.eventManager.dispatchEvent(enums_1.EventType.beforeUpsert, { entity, em, meta }, meta);
817
+ }
818
+ }
819
+ const res = await em.driver.nativeUpdateMany(entityName, allWhere, allData, {
820
+ ctx: em.transactionContext,
821
+ upsert: true,
822
+ convertCustomTypes,
823
+ ...options,
824
+ });
825
+ entities.clear();
826
+ entitiesByData.clear();
827
+ const loadPK = new Map();
828
+ allData.forEach((row, i) => {
829
+ const entity = utils_1.Utils.isEntity(data[i]) ? data[i] : em.entityFactory.create(entityName, row, {
830
+ refresh: true,
831
+ initialized: true,
832
+ schema: options.schema,
833
+ convertCustomTypes: true,
834
+ });
835
+ em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, utils_1.Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta);
836
+ if (!(0, entity_1.helper)(entity).hasPrimaryKey()) {
837
+ loadPK.set(entity, allWhere[i]);
838
+ }
839
+ entities.set(entity, row);
840
+ entitiesByData.set(row, entity);
841
+ });
842
+ // skip if we got the PKs via returning statement (`rows`)
843
+ const uniqueFields = options.onConflictFields ?? (utils_1.Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => utils_1.Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
844
+ const returning = (0, utils_1.getOnConflictReturningFields)(meta, data[0], uniqueFields, options);
845
+ const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
846
+ if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
847
+ const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
848
+ const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
849
+ for (const cond of loadPK.values()) {
850
+ utils_1.Utils.keys(cond).forEach(key => add.add(key));
851
+ }
852
+ const where = { $or: [] };
853
+ data.forEach((item, idx) => {
854
+ where.$or[idx] = {};
855
+ uniqueFields.forEach(prop => {
856
+ where.$or[idx][prop] = item[prop];
857
+ });
858
+ });
859
+ const data2 = await this.driver.find(meta.className, where, {
860
+ fields: returning.concat(...add).concat(...uniqueFields),
861
+ ctx: em.transactionContext,
862
+ convertCustomTypes: true,
863
+ connectionType: 'write',
864
+ });
865
+ for (const [entity, cond] of loadPK.entries()) {
866
+ const row = data2.find(row => {
867
+ const tmp = {};
868
+ add.forEach(k => {
869
+ if (!meta.properties[k]?.primary) {
870
+ tmp[k] = row[k];
871
+ }
872
+ });
873
+ return this.comparator.matching(entityName, cond, tmp);
874
+ });
875
+ /* istanbul ignore next */
876
+ if (!row) {
877
+ throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
878
+ }
879
+ em.getHydrator().hydrate(entity, meta, row, em.entityFactory, 'full');
880
+ }
881
+ if (loadPK.size !== data2.length) {
882
+ for (let i = 0; i < allData.length; i++) {
883
+ const data = allData[i];
884
+ const cond = uniqueFields.reduce((a, b) => {
885
+ // @ts-ignore
886
+ a[b] = data[b];
887
+ return a;
888
+ }, {});
889
+ const entity = entitiesByData.get(data);
890
+ const row = data2.find(item => {
891
+ const pk = uniqueFields.reduce((a, b) => {
892
+ // @ts-ignore
893
+ a[b] = item[b];
894
+ return a;
895
+ }, {});
896
+ return this.comparator.matching(entityName, cond, pk);
897
+ });
898
+ /* istanbul ignore next */
899
+ if (!row) {
900
+ throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
901
+ }
902
+ em.getHydrator().hydrate(entity, meta, row, em.entityFactory, 'full');
903
+ }
904
+ }
905
+ }
906
+ for (const [entity] of entities) {
907
+ // recompute the data as there might be some values missing (e.g. those with db column defaults)
908
+ const snapshot = this.comparator.prepareEntity(entity);
909
+ em.unitOfWork.register(entity, snapshot, { refresh: true });
910
+ }
911
+ if (em.eventManager.hasListeners(enums_1.EventType.afterUpsert, meta)) {
912
+ for (const [entity] of entities) {
913
+ await em.eventManager.dispatchEvent(enums_1.EventType.afterUpsert, { entity, em, meta }, meta);
914
+ }
915
+ }
916
+ return [...entities.keys()];
917
+ }
918
+ /**
919
+ * Runs your callback wrapped inside a database transaction.
920
+ */
921
+ async transactional(cb, options = {}) {
922
+ const em = this.getContext(false);
923
+ if (this.disableTransactions) {
924
+ return cb(em);
925
+ }
926
+ const fork = em.fork({
927
+ clear: options.clear ?? false, // state will be merged once resolves
928
+ flushMode: options.flushMode,
929
+ cloneEventManager: true,
930
+ disableTransactions: options.ignoreNestedTransactions,
931
+ loggerContext: options.loggerContext,
932
+ });
933
+ options.ctx ??= em.transactionContext;
934
+ return utils_1.TransactionContext.create(fork, async () => {
935
+ return fork.getConnection().transactional(async (trx) => {
936
+ fork.transactionContext = trx;
937
+ fork.eventManager.registerSubscriber({
938
+ afterFlush(args) {
939
+ args.uow.getChangeSets()
940
+ .filter(cs => [unit_of_work_1.ChangeSetType.DELETE, unit_of_work_1.ChangeSetType.DELETE_EARLY].includes(cs.type))
941
+ .forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
942
+ },
943
+ });
944
+ const ret = await cb(fork);
945
+ await fork.flush();
946
+ // ensure all entities from inner context are merged to the upper one
947
+ for (const entity of fork.unitOfWork.getIdentityMap()) {
948
+ em.unitOfWork.register(entity);
949
+ entity.__helper.__em = em;
950
+ }
951
+ return ret;
952
+ }, { ...options, eventBroadcaster: new events_1.TransactionEventBroadcaster(fork) });
953
+ });
954
+ }
955
+ /**
956
+ * Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
957
+ */
958
+ async begin(options = {}) {
959
+ if (this.disableTransactions) {
960
+ return;
961
+ }
962
+ const em = this.getContext(false);
963
+ em.transactionContext = await em.getConnection('write').begin({
964
+ ...options,
965
+ eventBroadcaster: new events_1.TransactionEventBroadcaster(em),
966
+ });
967
+ }
968
+ /**
969
+ * Commits the transaction bound to this EntityManager. Flushes before doing the actual commit query.
970
+ */
971
+ async commit() {
972
+ const em = this.getContext(false);
973
+ if (this.disableTransactions) {
974
+ await em.flush();
975
+ return;
976
+ }
977
+ if (!em.transactionContext) {
978
+ throw errors_1.ValidationError.transactionRequired();
979
+ }
980
+ await em.flush();
981
+ await em.getConnection('write').commit(em.transactionContext, new events_1.TransactionEventBroadcaster(em));
982
+ delete em.transactionContext;
983
+ }
984
+ /**
985
+ * Rollbacks the transaction bound to this EntityManager.
986
+ */
987
+ async rollback() {
988
+ if (this.disableTransactions) {
989
+ return;
990
+ }
991
+ const em = this.getContext(false);
992
+ if (!em.transactionContext) {
993
+ throw errors_1.ValidationError.transactionRequired();
994
+ }
995
+ await em.getConnection('write').rollback(em.transactionContext, new events_1.TransactionEventBroadcaster(em));
996
+ delete em.transactionContext;
997
+ em.unitOfWork.clearActionsQueue();
998
+ }
999
+ /**
1000
+ * Runs your callback wrapped inside a database transaction.
1001
+ */
1002
+ async lock(entity, lockMode, options = {}) {
1003
+ options = utils_1.Utils.isPlainObject(options) ? options : { lockVersion: options };
1004
+ await this.getUnitOfWork().lock(entity, { lockMode, ...options });
1005
+ }
1006
+ /**
1007
+ * Fires native insert query. Calling this has no side effects on the context (identity map).
1008
+ */
1009
+ async insert(entityNameOrEntity, data, options = {}) {
1010
+ const em = this.getContext(false);
1011
+ em.prepareOptions(options);
1012
+ let entityName;
1013
+ if (data === undefined) {
1014
+ entityName = entityNameOrEntity.constructor.name;
1015
+ data = entityNameOrEntity;
1016
+ }
1017
+ else {
1018
+ entityName = utils_1.Utils.className(entityNameOrEntity);
1019
+ }
1020
+ if (utils_1.Utils.isEntity(data)) {
1021
+ if (options.schema && (0, entity_1.helper)(data).getSchema() == null) {
1022
+ (0, entity_1.helper)(data).setSchema(options.schema);
1023
+ }
1024
+ if (!(0, entity_1.helper)(data).__managed) {
1025
+ // the entity might have been created via `em.create()`, which adds it to the persist stack automatically
1026
+ em.unitOfWork.getPersistStack().delete(data);
1027
+ // it can be also in the identity map if it had a PK value already
1028
+ em.unitOfWork.unsetIdentity(data);
1029
+ }
1030
+ const meta = (0, entity_1.helper)(data).__meta;
1031
+ const payload = em.comparator.prepareEntity(data);
1032
+ const cs = new unit_of_work_1.ChangeSet(data, unit_of_work_1.ChangeSetType.CREATE, payload, meta);
1033
+ await em.unitOfWork.getChangeSetPersister().executeInserts([cs], { ctx: em.transactionContext, ...options });
1034
+ return cs.getPrimaryKey();
1035
+ }
1036
+ data = utils_1.QueryHelper.processObjectParams(data);
1037
+ em.validator.validateParams(data, 'insert data');
1038
+ const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
1039
+ return res.insertId;
1040
+ }
1041
+ /**
1042
+ * Fires native multi-insert query. Calling this has no side effects on the context (identity map).
1043
+ */
1044
+ async insertMany(entityNameOrEntities, data, options = {}) {
1045
+ const em = this.getContext(false);
1046
+ em.prepareOptions(options);
1047
+ let entityName;
1048
+ if (data === undefined) {
1049
+ entityName = entityNameOrEntities[0].constructor.name;
1050
+ data = entityNameOrEntities;
1051
+ }
1052
+ else {
1053
+ entityName = utils_1.Utils.className(entityNameOrEntities);
1054
+ }
1055
+ if (data.length === 0) {
1056
+ return [];
1057
+ }
1058
+ if (utils_1.Utils.isEntity(data[0])) {
1059
+ const meta = (0, entity_1.helper)(data[0]).__meta;
1060
+ const css = data.map(row => {
1061
+ if (options.schema && (0, entity_1.helper)(row).getSchema() == null) {
1062
+ (0, entity_1.helper)(row).setSchema(options.schema);
1063
+ }
1064
+ if (!(0, entity_1.helper)(row).__managed) {
1065
+ // the entity might have been created via `em.create()`, which adds it to the persist stack automatically
1066
+ em.unitOfWork.getPersistStack().delete(row);
1067
+ // it can be also in the identity map if it had a PK value already
1068
+ em.unitOfWork.unsetIdentity(row);
1069
+ }
1070
+ const payload = em.comparator.prepareEntity(row);
1071
+ return new unit_of_work_1.ChangeSet(row, unit_of_work_1.ChangeSetType.CREATE, payload, meta);
1072
+ });
1073
+ await em.unitOfWork.getChangeSetPersister().executeInserts(css, { ctx: em.transactionContext, ...options });
1074
+ return css.map(cs => cs.getPrimaryKey());
1075
+ }
1076
+ data = data.map(row => utils_1.QueryHelper.processObjectParams(row));
1077
+ data.forEach(row => em.validator.validateParams(row, 'insert data'));
1078
+ const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
1079
+ if (res.insertedIds) {
1080
+ return res.insertedIds;
1081
+ }
1082
+ return [res.insertId];
1083
+ }
1084
+ /**
1085
+ * Fires native update query. Calling this has no side effects on the context (identity map).
1086
+ */
1087
+ async nativeUpdate(entityName, where, data, options = {}) {
1088
+ const em = this.getContext(false);
1089
+ em.prepareOptions(options);
1090
+ entityName = utils_1.Utils.className(entityName);
1091
+ data = utils_1.QueryHelper.processObjectParams(data);
1092
+ where = await em.processWhere(entityName, where, options, 'update');
1093
+ em.validator.validateParams(data, 'update data');
1094
+ em.validator.validateParams(where, 'update condition');
1095
+ const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
1096
+ return res.affectedRows;
1097
+ }
1098
+ /**
1099
+ * Fires native delete query. Calling this has no side effects on the context (identity map).
1100
+ */
1101
+ async nativeDelete(entityName, where, options = {}) {
1102
+ const em = this.getContext(false);
1103
+ em.prepareOptions(options);
1104
+ entityName = utils_1.Utils.className(entityName);
1105
+ where = await em.processWhere(entityName, where, options, 'delete');
1106
+ em.validator.validateParams(where, 'delete condition');
1107
+ const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
1108
+ return res.affectedRows;
1109
+ }
1110
+ /**
1111
+ * Maps raw database result to an entity and merges it to this EntityManager.
1112
+ */
1113
+ map(entityName, result, options = {}) {
1114
+ entityName = utils_1.Utils.className(entityName);
1115
+ const meta = this.metadata.get(entityName);
1116
+ const data = this.driver.mapResult(result, meta);
1117
+ Object.keys(data).forEach(k => {
1118
+ const prop = meta.properties[k];
1119
+ if (prop && prop.kind === enums_1.ReferenceKind.SCALAR && enums_1.SCALAR_TYPES.includes(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1120
+ data[k] = this.validator.validateProperty(prop, data[k], data);
1121
+ }
1122
+ });
1123
+ return this.merge(entityName, data, {
1124
+ convertCustomTypes: true,
1125
+ refresh: true, ...options,
1126
+ });
1127
+ }
1128
+ /**
1129
+ * Merges given entity to this EntityManager so it becomes managed. You can force refreshing of existing entities
1130
+ * via second parameter. By default, it will return already loaded entities without modifying them.
1131
+ */
1132
+ merge(entityName, data, options = {}) {
1133
+ const em = this.getContext();
1134
+ if (utils_1.Utils.isEntity(entityName)) {
1135
+ return em.merge(entityName.constructor.name, entityName, data);
1136
+ }
1137
+ options.schema ??= em._schema;
1138
+ entityName = utils_1.Utils.className(entityName);
1139
+ em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1140
+ let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1141
+ if (entity && (0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__initialized && !options.refresh) {
1142
+ return entity;
1143
+ }
1144
+ const meta = em.metadata.find(entityName);
1145
+ const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
1146
+ entity = utils_1.Utils.isEntity(data) ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
1147
+ em.validator.validate(entity, data, childMeta ?? meta);
1148
+ em.unitOfWork.merge(entity);
1149
+ return entity;
1150
+ }
1151
+ /**
1152
+ * Creates new instance of given entity and populates it with given data.
1153
+ * The entity constructor will be used unless you provide `{ managed: true }` in the options parameter.
1154
+ * The constructor will be given parameters based on the defined constructor of the entity. If the constructor
1155
+ * parameter matches a property name, its value will be extracted from `data`. If no matching property exists,
1156
+ * the whole `data` parameter will be passed. This means we can also define `constructor(data: Partial<T>)` and
1157
+ * `em.create()` will pass the data into it (unless we have a property named `data` too).
1158
+ *
1159
+ The parameters are strictly checked, you need to provide all required properties. You can use `OptionalProps`
1160
+ symbol to omit some properties from this check without making them optional. Alternatively, use `partial: true`
1161
+ in the options to disable the strict checks for required properties. This option has no effect on runtime.
1162
+ */
1163
+ create(entityName, data, options = {}) {
1164
+ const em = this.getContext();
1165
+ options.schema ??= em._schema;
1166
+ const entity = em.entityFactory.create(entityName, data, {
1167
+ ...options,
1168
+ newEntity: !options.managed,
1169
+ merge: options.managed,
1170
+ });
1171
+ options.persist ??= em.config.get('persistOnCreate');
1172
+ if (options.persist) {
1173
+ em.persist(entity);
1174
+ }
1175
+ return entity;
1176
+ }
1177
+ /**
1178
+ * Shortcut for `wrap(entity).assign(data, { em })`
1179
+ */
1180
+ assign(entity, data, options = {}) {
1181
+ return entity_1.EntityAssigner.assign(entity, data, { em: this.getContext(), ...options });
1182
+ }
1183
+ /**
1184
+ * Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
1185
+ */
1186
+ getReference(entityName, id, options = {}) {
1187
+ options.schema ??= this.schema;
1188
+ options.convertCustomTypes ??= false;
1189
+ const meta = this.metadata.get(utils_1.Utils.className(entityName));
1190
+ if (utils_1.Utils.isPrimaryKey(id)) {
1191
+ if (meta.compositePK) {
1192
+ throw errors_1.ValidationError.invalidCompositeIdentifier(meta);
1193
+ }
1194
+ id = [id];
1195
+ }
1196
+ const entity = this.getEntityFactory().createReference(entityName, id, { merge: true, ...options });
1197
+ if (options.wrapped) {
1198
+ return entity_1.Reference.create(entity);
1199
+ }
1200
+ return entity;
1201
+ }
1202
+ /**
1203
+ * Returns total number of entities matching your `where` query.
1204
+ */
1205
+ async count(entityName, where = {}, options = {}) {
1206
+ const em = this.getContext(false);
1207
+ // Shallow copy options since the object will be modified when deleting orderBy
1208
+ options = {
1209
+ schema: em._schema,
1210
+ ...options,
1211
+ };
1212
+ entityName = utils_1.Utils.className(entityName);
1213
+ where = await em.processWhere(entityName, where, options, 'read');
1214
+ options.populate = await em.preparePopulate(entityName, options);
1215
+ em.validator.validateParams(where);
1216
+ delete options.orderBy;
1217
+ const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
1218
+ const cached = await em.tryCache(entityName, options.cache, cacheKey);
1219
+ if (cached?.data) {
1220
+ return cached.data;
1221
+ }
1222
+ const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
1223
+ await em.storeCache(options.cache, cached, () => +count);
1224
+ return +count;
1225
+ }
1226
+ /**
1227
+ * Tells the EntityManager to make an instance managed and persistent.
1228
+ * The entity will be entered into the database at or before transaction commit or as a result of the flush operation.
1229
+ */
1230
+ persist(entity) {
1231
+ const em = this.getContext();
1232
+ if (utils_1.Utils.isEntity(entity)) {
1233
+ // do not cascade just yet, cascading of entities in persist stack is done when flushing
1234
+ em.unitOfWork.persist(entity, undefined, { cascade: false });
1235
+ return em;
1236
+ }
1237
+ const entities = utils_1.Utils.asArray(entity);
1238
+ for (const ent of entities) {
1239
+ if (!utils_1.Utils.isEntity(ent, true)) {
1240
+ /* istanbul ignore next */
1241
+ const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor.name) : undefined;
1242
+ throw errors_1.ValidationError.notDiscoveredEntity(ent, meta);
1243
+ }
1244
+ // do not cascade just yet, cascading of entities in persist stack is done when flushing
1245
+ em.unitOfWork.persist(entity_1.Reference.unwrapReference(ent), undefined, { cascade: false });
1246
+ }
1247
+ return this;
1248
+ }
1249
+ /**
1250
+ * Persists your entity immediately, flushing all not yet persisted changes to the database too.
1251
+ * Equivalent to `em.persist(e).flush()`.
1252
+ */
1253
+ async persistAndFlush(entity) {
1254
+ await this.persist(entity).flush();
1255
+ }
1256
+ /**
1257
+ * Marks entity for removal.
1258
+ * A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
1259
+ *
1260
+ * To remove entities by condition, use `em.nativeDelete()`.
1261
+ */
1262
+ remove(entity) {
1263
+ const em = this.getContext();
1264
+ if (utils_1.Utils.isEntity(entity)) {
1265
+ // do not cascade just yet, cascading of entities in persist stack is done when flushing
1266
+ em.unitOfWork.remove(entity, undefined, { cascade: false });
1267
+ return em;
1268
+ }
1269
+ const entities = utils_1.Utils.asArray(entity, true);
1270
+ for (const ent of entities) {
1271
+ if (!utils_1.Utils.isEntity(ent, true)) {
1272
+ throw new Error(`You need to pass entity instance or reference to 'em.remove()'. To remove entities by condition, use 'em.nativeDelete()'.`);
1273
+ }
1274
+ // do not cascade just yet, cascading of entities in remove stack is done when flushing
1275
+ em.unitOfWork.remove(entity_1.Reference.unwrapReference(ent), undefined, { cascade: false });
1276
+ }
1277
+ return em;
1278
+ }
1279
+ /**
1280
+ * Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
1281
+ * Equivalent to `em.remove(e).flush()`
1282
+ */
1283
+ async removeAndFlush(entity) {
1284
+ await this.remove(entity).flush();
1285
+ }
1286
+ /**
1287
+ * Flushes all changes to objects that have been queued up to now to the database.
1288
+ * This effectively synchronizes the in-memory state of managed objects with the database.
1289
+ */
1290
+ async flush() {
1291
+ await this.getUnitOfWork().commit();
1292
+ }
1293
+ /**
1294
+ * @internal
1295
+ */
1296
+ async tryFlush(entityName, options) {
1297
+ const em = this.getContext();
1298
+ const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
1299
+ entityName = utils_1.Utils.className(entityName);
1300
+ const meta = em.metadata.get(entityName);
1301
+ if (flushMode === enums_1.FlushMode.COMMIT) {
1302
+ return;
1303
+ }
1304
+ if (flushMode === enums_1.FlushMode.ALWAYS || em.getUnitOfWork().shouldAutoFlush(meta)) {
1305
+ await em.flush();
1306
+ }
1307
+ }
1308
+ /**
1309
+ * Clears the EntityManager. All entities that are currently managed by this EntityManager become detached.
1310
+ */
1311
+ clear() {
1312
+ this.getContext().unitOfWork.clear();
1313
+ }
1314
+ /**
1315
+ * Checks whether given property can be populated on the entity.
1316
+ */
1317
+ canPopulate(entityName, property) {
1318
+ entityName = utils_1.Utils.className(entityName);
1319
+ // eslint-disable-next-line prefer-const
1320
+ let [p, ...parts] = property.split('.');
1321
+ const meta = this.metadata.find(entityName);
1322
+ if (!meta) {
1323
+ return true;
1324
+ }
1325
+ if (p.includes(':')) {
1326
+ p = p.split(':', 2)[0];
1327
+ }
1328
+ const ret = p in meta.root.properties;
1329
+ if (!ret) {
1330
+ return !!this.metadata.find(property)?.pivotTable;
1331
+ }
1332
+ if (parts.length > 0) {
1333
+ return this.canPopulate((meta.root.properties)[p].type, parts.join('.'));
1334
+ }
1335
+ return ret;
1336
+ }
1337
+ /**
1338
+ * Loads specified relations in batch. This will execute one query for each relation, that will populate it on all the specified entities.
1339
+ */
1340
+ async populate(entities, populate, options = {}) {
1341
+ const arr = utils_1.Utils.asArray(entities);
1342
+ if (arr.length === 0) {
1343
+ return entities;
1344
+ }
1345
+ const em = this.getContext();
1346
+ em.prepareOptions(options);
1347
+ const entityName = arr[0].constructor.name;
1348
+ const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1349
+ await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1350
+ return entities;
1351
+ }
1352
+ /**
1353
+ * Returns new EntityManager instance with its own identity map
1354
+ */
1355
+ fork(options = {}) {
1356
+ const em = options.disableContextResolution ? this : this.getContext(false);
1357
+ options.clear ??= true;
1358
+ options.useContext ??= false;
1359
+ options.freshEventManager ??= false;
1360
+ options.cloneEventManager ??= false;
1361
+ const eventManager = options.freshEventManager
1362
+ ? new events_1.EventManager(em.config.get('subscribers'))
1363
+ : options.cloneEventManager
1364
+ ? em.eventManager.clone()
1365
+ : em.eventManager;
1366
+ // we need to allow global context here as forking from global EM is fine
1367
+ const allowGlobalContext = em.config.get('allowGlobalContext');
1368
+ em.config.set('allowGlobalContext', true);
1369
+ const fork = new em.constructor(em.config, em.driver, em.metadata, options.useContext, eventManager);
1370
+ fork.setFlushMode(options.flushMode ?? em.flushMode);
1371
+ fork.disableTransactions = options.disableTransactions ?? this.disableTransactions ?? this.config.get('disableTransactions');
1372
+ em.config.set('allowGlobalContext', allowGlobalContext);
1373
+ fork.filters = { ...em.filters };
1374
+ fork.filterParams = utils_1.Utils.copy(em.filterParams);
1375
+ fork.loggerContext = utils_1.Utils.merge({}, em.loggerContext, options.loggerContext);
1376
+ fork._schema = options.schema ?? em._schema;
1377
+ if (!options.clear) {
1378
+ for (const entity of em.unitOfWork.getIdentityMap()) {
1379
+ fork.unitOfWork.register(entity);
1380
+ }
1381
+ for (const entity of em.unitOfWork.getOrphanRemoveStack()) {
1382
+ fork.unitOfWork.getOrphanRemoveStack().add(entity);
1383
+ }
1384
+ }
1385
+ return fork;
1386
+ }
1387
+ /**
1388
+ * Gets the UnitOfWork used by the EntityManager to coordinate operations.
1389
+ */
1390
+ getUnitOfWork(useContext = true) {
1391
+ if (!useContext) {
1392
+ return this.unitOfWork;
1393
+ }
1394
+ return this.getContext().unitOfWork;
1395
+ }
1396
+ /**
1397
+ * Gets the EntityFactory used by the EntityManager.
1398
+ */
1399
+ getEntityFactory() {
1400
+ return this.getContext().entityFactory;
1401
+ }
1402
+ /**
1403
+ * Gets the Hydrator used by the EntityManager.
1404
+ */
1405
+ getHydrator() {
1406
+ return this.config.getHydrator(this.getMetadata());
1407
+ }
1408
+ /**
1409
+ * Gets the EntityManager based on current transaction/request context.
1410
+ * @internal
1411
+ */
1412
+ getContext(validate = true) {
1413
+ if (!this.useContext) {
1414
+ return this;
1415
+ }
1416
+ let em = utils_1.TransactionContext.getEntityManager(this.name); // prefer the tx context
1417
+ if (em) {
1418
+ return em;
1419
+ }
1420
+ // no explicit tx started
1421
+ em = this.config.get('context')(this.name) ?? this;
1422
+ if (validate && !this.config.get('allowGlobalContext') && em.global) {
1423
+ throw errors_1.ValidationError.cannotUseGlobalContext();
1424
+ }
1425
+ return em;
1426
+ }
1427
+ getEventManager() {
1428
+ return this.eventManager;
1429
+ }
1430
+ /**
1431
+ * Checks whether this EntityManager is currently operating inside a database transaction.
1432
+ */
1433
+ isInTransaction() {
1434
+ return !!this.transactionContext;
1435
+ }
1436
+ /**
1437
+ * Gets the transaction context (driver dependent object used to make sure queries are executed on same connection).
1438
+ */
1439
+ getTransactionContext() {
1440
+ return this.transactionContext;
1441
+ }
1442
+ /**
1443
+ * Sets the transaction context.
1444
+ */
1445
+ setTransactionContext(ctx) {
1446
+ this.transactionContext = ctx;
1447
+ }
1448
+ /**
1449
+ * Resets the transaction context.
1450
+ */
1451
+ resetTransactionContext() {
1452
+ delete this.transactionContext;
1453
+ }
1454
+ /**
1455
+ * Gets the `MetadataStorage` (without parameters) or `EntityMetadata` instance when provided with the `entityName` parameter.
1456
+ */
1457
+ getMetadata(entityName) {
1458
+ if (entityName) {
1459
+ entityName = utils_1.Utils.className(entityName);
1460
+ return this.metadata.get(entityName);
1461
+ }
1462
+ return this.metadata;
1463
+ }
1464
+ /**
1465
+ * Gets the EntityComparator.
1466
+ */
1467
+ getComparator() {
1468
+ return this.comparator;
1469
+ }
1470
+ checkLockRequirements(mode, meta) {
1471
+ if (!mode) {
1472
+ return;
1473
+ }
1474
+ if (mode === enums_1.LockMode.OPTIMISTIC && !meta.versionProperty) {
1475
+ throw errors_1.OptimisticLockError.notVersioned(meta);
1476
+ }
1477
+ if ([enums_1.LockMode.PESSIMISTIC_READ, enums_1.LockMode.PESSIMISTIC_WRITE].includes(mode) && !this.isInTransaction()) {
1478
+ throw errors_1.ValidationError.transactionRequired();
1479
+ }
1480
+ }
1481
+ async lockAndPopulate(meta, entity, where, options) {
1482
+ if (!meta.virtual && options.lockMode === enums_1.LockMode.OPTIMISTIC) {
1483
+ await this.lock(entity, options.lockMode, {
1484
+ lockVersion: options.lockVersion,
1485
+ lockTableAliases: options.lockTableAliases,
1486
+ });
1487
+ }
1488
+ const preparedPopulate = await this.preparePopulate(meta.className, options);
1489
+ await this.entityLoader.populate(meta.className, [entity], preparedPopulate, {
1490
+ ...options,
1491
+ ...this.getPopulateWhere(where, options),
1492
+ convertCustomTypes: false,
1493
+ ignoreLazyScalarProperties: true,
1494
+ lookup: false,
1495
+ });
1496
+ return entity;
1497
+ }
1498
+ buildFields(fields) {
1499
+ return fields.reduce((ret, f) => {
1500
+ if (utils_1.Utils.isPlainObject(f)) {
1501
+ utils_1.Utils.keys(f).forEach(ff => ret.push(...this.buildFields(f[ff]).map(field => `${ff}.${field}`)));
1502
+ }
1503
+ else {
1504
+ ret.push(f);
1505
+ }
1506
+ return ret;
1507
+ }, []);
1508
+ }
1509
+ async preparePopulate(entityName, options, validate = true) {
1510
+ if (options.populate === false) {
1511
+ return [];
1512
+ }
1513
+ const meta = this.metadata.find(entityName);
1514
+ // infer populate hint if only `fields` are available
1515
+ if (!options.populate && options.fields) {
1516
+ // we need to prune the `populate` hint from to-one relations, as partially loading them does not require their population, we want just the FK
1517
+ const pruneToOneRelations = (meta, fields) => {
1518
+ const ret = [];
1519
+ for (const field of fields) {
1520
+ if (field === '*' || field.startsWith('*.')) {
1521
+ ret.push(...meta.props.filter(prop => prop.lazy || [enums_1.ReferenceKind.SCALAR, enums_1.ReferenceKind.EMBEDDED].includes(prop.kind)).map(prop => prop.name));
1522
+ continue;
1523
+ }
1524
+ if (!field.includes('.') && ![enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
1525
+ ret.push(field);
1526
+ continue;
1527
+ }
1528
+ const parts = field.split('.');
1529
+ const key = parts.shift();
1530
+ if (parts.length === 0) {
1531
+ continue;
1532
+ }
1533
+ const prop = meta.properties[key];
1534
+ const inner = pruneToOneRelations(prop.targetMeta, [parts.join('.')]);
1535
+ if (inner.length > 0) {
1536
+ ret.push(...inner.map(c => `${key}.${c}`));
1537
+ }
1538
+ }
1539
+ return utils_1.Utils.unique(ret);
1540
+ };
1541
+ options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
1542
+ }
1543
+ if (!options.populate) {
1544
+ const populate = this.entityLoader.normalizePopulate(entityName, [], options.strategy);
1545
+ await this.autoJoinRefsForFilters(meta, { ...options, populate });
1546
+ return populate;
1547
+ }
1548
+ if (typeof options.populate !== 'boolean') {
1549
+ options.populate = utils_1.Utils.asArray(options.populate).map(field => {
1550
+ /* istanbul ignore next */
1551
+ if (typeof field === 'boolean' || field === '*') {
1552
+ return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
1553
+ }
1554
+ // will be handled in QueryBuilder when processing the where condition via CriteriaNode
1555
+ if (field === '$infer') {
1556
+ options.flags ??= [];
1557
+ options.flags.push(enums_1.QueryFlag.INFER_POPULATE);
1558
+ return [];
1559
+ }
1560
+ if (utils_1.Utils.isString(field)) {
1561
+ return [{ field, strategy: options.strategy }];
1562
+ }
1563
+ return [field];
1564
+ }).flat();
1565
+ }
1566
+ const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
1567
+ let invalid;
1568
+ if (meta) {
1569
+ // analyse all the extending entities because it is possible to populate a field only available in one of the STI children
1570
+ const extending = this.getSTIExtendingMetadata(meta);
1571
+ invalid = populate.find(({ field }) => extending.every(meta => !this.canPopulate(meta.className, field)));
1572
+ }
1573
+ else {
1574
+ invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
1575
+ }
1576
+ if (validate && invalid) {
1577
+ throw errors_1.ValidationError.invalidPropertyName(entityName, invalid.field);
1578
+ }
1579
+ await this.autoJoinRefsForFilters(meta, { ...options, populate });
1580
+ return populate.map(field => {
1581
+ // force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
1582
+ const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
1583
+ field.strategy = all ? enums_1.LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
1584
+ return field;
1585
+ });
1586
+ }
1587
+ /**
1588
+ * returns all the EntityMetadata that extends the passed EntityMetadata in the context of single table inheritance
1589
+ */
1590
+ getSTIExtendingMetadata(meta, metadata) {
1591
+ if (!metadata) {
1592
+ const map = meta.root.discriminatorMap;
1593
+ if (!map) {
1594
+ return [meta];
1595
+ }
1596
+ const metadata = Object.values(map).map(entityName => this.metadata.get(entityName));
1597
+ return this.getSTIExtendingMetadata(meta, metadata);
1598
+ }
1599
+ const extending = metadata.filter(_meta => _meta.extends === meta.className);
1600
+ const childrenExtending = extending.map(meta => this.getSTIExtendingMetadata(meta, metadata));
1601
+ return [meta, ...childrenExtending.reduce((prev, curr) => [...prev, ...curr], [])];
1602
+ }
1603
+ /**
1604
+ * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
1605
+ * some additional lazy properties, if so, we reload and merge the data from database
1606
+ */
1607
+ shouldRefresh(meta, entity, options) {
1608
+ if (!(0, entity_1.helper)(entity).__initialized || options.refresh) {
1609
+ return true;
1610
+ }
1611
+ let autoRefresh;
1612
+ if (options.fields) {
1613
+ autoRefresh = options.fields.some(field => !(0, entity_1.helper)(entity).__loadedProperties.has(field));
1614
+ }
1615
+ else {
1616
+ autoRefresh = meta.comparableProps.some(prop => {
1617
+ const inlineEmbedded = prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object;
1618
+ return !inlineEmbedded && !prop.lazy && !(0, entity_1.helper)(entity).__loadedProperties.has(prop.name);
1619
+ });
1620
+ }
1621
+ if (autoRefresh) {
1622
+ return true;
1623
+ }
1624
+ if (Array.isArray(options.populate)) {
1625
+ return options.populate.some(field => !(0, entity_1.helper)(entity).__loadedProperties.has(field));
1626
+ }
1627
+ return !!options.populate;
1628
+ }
1629
+ prepareOptions(options) {
1630
+ if (!utils_1.Utils.isEmpty(options.fields) && !utils_1.Utils.isEmpty(options.exclude)) {
1631
+ throw new errors_1.ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
1632
+ }
1633
+ options.schema ??= this._schema;
1634
+ options.logging = utils_1.Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
1635
+ }
1636
+ /**
1637
+ * @internal
1638
+ */
1639
+ cacheKey(entityName, options, method, where) {
1640
+ const { ...opts } = options;
1641
+ // ignore some irrelevant options, e.g. logger context can contain dynamic data for the same query
1642
+ for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
1643
+ delete opts[k];
1644
+ }
1645
+ return [entityName, method, opts, where];
1646
+ }
1647
+ /**
1648
+ * @internal
1649
+ */
1650
+ async tryCache(entityName, config, key, refresh, merge) {
1651
+ config ??= this.config.get('resultCache').global;
1652
+ if (!config) {
1653
+ return undefined;
1654
+ }
1655
+ const em = this.getContext();
1656
+ const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
1657
+ const cached = await em.resultCache.get(cacheKey);
1658
+ if (cached) {
1659
+ let data;
1660
+ if (Array.isArray(cached) && merge) {
1661
+ data = cached.map(item => em.entityFactory.create(entityName, item, {
1662
+ merge: true,
1663
+ convertCustomTypes: true,
1664
+ refresh,
1665
+ recomputeSnapshot: true,
1666
+ }));
1667
+ }
1668
+ else if (utils_1.Utils.isObject(cached) && merge) {
1669
+ data = em.entityFactory.create(entityName, cached, {
1670
+ merge: true,
1671
+ convertCustomTypes: true,
1672
+ refresh,
1673
+ recomputeSnapshot: true,
1674
+ });
1675
+ }
1676
+ else {
1677
+ data = cached;
1678
+ }
1679
+ await em.unitOfWork.dispatchOnLoadEvent();
1680
+ return { key: cacheKey, data };
1681
+ }
1682
+ return { key: cacheKey };
1683
+ }
1684
+ /**
1685
+ * @internal
1686
+ */
1687
+ async storeCache(config, key, data) {
1688
+ config ??= this.config.get('resultCache').global;
1689
+ if (config) {
1690
+ const em = this.getContext();
1691
+ const expiration = Array.isArray(config) ? config[1] : (utils_1.Utils.isNumber(config) ? config : undefined);
1692
+ await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
1693
+ }
1694
+ }
1695
+ /**
1696
+ * Clears result cache for given cache key. If we want to be able to call this method,
1697
+ * we need to set the cache key explicitly when storing the cache.
1698
+ *
1699
+ * ```ts
1700
+ * // set the cache key to 'book-cache-key', with expiration of 60s
1701
+ * const res = await em.find(Book, { ... }, { cache: ['book-cache-key', 60_000] });
1702
+ *
1703
+ * // clear the cache key by name
1704
+ * await em.clearCache('book-cache-key');
1705
+ * ```
1706
+ */
1707
+ async clearCache(cacheKey) {
1708
+ await this.getContext().resultCache.remove(cacheKey);
1709
+ }
1710
+ /**
1711
+ * Returns the default schema of this EntityManager. Respects the context, so global EM will give you the contextual schema
1712
+ * if executed inside request context handler.
1713
+ */
1714
+ get schema() {
1715
+ return this.getContext(false)._schema;
1716
+ }
1717
+ /**
1718
+ * Sets the default schema of this EntityManager. Respects the context, so global EM will set the contextual schema
1719
+ * if executed inside request context handler.
1720
+ */
1721
+ set schema(schema) {
1722
+ this.getContext(false)._schema = schema ?? undefined;
1723
+ }
1724
+ /**
1725
+ * Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
1726
+ * if executed inside request context handler.
1727
+ */
1728
+ get id() {
1729
+ return this.getContext(false)._id;
1730
+ }
1731
+ /** @ignore */
1732
+ [util_1.inspect.custom]() {
1733
+ return `[EntityManager<${this.id}>]`;
1734
+ }
1735
+ }
1736
+ exports.EntityManager = EntityManager;