@mikro-orm/core 7.0.17 → 7.0.18-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +106 -12
- package/EntityManager.js +228 -19
- package/MikroORM.d.ts +3 -3
- package/README.md +2 -1
- package/connections/Connection.d.ts +67 -1
- package/connections/Connection.js +64 -0
- package/drivers/DatabaseDriver.d.ts +1 -0
- package/drivers/DatabaseDriver.js +3 -0
- package/drivers/IDatabaseDriver.d.ts +50 -7
- package/entity/BaseEntity.d.ts +70 -3
- package/entity/BaseEntity.js +18 -0
- package/entity/Collection.d.ts +6 -3
- package/entity/Collection.js +15 -4
- package/entity/EntityAssigner.js +8 -0
- package/entity/EntityFactory.js +20 -1
- package/entity/EntityLoader.d.ts +8 -1
- package/entity/EntityLoader.js +41 -16
- package/entity/EntityRepository.d.ts +27 -9
- package/entity/EntityRepository.js +12 -0
- package/entity/Reference.d.ts +42 -1
- package/entity/Reference.js +9 -0
- package/entity/WrappedEntity.d.ts +2 -2
- package/entity/defineEntity.d.ts +92 -17
- package/entity/defineEntity.js +17 -6
- package/entity/utils.js +4 -5
- package/enums.d.ts +8 -1
- package/errors.d.ts +2 -0
- package/errors.js +4 -0
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/metadata/EntitySchema.js +3 -0
- package/metadata/MetadataDiscovery.d.ts +11 -0
- package/metadata/MetadataDiscovery.js +135 -10
- package/metadata/MetadataValidator.d.ts +26 -0
- package/metadata/MetadataValidator.js +236 -0
- package/metadata/Routine.d.ts +74 -0
- package/metadata/Routine.js +140 -0
- package/metadata/index.d.ts +1 -0
- package/metadata/index.js +1 -0
- package/metadata/types.d.ts +77 -4
- package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
- package/naming-strategy/NamingStrategy.d.ts +1 -1
- package/package.json +1 -1
- package/platforms/Platform.d.ts +7 -0
- package/platforms/Platform.js +15 -1
- package/serialization/EntitySerializer.d.ts +18 -6
- package/serialization/EntitySerializer.js +31 -4
- package/typings.d.ts +389 -18
- package/typings.js +21 -4
- package/unit-of-work/UnitOfWork.js +11 -3
- package/utils/AbstractMigrator.d.ts +26 -3
- package/utils/AbstractMigrator.js +264 -15
- package/utils/AbstractSchemaGenerator.d.ts +1 -1
- package/utils/AbstractSchemaGenerator.js +4 -1
- package/utils/Configuration.d.ts +40 -3
- package/utils/Configuration.js +46 -0
- package/utils/DataloaderUtils.d.ts +10 -1
- package/utils/DataloaderUtils.js +78 -0
- package/utils/QueryHelper.d.ts +16 -0
- package/utils/QueryHelper.js +15 -0
- package/utils/TransactionManager.js +2 -0
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +1 -1
- package/utils/fs-utils.d.ts +2 -0
- package/utils/fs-utils.js +7 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/partition-utils.d.ts +17 -0
- package/utils/partition-utils.js +79 -0
package/EntityManager.d.ts
CHANGED
|
@@ -6,11 +6,12 @@ import { type EntityRepository } from './entity/EntityRepository.js';
|
|
|
6
6
|
import { EntityLoader, type EntityLoaderOptions } from './entity/EntityLoader.js';
|
|
7
7
|
import { Reference } from './entity/Reference.js';
|
|
8
8
|
import { UnitOfWork } from './unit-of-work/UnitOfWork.js';
|
|
9
|
-
import type { CountOptions, DeleteOptions, FilterOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers/IDatabaseDriver.js';
|
|
10
|
-
import type { AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityClass, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MergeLoaded, MergeSelected, ObjectQuery, PopulateOptions, Primary, Ref, RequiredEntityData, UnboxArray } from './typings.js';
|
|
9
|
+
import type { CountByOptions, CountOptions, DeleteOptions, FilterOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, StreamOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers/IDatabaseDriver.js';
|
|
10
|
+
import type { AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityClass, EntityData, EntityDictionary, EntityDTO, EntityKey, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MergeLoaded, MergeSelected, ObjectQuery, PopulateOptions, Primary, Ref, RequiredEntityData, RoutineArgs, RoutineReturn, UnboxArray, IndexFilterQuery, WithUsingOptions } from './typings.js';
|
|
11
|
+
import type { Routine } from './metadata/Routine.js';
|
|
11
12
|
import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums.js';
|
|
12
13
|
import type { MetadataStorage } from './metadata/MetadataStorage.js';
|
|
13
|
-
import type { Transaction } from './connections/Connection.js';
|
|
14
|
+
import type { AbortQueryOptions, InflightQueryAbortStrategy, Transaction } from './connections/Connection.js';
|
|
14
15
|
import { EventManager } from './events/EventManager.js';
|
|
15
16
|
import type { EntityComparator } from './utils/EntityComparator.js';
|
|
16
17
|
/**
|
|
@@ -33,6 +34,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
33
34
|
/** The context name of this EntityManager, derived from the ORM configuration. */
|
|
34
35
|
readonly name: string;
|
|
35
36
|
protected loggerContext?: Dictionary;
|
|
37
|
+
/** @internal */
|
|
38
|
+
protected signal?: AbortSignal;
|
|
39
|
+
/** @internal */
|
|
40
|
+
protected inflightQueryAbortStrategy?: InflightQueryAbortStrategy;
|
|
36
41
|
/**
|
|
37
42
|
* @internal
|
|
38
43
|
*/
|
|
@@ -61,7 +66,9 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
61
66
|
/**
|
|
62
67
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
63
68
|
*/
|
|
64
|
-
find<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity
|
|
69
|
+
find<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(entityName: EntityName<Entity>, where: [Using] extends [never] ? FilterQuery<NoInfer<Entity>> : IndexFilterQuery<NoInfer<Entity>, Using>, options?: FindOptions<Entity, Hint, Fields, Excludes> & {
|
|
70
|
+
using?: Using | Using[];
|
|
71
|
+
}): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
|
|
65
72
|
/**
|
|
66
73
|
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
67
74
|
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
@@ -79,11 +86,11 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
79
86
|
* }
|
|
80
87
|
* ```
|
|
81
88
|
*/
|
|
82
|
-
stream<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entityName: EntityName<Entity>, options?: StreamOptions<NoInfer<Entity>, Hint, Fields, Excludes>): AsyncIterableIterator<Loaded<Entity, Hint, Fields, Excludes>>;
|
|
89
|
+
stream<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(entityName: EntityName<Entity>, options?: WithUsingOptions<StreamOptions<NoInfer<Entity>, Hint, Fields, Excludes>, NoInfer<Entity>, Using>): AsyncIterableIterator<Loaded<Entity, Hint, Fields, Excludes>>;
|
|
83
90
|
/**
|
|
84
91
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
85
92
|
*/
|
|
86
|
-
findAll<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entityName: EntityName<Entity>, options?: FindAllOptions<NoInfer<Entity>, Hint, Fields, Excludes>): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
|
|
93
|
+
findAll<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(entityName: EntityName<Entity>, options?: WithUsingOptions<FindAllOptions<NoInfer<Entity>, Hint, Fields, Excludes>, NoInfer<Entity>, Using>): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
|
|
87
94
|
private getPopulateWhere;
|
|
88
95
|
/**
|
|
89
96
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
@@ -130,7 +137,9 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
130
137
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as tuple
|
|
131
138
|
* where the first element is the array of entities, and the second is the count.
|
|
132
139
|
*/
|
|
133
|
-
findAndCount<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity
|
|
140
|
+
findAndCount<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(entityName: EntityName<Entity>, where: [Using] extends [never] ? FilterQuery<NoInfer<Entity>> : IndexFilterQuery<NoInfer<Entity>, Using>, options?: FindOptions<Entity, Hint, Fields, Excludes> & {
|
|
141
|
+
using?: Using | Using[];
|
|
142
|
+
}): Promise<[Loaded<Entity, Hint, Fields, Excludes>[], number]>;
|
|
134
143
|
/**
|
|
135
144
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
|
|
136
145
|
* Supports `before`, `after`, `first` and `last` options while disallowing `limit` and `offset`. Explicit `orderBy` option
|
|
@@ -187,7 +196,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
187
196
|
* }
|
|
188
197
|
* ```
|
|
189
198
|
*/
|
|
190
|
-
findByCursor<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, IncludeCount extends boolean = true>(entityName: EntityName<Entity>, options: FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>): Promise<Cursor<Entity, Hint, Fields, Excludes, IncludeCount>>;
|
|
199
|
+
findByCursor<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, IncludeCount extends boolean = true, Using extends string = never>(entityName: EntityName<Entity>, options: WithUsingOptions<FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>, Entity, Using>): Promise<Cursor<Entity, Hint, Fields, Excludes, IncludeCount>>;
|
|
191
200
|
/**
|
|
192
201
|
* Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
|
|
193
202
|
* persisted. Returns the same entity instance (same object reference), but re-hydrated. If the entity is no longer
|
|
@@ -203,14 +212,18 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
203
212
|
/**
|
|
204
213
|
* Finds first entity matching your `where` query.
|
|
205
214
|
*/
|
|
206
|
-
findOne<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity
|
|
215
|
+
findOne<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(entityName: EntityName<Entity>, where: [Using] extends [never] ? FilterQuery<NoInfer<Entity>> : IndexFilterQuery<NoInfer<Entity>, Using>, options?: FindOneOptions<Entity, Hint, Fields, Excludes> & {
|
|
216
|
+
using?: Using | Using[];
|
|
217
|
+
}): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
|
|
207
218
|
/**
|
|
208
219
|
* Finds first entity matching your `where` query. If nothing found, it will throw an error.
|
|
209
220
|
* If the `strict` option is specified and nothing is found or more than one matching entity is found, it will throw an error.
|
|
210
221
|
* You can override the factory for creating this method via `options.failHandler` locally
|
|
211
222
|
* or via `Configuration.findOneOrFailHandler` (`findExactlyOneOrFailHandler` when specifying `strict`) globally.
|
|
212
223
|
*/
|
|
213
|
-
findOneOrFail<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity
|
|
224
|
+
findOneOrFail<Entity extends object, Hint extends string = never, Fields extends string = never, Excludes extends string = never, Using extends string = never>(entityName: EntityName<Entity>, where: [Using] extends [never] ? FilterQuery<NoInfer<Entity>> : IndexFilterQuery<NoInfer<Entity>, Using>, options?: FindOneOrFailOptions<Entity, Hint, Fields, Excludes> & {
|
|
225
|
+
using?: Using | Using[];
|
|
226
|
+
}): Promise<Loaded<Entity, Hint, Fields, Excludes>>;
|
|
214
227
|
/**
|
|
215
228
|
* Creates or updates the entity, based on whether it is already present in the database.
|
|
216
229
|
* This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed
|
|
@@ -307,6 +320,25 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
307
320
|
* Fires native insert query. Calling this has no side effects on the context (identity map).
|
|
308
321
|
*/
|
|
309
322
|
insert<Entity extends object>(entityNameOrEntity: EntityName<Entity> | Entity, data?: RequiredEntityData<Entity> | Entity, options?: NativeInsertUpdateOptions<Entity>): Promise<Primary<Entity>>;
|
|
323
|
+
/**
|
|
324
|
+
* Clones rows matching the condition at the database level via INSERT...SELECT.
|
|
325
|
+
* Automatically excludes auto-increment PKs, generated columns, and resets version properties.
|
|
326
|
+
* Returns the cloned entity, registered in the identity map.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* // Clone by entity class + where + overrides
|
|
331
|
+
* const cloned = await em.clone(Author, { id: 1 }, { email: 'new@email.com' });
|
|
332
|
+
*
|
|
333
|
+
* // Pure clone (all non-PK fields copied)
|
|
334
|
+
* const cloned = await em.clone(Author, { id: 1 });
|
|
335
|
+
*
|
|
336
|
+
* // Clone a loaded entity
|
|
337
|
+
* const author = await em.findOneOrFail(Author, 1);
|
|
338
|
+
* const cloned = await em.clone(author, { email: 'new@email.com' });
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
clone<Entity extends object>(entityNameOrEntity: EntityName<Entity> | Entity, whereOrOverrides?: FilterQuery<NoInfer<Entity>> | EntityData<Entity>, overridesOrOptions?: EntityData<Entity> | NativeInsertUpdateOptions<Entity>, options?: NativeInsertUpdateOptions<Entity>): Promise<Entity>;
|
|
310
342
|
/**
|
|
311
343
|
* Fires native multi-insert query. Calling this has no side effects on the context (identity map).
|
|
312
344
|
*/
|
|
@@ -315,6 +347,29 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
315
347
|
* Fires native update query. Calling this has no side effects on the context (identity map).
|
|
316
348
|
*/
|
|
317
349
|
nativeUpdate<Entity extends object>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity>>, data: EntityData<Entity>, options?: UpdateOptions<Entity>): Promise<number>;
|
|
350
|
+
/**
|
|
351
|
+
* Invokes a stored procedure or function declared via the {@link Routine} class. Arg and return
|
|
352
|
+
* types are inferred from the literal config; use {@link Routine.create} to refine when needed.
|
|
353
|
+
*
|
|
354
|
+
* Procedures emitting result sets (MySQL `SELECT`s, PG/Oracle refcursor OUT params) return
|
|
355
|
+
* `Dictionary[][]`; MSSQL multi-result procs aren't exposed here, use `em.getConnection().execute()`.
|
|
356
|
+
*
|
|
357
|
+
* Mongo throws. SQLite bridges `bodyJs` functions via better-sqlite3 UDFs (procedures and
|
|
358
|
+
* function-without-bodyJs throw); libSQL throws unconditionally. Oracle calls run on their own
|
|
359
|
+
* pool connection with `autoCommit: true`, so wrapping in `em.transactional(...)` throws.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* const hash = await em.callRoutine(HashUser, { name: 'jon', salt: 'pepper' });
|
|
364
|
+
*
|
|
365
|
+
* const hashRef = new ScalarReference<string>();
|
|
366
|
+
* await em.callRoutine(AddRecord, { p_name: 'jon', p_age: 30, p_hash: hashRef });
|
|
367
|
+
*
|
|
368
|
+
* const TwoCursors = Routine.create<Record<string, never>, unknown[][]>({ ... });
|
|
369
|
+
* const [users, books] = await em.transactional(em => em.callRoutine(TwoCursors, {}));
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
callRoutine<R extends Routine>(routine: R, args: RoutineArgs<R>): Promise<RoutineReturn<R>>;
|
|
318
373
|
/**
|
|
319
374
|
* Fires native delete query. Calling this has no side effects on the context (identity map).
|
|
320
375
|
*/
|
|
@@ -414,6 +469,27 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
414
469
|
* Returns total number of entities matching your `where` query.
|
|
415
470
|
*/
|
|
416
471
|
count<Entity extends object, Hint extends string = never>(entityName: EntityName<Entity>, where?: FilterQuery<NoInfer<Entity>>, options?: CountOptions<Entity, Hint>): Promise<number>;
|
|
472
|
+
/**
|
|
473
|
+
* Counts entities grouped by one or more properties. Returns a dictionary keyed by the grouped
|
|
474
|
+
* field value(s), with counts as values. For composite `groupBy`, keys are joined with `~~~`.
|
|
475
|
+
*
|
|
476
|
+
* SQL drivers issue a single `GROUP BY` query; MongoDB uses an aggregation pipeline.
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* ```ts
|
|
480
|
+
* // Count books per author
|
|
481
|
+
* const counts = await em.countBy(Book, 'author');
|
|
482
|
+
* // { '1': 2, '2': 1, '3': 3 }
|
|
483
|
+
*
|
|
484
|
+
* // Count with a filter
|
|
485
|
+
* const counts = await em.countBy(Book, 'author', { where: { active: true } });
|
|
486
|
+
*
|
|
487
|
+
* // Composite groupBy — keys joined with ~~~
|
|
488
|
+
* const counts = await em.countBy(Order, ['status', 'country']);
|
|
489
|
+
* // { 'pending~~~US': 5, 'shipped~~~DE': 3 }
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
countBy<Entity extends object>(entityName: EntityName<Entity>, groupBy: EntityKey<Entity> | readonly EntityKey<Entity>[], options?: CountByOptions<Entity>): Promise<Dictionary<number>>;
|
|
417
493
|
/**
|
|
418
494
|
* Tells the EntityManager to make an instance managed and persistent.
|
|
419
495
|
* The entity will be entered into the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -484,6 +560,13 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
484
560
|
* Gets the transaction context (driver dependent object used to make sure queries are executed on same connection).
|
|
485
561
|
*/
|
|
486
562
|
getTransactionContext<T extends Transaction = Transaction>(): T | undefined;
|
|
563
|
+
/**
|
|
564
|
+
* Returns the cancellation defaults configured on this EntityManager (via `em.fork({ signal })`
|
|
565
|
+
* or inherited from a transactional fork). Returns `undefined` when no signal is set.
|
|
566
|
+
*
|
|
567
|
+
* @internal — exposed for subclass drivers and `UnitOfWork`; not part of the public API.
|
|
568
|
+
*/
|
|
569
|
+
protected getAbortOptions(): AbortQueryOptions | undefined;
|
|
487
570
|
/**
|
|
488
571
|
* Sets the transaction context.
|
|
489
572
|
*/
|
|
@@ -505,16 +588,20 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
505
588
|
*/
|
|
506
589
|
getComparator(): EntityComparator;
|
|
507
590
|
private checkLockRequirements;
|
|
591
|
+
private validateIndexUsage;
|
|
592
|
+
private validateWhereKeysForIndex;
|
|
508
593
|
private lockAndPopulate;
|
|
509
594
|
private buildFields;
|
|
510
595
|
/** @internal */
|
|
511
596
|
preparePopulate<Entity extends object>(entityName: EntityName<Entity>, options: Pick<FindOptions<Entity, any, any, any>, 'populate' | 'strategy' | 'fields' | 'flags' | 'filters' | 'exclude' | 'populateHints'>, validate?: boolean): Promise<PopulateOptions<Entity>[]>;
|
|
597
|
+
/** Force SELECT_IN strategy on populate entries with `limit`, since JOINED cannot do per-parent limiting. */
|
|
598
|
+
private forceSelectInForLimitedPopulate;
|
|
512
599
|
/**
|
|
513
600
|
* when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
|
|
514
601
|
* some additional lazy properties, if so, we reload and merge the data from database
|
|
515
602
|
*/
|
|
516
603
|
protected shouldRefresh<T extends object, P extends string = never, F extends string = never, E extends string = never>(meta: EntityMetadata<T>, entity: T, options: FindOneOptions<T, P, F, E>): boolean;
|
|
517
|
-
protected prepareOptions(options: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any> | CountOptions<any, any>): void;
|
|
604
|
+
protected prepareOptions(options: (FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any> | CountOptions<any, any> | CountByOptions<any>) & AbortQueryOptions): void;
|
|
518
605
|
/**
|
|
519
606
|
* @internal
|
|
520
607
|
*/
|
|
@@ -556,7 +643,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
556
643
|
*/
|
|
557
644
|
set schema(schema: string | null | undefined);
|
|
558
645
|
/** @internal */
|
|
559
|
-
getDataLoader(type: 'ref' | '1:m' | 'm:n'): Promise<any>;
|
|
646
|
+
getDataLoader(type: 'ref' | '1:m' | 'm:n' | 'count'): Promise<any>;
|
|
560
647
|
/**
|
|
561
648
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
562
649
|
* if executed inside request context handler.
|
|
@@ -609,4 +696,11 @@ export interface ForkOptions {
|
|
|
609
696
|
schema?: string;
|
|
610
697
|
/** default logger context, can be overridden via {@apilink FindOptions} */
|
|
611
698
|
loggerContext?: Dictionary;
|
|
699
|
+
/**
|
|
700
|
+
* Default `AbortSignal` applied to every operation on this fork (queries and UoW flushes).
|
|
701
|
+
* Per-call options.signal still takes precedence.
|
|
702
|
+
*/
|
|
703
|
+
signal?: AbortSignal;
|
|
704
|
+
/** Default cancellation strategy paired with {@link signal}. */
|
|
705
|
+
inflightQueryAbortStrategy?: InflightQueryAbortStrategy;
|
|
612
706
|
}
|
package/EntityManager.js
CHANGED
|
@@ -49,6 +49,10 @@ export class EntityManager {
|
|
|
49
49
|
#disableTransactions;
|
|
50
50
|
#flushMode;
|
|
51
51
|
#schema;
|
|
52
|
+
/** @internal */
|
|
53
|
+
signal;
|
|
54
|
+
/** @internal */
|
|
55
|
+
inflightQueryAbortStrategy;
|
|
52
56
|
#useContext;
|
|
53
57
|
/**
|
|
54
58
|
* @internal
|
|
@@ -103,9 +107,6 @@ export class EntityManager {
|
|
|
103
107
|
repo(entityName) {
|
|
104
108
|
return this.getRepository(entityName);
|
|
105
109
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
108
|
-
*/
|
|
109
110
|
async find(entityName, where, options = {}) {
|
|
110
111
|
if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) {
|
|
111
112
|
const em = this.getContext(false);
|
|
@@ -116,10 +117,11 @@ export class EntityManager {
|
|
|
116
117
|
}
|
|
117
118
|
const em = this.getContext();
|
|
118
119
|
em.prepareOptions(options);
|
|
120
|
+
const meta = this.metadata.get(entityName);
|
|
121
|
+
em.validateIndexUsage(meta, where, options);
|
|
119
122
|
await em.tryFlush(entityName, options);
|
|
120
123
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
121
124
|
validateParams(where);
|
|
122
|
-
const meta = this.metadata.get(entityName);
|
|
123
125
|
if (meta.orderBy) {
|
|
124
126
|
options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
|
|
125
127
|
}
|
|
@@ -203,6 +205,7 @@ export class EntityManager {
|
|
|
203
205
|
options.orderBy = options.orderBy || {};
|
|
204
206
|
options.populate = (await em.preparePopulate(entityName, options));
|
|
205
207
|
const meta = this.metadata.get(entityName);
|
|
208
|
+
em.validateIndexUsage(meta, options.where ?? {}, options);
|
|
206
209
|
options = { ...options };
|
|
207
210
|
// save the original hint value so we know it was infer/all
|
|
208
211
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
@@ -520,10 +523,6 @@ export class EntityManager {
|
|
|
520
523
|
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
|
|
521
524
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
522
525
|
}
|
|
523
|
-
/**
|
|
524
|
-
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as tuple
|
|
525
|
-
* where the first element is the array of entities, and the second is the count.
|
|
526
|
-
*/
|
|
527
526
|
async findAndCount(entityName, where, options = {}) {
|
|
528
527
|
const em = this.getContext(false);
|
|
529
528
|
await em.tryFlush(entityName, options);
|
|
@@ -657,9 +656,6 @@ export class EntityManager {
|
|
|
657
656
|
}
|
|
658
657
|
return entity;
|
|
659
658
|
}
|
|
660
|
-
/**
|
|
661
|
-
* Finds first entity matching your `where` query.
|
|
662
|
-
*/
|
|
663
659
|
async findOne(entityName, where, options = {}) {
|
|
664
660
|
if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) {
|
|
665
661
|
const em = this.getContext(false);
|
|
@@ -679,6 +675,7 @@ export class EntityManager {
|
|
|
679
675
|
}
|
|
680
676
|
await em.tryFlush(entityName, options);
|
|
681
677
|
const meta = em.metadata.get(entityName);
|
|
678
|
+
em.validateIndexUsage(meta, where, options);
|
|
682
679
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
683
680
|
validateEmptyWhere(where);
|
|
684
681
|
em.checkLockRequirements(options.lockMode, meta);
|
|
@@ -727,12 +724,6 @@ export class EntityManager {
|
|
|
727
724
|
await em.storeCache(options.cache, cached, () => helper(entity).toPOJO());
|
|
728
725
|
return entity;
|
|
729
726
|
}
|
|
730
|
-
/**
|
|
731
|
-
* Finds first entity matching your `where` query. If nothing found, it will throw an error.
|
|
732
|
-
* If the `strict` option is specified and nothing is found or more than one matching entity is found, it will throw an error.
|
|
733
|
-
* You can override the factory for creating this method via `options.failHandler` locally
|
|
734
|
-
* or via `Configuration.findOneOrFailHandler` (`findExactlyOneOrFailHandler` when specifying `strict`) globally.
|
|
735
|
-
*/
|
|
736
727
|
async findOneOrFail(entityName, where, options = {}) {
|
|
737
728
|
let entity;
|
|
738
729
|
let isStrictViolation = false;
|
|
@@ -1207,6 +1198,7 @@ export class EntityManager {
|
|
|
1207
1198
|
*/
|
|
1208
1199
|
async lock(entity, lockMode, options = {}) {
|
|
1209
1200
|
options = Utils.isPlainObject(options) ? options : { lockVersion: options };
|
|
1201
|
+
this.getContext(false).prepareOptions(options);
|
|
1210
1202
|
await this.getUnitOfWork().lock(entity, { lockMode, ...options });
|
|
1211
1203
|
}
|
|
1212
1204
|
/**
|
|
@@ -1244,6 +1236,54 @@ export class EntityManager {
|
|
|
1244
1236
|
});
|
|
1245
1237
|
return res.insertId;
|
|
1246
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Clones rows matching the condition at the database level via INSERT...SELECT.
|
|
1241
|
+
* Automatically excludes auto-increment PKs, generated columns, and resets version properties.
|
|
1242
|
+
* Returns the cloned entity, registered in the identity map.
|
|
1243
|
+
*
|
|
1244
|
+
* @example
|
|
1245
|
+
* ```ts
|
|
1246
|
+
* // Clone by entity class + where + overrides
|
|
1247
|
+
* const cloned = await em.clone(Author, { id: 1 }, { email: 'new@email.com' });
|
|
1248
|
+
*
|
|
1249
|
+
* // Pure clone (all non-PK fields copied)
|
|
1250
|
+
* const cloned = await em.clone(Author, { id: 1 });
|
|
1251
|
+
*
|
|
1252
|
+
* // Clone a loaded entity
|
|
1253
|
+
* const author = await em.findOneOrFail(Author, 1);
|
|
1254
|
+
* const cloned = await em.clone(author, { email: 'new@email.com' });
|
|
1255
|
+
* ```
|
|
1256
|
+
*/
|
|
1257
|
+
async clone(entityNameOrEntity, whereOrOverrides, overridesOrOptions, options) {
|
|
1258
|
+
const em = this.getContext(false);
|
|
1259
|
+
let entityName;
|
|
1260
|
+
let where;
|
|
1261
|
+
let overrides;
|
|
1262
|
+
if (Utils.isEntity(entityNameOrEntity)) {
|
|
1263
|
+
// clone(entity, overrides?, options?)
|
|
1264
|
+
entityName = entityNameOrEntity.constructor;
|
|
1265
|
+
where = helper(entityNameOrEntity).getPrimaryKey();
|
|
1266
|
+
overrides = whereOrOverrides;
|
|
1267
|
+
options = overridesOrOptions;
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
// clone(EntityClass, where, overrides?, options?)
|
|
1271
|
+
entityName = entityNameOrEntity;
|
|
1272
|
+
where = whereOrOverrides;
|
|
1273
|
+
overrides = overridesOrOptions;
|
|
1274
|
+
}
|
|
1275
|
+
options ??= {};
|
|
1276
|
+
em.prepareOptions(options);
|
|
1277
|
+
const meta = em.metadata.get(entityName);
|
|
1278
|
+
const res = await em.driver.nativeClone(entityName, where, overrides, {
|
|
1279
|
+
ctx: em.#transactionContext,
|
|
1280
|
+
...options,
|
|
1281
|
+
});
|
|
1282
|
+
const pk = res.insertId ?? res.row?.[meta.primaryKeys[0]];
|
|
1283
|
+
return em.findOneOrFail(entityName, pk, {
|
|
1284
|
+
schema: options.schema,
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1247
1287
|
/**
|
|
1248
1288
|
* Fires native multi-insert query. Calling this has no side effects on the context (identity map).
|
|
1249
1289
|
*/
|
|
@@ -1305,6 +1345,37 @@ export class EntityManager {
|
|
|
1305
1345
|
});
|
|
1306
1346
|
return res.affectedRows;
|
|
1307
1347
|
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Invokes a stored procedure or function declared via the {@link Routine} class. Arg and return
|
|
1350
|
+
* types are inferred from the literal config; use {@link Routine.create} to refine when needed.
|
|
1351
|
+
*
|
|
1352
|
+
* Procedures emitting result sets (MySQL `SELECT`s, PG/Oracle refcursor OUT params) return
|
|
1353
|
+
* `Dictionary[][]`; MSSQL multi-result procs aren't exposed here, use `em.getConnection().execute()`.
|
|
1354
|
+
*
|
|
1355
|
+
* Mongo throws. SQLite bridges `bodyJs` functions via better-sqlite3 UDFs (procedures and
|
|
1356
|
+
* function-without-bodyJs throw); libSQL throws unconditionally. Oracle calls run on their own
|
|
1357
|
+
* pool connection with `autoCommit: true`, so wrapping in `em.transactional(...)` throws.
|
|
1358
|
+
*
|
|
1359
|
+
* @example
|
|
1360
|
+
* ```ts
|
|
1361
|
+
* const hash = await em.callRoutine(HashUser, { name: 'jon', salt: 'pepper' });
|
|
1362
|
+
*
|
|
1363
|
+
* const hashRef = new ScalarReference<string>();
|
|
1364
|
+
* await em.callRoutine(AddRecord, { p_name: 'jon', p_age: 30, p_hash: hashRef });
|
|
1365
|
+
*
|
|
1366
|
+
* const TwoCursors = Routine.create<Record<string, never>, unknown[][]>({ ... });
|
|
1367
|
+
* const [users, books] = await em.transactional(em => em.callRoutine(TwoCursors, {}));
|
|
1368
|
+
* ```
|
|
1369
|
+
*/
|
|
1370
|
+
async callRoutine(routine, args) {
|
|
1371
|
+
const em = this.getContext(false);
|
|
1372
|
+
// Identity by reference so the user-held import survives bundling and tree-shakes unused routines.
|
|
1373
|
+
if (!em.config.hasRoutine(routine)) {
|
|
1374
|
+
throw new Error(`Routine '${routine.name}' is not registered in the 'routines' config option.`);
|
|
1375
|
+
}
|
|
1376
|
+
const conn = em.driver.getConnection('write');
|
|
1377
|
+
return conn.callRoutine(routine, args, em.#transactionContext);
|
|
1378
|
+
}
|
|
1308
1379
|
/**
|
|
1309
1380
|
* Fires native delete query. Calling this has no side effects on the context (identity map).
|
|
1310
1381
|
*/
|
|
@@ -1453,6 +1524,29 @@ export class EntityManager {
|
|
|
1453
1524
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1454
1525
|
return +count;
|
|
1455
1526
|
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Counts entities grouped by one or more properties. Returns a dictionary keyed by the grouped
|
|
1529
|
+
* field value(s), with counts as values. For composite `groupBy`, keys are joined with `~~~`.
|
|
1530
|
+
*
|
|
1531
|
+
* SQL drivers issue a single `GROUP BY` query; MongoDB uses an aggregation pipeline.
|
|
1532
|
+
*
|
|
1533
|
+
* @example
|
|
1534
|
+
* ```ts
|
|
1535
|
+
* // Count books per author
|
|
1536
|
+
* const counts = await em.countBy(Book, 'author');
|
|
1537
|
+
* // { '1': 2, '2': 1, '3': 3 }
|
|
1538
|
+
*
|
|
1539
|
+
* // Count with a filter
|
|
1540
|
+
* const counts = await em.countBy(Book, 'author', { where: { active: true } });
|
|
1541
|
+
*
|
|
1542
|
+
* // Composite groupBy — keys joined with ~~~
|
|
1543
|
+
* const counts = await em.countBy(Order, ['status', 'country']);
|
|
1544
|
+
* // { 'pending~~~US': 5, 'shipped~~~DE': 3 }
|
|
1545
|
+
* ```
|
|
1546
|
+
*/
|
|
1547
|
+
async countBy(entityName, groupBy, options) {
|
|
1548
|
+
throw new Error(`${this.constructor.name}.countBy() is not supported by the current driver`);
|
|
1549
|
+
}
|
|
1456
1550
|
/**
|
|
1457
1551
|
* Tells the EntityManager to make an instance managed and persistent.
|
|
1458
1552
|
* The entity will be entered into the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1558,7 +1652,7 @@ export class EntityManager {
|
|
|
1558
1652
|
const em = this.getContext();
|
|
1559
1653
|
em.prepareOptions(options);
|
|
1560
1654
|
const entityName = arr[0].constructor;
|
|
1561
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1655
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters, populateHints: options.populateHints }, options.validate);
|
|
1562
1656
|
await em.#entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1563
1657
|
return entities;
|
|
1564
1658
|
}
|
|
@@ -1591,6 +1685,8 @@ export class EntityManager {
|
|
|
1591
1685
|
fork.#filterParams = Utils.copy(em.#filterParams);
|
|
1592
1686
|
fork.loggerContext = Utils.merge({}, em.loggerContext, options.loggerContext);
|
|
1593
1687
|
fork.#schema = options.schema ?? em.#schema;
|
|
1688
|
+
fork.signal = options.signal ?? em.signal;
|
|
1689
|
+
fork.inflightQueryAbortStrategy = options.inflightQueryAbortStrategy ?? em.inflightQueryAbortStrategy;
|
|
1594
1690
|
if (!options.clear) {
|
|
1595
1691
|
for (const entity of em.#unitOfWork.getIdentityMap()) {
|
|
1596
1692
|
fork.#unitOfWork.register(entity);
|
|
@@ -1666,6 +1762,22 @@ export class EntityManager {
|
|
|
1666
1762
|
getTransactionContext() {
|
|
1667
1763
|
return this.getContext(false).#transactionContext;
|
|
1668
1764
|
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Returns the cancellation defaults configured on this EntityManager (via `em.fork({ signal })`
|
|
1767
|
+
* or inherited from a transactional fork). Returns `undefined` when no signal is set.
|
|
1768
|
+
*
|
|
1769
|
+
* @internal — exposed for subclass drivers and `UnitOfWork`; not part of the public API.
|
|
1770
|
+
*/
|
|
1771
|
+
getAbortOptions() {
|
|
1772
|
+
const em = this.getContext(false);
|
|
1773
|
+
if (em.signal == null && em.inflightQueryAbortStrategy == null) {
|
|
1774
|
+
return undefined;
|
|
1775
|
+
}
|
|
1776
|
+
return {
|
|
1777
|
+
signal: em.signal,
|
|
1778
|
+
inflightQueryAbortStrategy: em.inflightQueryAbortStrategy,
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1669
1781
|
/**
|
|
1670
1782
|
* Sets the transaction context.
|
|
1671
1783
|
*/
|
|
@@ -1709,6 +1821,79 @@ export class EntityManager {
|
|
|
1709
1821
|
throw ValidationError.transactionRequired();
|
|
1710
1822
|
}
|
|
1711
1823
|
}
|
|
1824
|
+
validateIndexUsage(meta, where, options) {
|
|
1825
|
+
if (!options.using) {
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
const indexNames = Utils.asArray(options.using);
|
|
1829
|
+
const allIndexes = [...meta.indexes, ...meta.uniques];
|
|
1830
|
+
const indexMap = new Map();
|
|
1831
|
+
for (const idx of allIndexes) {
|
|
1832
|
+
if (idx.name) {
|
|
1833
|
+
indexMap.set(idx.name, Utils.asArray(idx.properties ?? []));
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
for (const prop of meta.props) {
|
|
1837
|
+
if (typeof prop.index === 'string') {
|
|
1838
|
+
indexMap.set(prop.index, [prop.name]);
|
|
1839
|
+
}
|
|
1840
|
+
if (typeof prop.unique === 'string') {
|
|
1841
|
+
indexMap.set(prop.unique, [prop.name]);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
const allowedProps = new Set();
|
|
1845
|
+
for (const name of indexNames) {
|
|
1846
|
+
const props = indexMap.get(name);
|
|
1847
|
+
if (!props) {
|
|
1848
|
+
const available = [...indexMap.keys()];
|
|
1849
|
+
throw new Error(`Index '${name}' not found on entity '${meta.className}'. ` +
|
|
1850
|
+
(available.length > 0 ? `Available indexes: ${available.join(', ')}` : 'No named indexes defined.'));
|
|
1851
|
+
}
|
|
1852
|
+
for (const prop of props) {
|
|
1853
|
+
allowedProps.add(prop);
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
this.validateWhereKeysForIndex(where, allowedProps, indexNames);
|
|
1857
|
+
if (options.orderBy) {
|
|
1858
|
+
const orderMaps = Array.isArray(options.orderBy) ? options.orderBy : [options.orderBy];
|
|
1859
|
+
for (const orderMap of orderMaps) {
|
|
1860
|
+
for (const key of Object.keys(orderMap)) {
|
|
1861
|
+
if (!allowedProps.has(key)) {
|
|
1862
|
+
throw new Error(`Property '${key}' in orderBy is not covered by index '${indexNames.join("', '")}'. ` +
|
|
1863
|
+
`Allowed properties: ${[...allowedProps].join(', ')}`);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
validateWhereKeysForIndex(where, allowedProps, indexNames) {
|
|
1870
|
+
if (typeof where !== 'object' || where === null) {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
if (Array.isArray(where)) {
|
|
1874
|
+
for (const item of where) {
|
|
1875
|
+
this.validateWhereKeysForIndex(item, allowedProps, indexNames);
|
|
1876
|
+
}
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
for (const key of Object.keys(where)) {
|
|
1880
|
+
if (key === '$and' || key === '$or') {
|
|
1881
|
+
for (const item of Utils.asArray(where[key])) {
|
|
1882
|
+
this.validateWhereKeysForIndex(item, allowedProps, indexNames);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
else if (key === '$not') {
|
|
1886
|
+
this.validateWhereKeysForIndex(where[key], allowedProps, indexNames);
|
|
1887
|
+
}
|
|
1888
|
+
else if (key.startsWith('$')) {
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
else if (!allowedProps.has(key)) {
|
|
1892
|
+
throw new Error(`Property '${key}' in where clause is not covered by index '${indexNames.join("', '")}'. ` +
|
|
1893
|
+
`Allowed properties: ${[...allowedProps].join(', ')}`);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1712
1897
|
async lockAndPopulate(meta, entity, where, options) {
|
|
1713
1898
|
if (!meta.virtual && options.lockMode === LockMode.OPTIMISTIC) {
|
|
1714
1899
|
await this.lock(entity, options.lockMode, {
|
|
@@ -1839,9 +2024,21 @@ export class EntityManager {
|
|
|
1839
2024
|
}
|
|
1840
2025
|
if (options.populateHints) {
|
|
1841
2026
|
applyPopulateHints(populate, options.populateHints);
|
|
2027
|
+
this.forceSelectInForLimitedPopulate(populate);
|
|
1842
2028
|
}
|
|
1843
2029
|
return populate;
|
|
1844
2030
|
}
|
|
2031
|
+
/** Force SELECT_IN strategy on populate entries with `limit`, since JOINED cannot do per-parent limiting. */
|
|
2032
|
+
forceSelectInForLimitedPopulate(populate) {
|
|
2033
|
+
for (const entry of populate) {
|
|
2034
|
+
if (entry.limit != null) {
|
|
2035
|
+
entry.strategy = LoadStrategy.SELECT_IN;
|
|
2036
|
+
}
|
|
2037
|
+
if (entry.children?.length) {
|
|
2038
|
+
this.forceSelectInForLimitedPopulate(entry.children);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1845
2042
|
/**
|
|
1846
2043
|
* when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
|
|
1847
2044
|
* some additional lazy properties, if so, we reload and merge the data from database
|
|
@@ -1873,6 +2070,8 @@ export class EntityManager {
|
|
|
1873
2070
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1874
2071
|
}
|
|
1875
2072
|
options.schema ??= this.#schema;
|
|
2073
|
+
options.signal ??= this.signal;
|
|
2074
|
+
options.inflightQueryAbortStrategy ??= this.inflightQueryAbortStrategy;
|
|
1876
2075
|
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1877
2076
|
}
|
|
1878
2077
|
/**
|
|
@@ -1881,7 +2080,15 @@ export class EntityManager {
|
|
|
1881
2080
|
cacheKey(entityName, options, method, where) {
|
|
1882
2081
|
const { ...opts } = options;
|
|
1883
2082
|
// ignore some irrelevant options, e.g. logger context can contain dynamic data for the same query
|
|
1884
|
-
for (const k of [
|
|
2083
|
+
for (const k of [
|
|
2084
|
+
'ctx',
|
|
2085
|
+
'strategy',
|
|
2086
|
+
'flushMode',
|
|
2087
|
+
'logging',
|
|
2088
|
+
'loggerContext',
|
|
2089
|
+
'signal',
|
|
2090
|
+
'inflightQueryAbortStrategy',
|
|
2091
|
+
]) {
|
|
1885
2092
|
delete opts[k];
|
|
1886
2093
|
}
|
|
1887
2094
|
return [Utils.className(entityName), method, opts, where];
|
|
@@ -1974,6 +2181,8 @@ export class EntityManager {
|
|
|
1974
2181
|
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1975
2182
|
case 'm:n':
|
|
1976
2183
|
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
2184
|
+
case 'count':
|
|
2185
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getCountBatchLoadFn(em)));
|
|
1977
2186
|
}
|
|
1978
2187
|
}
|
|
1979
2188
|
/**
|
package/MikroORM.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Configuration, type Options } from './utils/Configuration.js';
|
|
|
5
5
|
import type { EntityManager } from './EntityManager.js';
|
|
6
6
|
import type { AnyEntity, Constructor, EntityClass, EntityMetadata, EntityName, IEntityGenerator, IMigrator, ISeedManager } from './typings.js';
|
|
7
7
|
/** @internal */
|
|
8
|
-
export declare function loadOptionalDependencies(options: Partial<Options
|
|
8
|
+
export declare function loadOptionalDependencies(options: Partial<Options<any, any, any>>): Promise<void>;
|
|
9
9
|
/**
|
|
10
10
|
* The main class used to configure and bootstrap the ORM.
|
|
11
11
|
*
|
|
@@ -34,7 +34,7 @@ export declare function loadOptionalDependencies(options: Partial<Options>): Pro
|
|
|
34
34
|
* await em.flush();
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
-
export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver, EM extends Driver[typeof EntityManagerType] & EntityManager<Driver> = Driver[typeof EntityManagerType] & EntityManager<Driver>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> {
|
|
37
|
+
export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver, EM extends Driver[typeof EntityManagerType] & EntityManager<Driver> = Driver[typeof EntityManagerType] & EntityManager<Driver>, Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]> {
|
|
38
38
|
#private;
|
|
39
39
|
/** The global EntityManager instance. If you are using `RequestContext` helper, it will automatically pick the request specific context under the hood */
|
|
40
40
|
em: EM & {
|
|
@@ -48,7 +48,7 @@ export declare class MikroORM<Driver extends IDatabaseDriver = IDatabaseDriver,
|
|
|
48
48
|
* Initialize the ORM, load entity metadata, create EntityManager and connect to the database.
|
|
49
49
|
* If you omit the `options` parameter, your CLI config will be used.
|
|
50
50
|
*/
|
|
51
|
-
static init<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager<D> = D[typeof EntityManagerType] & EntityManager<D>, Entities extends (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<D, EM, Entities>>): Promise<MikroORM<D, EM, Entities>>;
|
|
51
|
+
static init<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager<D> = D[typeof EntityManagerType] & EntityManager<D>, Entities extends readonly (string | EntityClass<AnyEntity> | EntitySchema)[] = (string | EntityClass<AnyEntity> | EntitySchema)[]>(options: Partial<Options<D, EM, Entities>>): Promise<MikroORM<D, EM, Entities>>;
|
|
52
52
|
/**
|
|
53
53
|
* Synchronous variant of the `init` method with some limitations:
|
|
54
54
|
* - folder-based discovery not supported
|