import { type Configuration } from './utils/Configuration.js';
import { Cursor } from './utils/Cursor.js';
import { EntityFactory } from './entity/EntityFactory.js';
import { type AssignOptions } from './entity/EntityAssigner.js';
import { type EntityRepository } from './entity/EntityRepository.js';
import { EntityLoader, type EntityLoaderOptions } from './entity/EntityLoader.js';
import { Reference } from './entity/Reference.js';
import { UnitOfWork } from './unit-of-work/UnitOfWork.js';
import type {
  CountOptions,
  DeleteOptions,
  FilterOptions,
  FindAllOptions,
  FindByCursorOptions,
  FindOneOptions,
  FindOneOrFailOptions,
  FindOptions,
  GetReferenceOptions,
  IDatabaseDriver,
  LockOptions,
  NativeInsertUpdateOptions,
  StreamOptions,
  UpdateOptions,
  UpsertManyOptions,
  UpsertOptions,
} from './drivers/IDatabaseDriver.js';
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';
import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums.js';
import type { MetadataStorage } from './metadata/MetadataStorage.js';
import type { Transaction } from './connections/Connection.js';
import { EventManager } from './events/EventManager.js';
import type { EntityComparator } from './utils/EntityComparator.js';
/**
 * The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
 * such as UnitOfWork, Query Language, and Repository API.
 * @template {IDatabaseDriver} Driver current driver type
 */
export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDriver> {
  #private;
  readonly config: Configuration;
  protected readonly driver: Driver;
  protected readonly metadata: MetadataStorage;
  protected readonly eventManager: EventManager;
  /** @internal */
  readonly '~entities'?: unknown;
  /** @internal */
  readonly _id: number;
  /** Whether this is the global (root) EntityManager instance. */
  readonly global = false;
  /** The context name of this EntityManager, derived from the ORM configuration. */
  readonly name: string;
  protected loggerContext?: Dictionary;
  /**
   * @internal
   */
  constructor(
    config: Configuration,
    driver: Driver,
    metadata: MetadataStorage,
    useContext?: boolean,
    eventManager?: EventManager,
  );
  /**
   * Gets the Driver instance used by this EntityManager.
   * Driver is singleton, for one MikroORM instance, only one driver is created.
   */
  getDriver(): Driver;
  /**
   * Gets the Connection instance, by default returns write connection
   */
  getConnection(type?: ConnectionType): ReturnType<Driver['getConnection']>;
  /**
   * Gets the platform instance. Just like the driver, platform is singleton, one for a MikroORM instance.
   */
  getPlatform(): ReturnType<Driver['getPlatform']>;
  /**
   * Gets repository for given entity. You can pass either string name or entity class reference.
   */
  getRepository<Entity extends object, Repository extends EntityRepository<Entity> = EntityRepository<Entity>>(
    entityName: EntityName<Entity>,
  ): GetRepository<Entity, Repository>;
  /**
   * Shortcut for `em.getRepository()`.
   */
  repo<Entity extends object, Repository extends EntityRepository<Entity> = EntityRepository<Entity>>(
    entityName: EntityName<Entity>,
  ): GetRepository<Entity, Repository>;
  /**
   * Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
   */
  find<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = PopulatePath.ALL,
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    where: FilterQuery<NoInfer<Entity>>,
    options?: FindOptions<Entity, Hint, Fields, Excludes>,
  ): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
  /**
   * Finds all entities and returns an async iterable (async generator) that yields results one by one.
   * The results are merged and mapped to entity instances, without adding them to the identity map.
   * You can disable merging by passing the options `{ mergeResults: false }`.
   * With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
   * root entities when there are multiple items in the populated collection.
   * This is useful for processing large datasets without loading everything into memory at once.
   *
   * ```ts
   * const stream = em.stream(Book, { populate: ['author'] });
   *
   * for await (const book of stream) {
   *   // book is an instance of Book entity
   *   console.log(book.title, book.author.name);
   * }
   * ```
   */
  stream<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    options?: StreamOptions<NoInfer<Entity>, Hint, Fields, Excludes>,
  ): AsyncIterableIterator<Loaded<Entity, Hint, Fields, Excludes>>;
  /**
   * Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
   */
  findAll<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    options?: FindAllOptions<NoInfer<Entity>, Hint, Fields, Excludes>,
  ): Promise<Loaded<Entity, Hint, Fields, Excludes>[]>;
  private getPopulateWhere;
  /**
   * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
   */
  addFilter<T extends EntityName | readonly EntityName[]>(options: FilterDef<T>): void;
  /**
   * Sets filter parameter values globally inside context defined by this entity manager.
   * If you want to set shared value for all contexts, be sure to use the root entity manager.
   */
  setFilterParams(name: string, args: Dictionary): void;
  /**
   * Returns filter parameters for given filter set in this context.
   */
  getFilterParams<T extends Dictionary = Dictionary>(name: string): T;
  /**
   * Sets logger context for this entity manager.
   */
  setLoggerContext(context: Dictionary): void;
  /**
   * Gets logger context for this entity manager.
   */
  getLoggerContext<T extends Dictionary = Dictionary>(options?: { disableContextResolution?: boolean }): T;
  /** Sets the flush mode for this EntityManager. Pass `undefined` to reset to the global default. */
  setFlushMode(flushMode?: FlushMode | `${FlushMode}`): void;
  protected processWhere<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    where: FilterQuery<Entity>,
    options: FindOptions<Entity, Hint, Fields, Excludes> | FindOneOptions<Entity, Hint, Fields, Excludes>,
    type: 'read' | 'update' | 'delete',
  ): Promise<FilterQuery<Entity>>;
  protected processUnionWhere<Entity extends object, Hint extends string = never>(
    entityName: EntityName<Entity>,
    options:
      | FindOptions<Entity, Hint, any, any>
      | CountOptions<Entity, Hint>
      | UpdateOptions<Entity>
      | DeleteOptions<Entity>,
    type: 'read' | 'update' | 'delete',
  ): Promise<void>;
  protected applyDiscriminatorCondition<Entity extends object>(
    entityName: EntityName<Entity>,
    where: FilterQuery<Entity>,
  ): FilterQuery<Entity>;
  protected createPopulateWhere<Entity extends object>(
    cond: ObjectQuery<Entity>,
    options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any> | CountOptions<Entity, any>,
  ): ObjectQuery<Entity>;
  protected getJoinedFilters<Entity extends object>(
    meta: EntityMetadata<Entity>,
    options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>,
  ): Promise<ObjectQuery<Entity> | undefined>;
  /**
   * 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.
   */
  protected autoJoinRefsForFilters<T extends object>(
    meta: EntityMetadata<T>,
    options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any>,
    parent?: {
      class: EntityClass;
      propName: string;
    },
  ): Promise<void>;
  /**
   * @internal
   */
  applyFilters<Entity extends object>(
    entityName: EntityName<Entity>,
    where: FilterQuery<Entity> | undefined,
    options: FilterOptions | undefined,
    type: 'read' | 'update' | 'delete',
    findOptions?: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>,
  ): Promise<FilterQuery<Entity> | undefined>;
  /**
   * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as tuple
   * where the first element is the array of entities, and the second is the count.
   */
  findAndCount<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    where: FilterQuery<NoInfer<Entity>>,
    options?: FindOptions<Entity, Hint, Fields, Excludes>,
  ): Promise<[Loaded<Entity, Hint, Fields, Excludes>[], number]>;
  /**
   * Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
   * Supports `before`, `after`, `first` and `last` options while disallowing `limit` and `offset`. Explicit `orderBy` option
   * is required.
   *
   * Use `first` and `after` for forward pagination, or `last` and `before` for backward pagination.
   *
   * - `first` and `last` are numbers and serve as an alternative to `offset`, those options are mutually exclusive, use only one at a time
   * - `before` and `after` specify the previous cursor value, it can be one of the:
   *     - `Cursor` instance
   *     - opaque string provided by `startCursor/endCursor` properties
   *     - POJO/entity instance
   *
   * ```ts
   * const currentCursor = await em.findByCursor(User, {
   *   first: 10,
   *   after: previousCursor, // cursor instance
   *   orderBy: { id: 'desc' },
   * });
   *
   * // to fetch next page
   * const nextCursor = await em.findByCursor(User, {
   *   first: 10,
   *   after: currentCursor.endCursor, // opaque string
   *   orderBy: { id: 'desc' },
   * });
   *
   * // to fetch next page
   * const nextCursor2 = await em.findByCursor(User, {
   *   first: 10,
   *   after: { id: lastSeenId }, // entity-like POJO
   *   orderBy: { id: 'desc' },
   * });
   * ```
   *
   * The options also support an `includeCount` (true by default) option. If set to false, the `totalCount` is not
   * returned as part of the cursor. This is useful for performance reason, when you don't care about the total number
   * of pages.
   *
   * The `Cursor` object provides the following interface:
   *
   * ```ts
   * Cursor<User> {
   *   items: [
   *     User { ... },
   *     User { ... },
   *     User { ... },
   *   ],
   *   totalCount: 50, // not included if `includeCount: false`
   *   startCursor: 'WzRd',
   *   endCursor: 'WzZd',
   *   hasPrevPage: true,
   *   hasNextPage: true,
   * }
   * ```
   */
  findByCursor<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    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>>;
  /**
   * Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
   * persisted. Returns the same entity instance (same object reference), but re-hydrated. If the entity is no longer
   * in database, the method throws an error just like `em.findOneOrFail()` (and respects the same config options).
   */
  refreshOrFail<
    Entity extends object,
    Naked extends FromEntityType<Entity> = FromEntityType<Entity>,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entity: Entity,
    options?: FindOneOrFailOptions<Entity, Hint, Fields, Excludes>,
  ): Promise<MergeLoaded<Entity, Naked, Hint, Fields, Excludes, true>>;
  /**
   * Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
   * persisted. Returns the same entity instance (same object reference), but re-hydrated. If the entity is no longer
   * in database, the method returns `null`.
   */
  refresh<
    Entity extends object,
    Naked extends FromEntityType<Entity> = FromEntityType<Entity>,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entity: Entity,
    options?: FindOneOptions<Entity, Hint, Fields, Excludes>,
  ): Promise<MergeLoaded<Entity, Naked, Hint, Fields, Excludes, true> | null>;
  /**
   * Finds first entity matching your `where` query.
   */
  findOne<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    where: FilterQuery<NoInfer<Entity>>,
    options?: FindOneOptions<Entity, Hint, Fields, Excludes>,
  ): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
  /**
   * Finds first entity matching your `where` query. If nothing found, it will throw an error.
   * If the `strict` option is specified and nothing is found or more than one matching entity is found, it will throw an error.
   * You can override the factory for creating this method via `options.failHandler` locally
   * or via `Configuration.findOneOrFailHandler` (`findExactlyOneOrFailHandler` when specifying `strict`) globally.
   */
  findOneOrFail<
    Entity extends object,
    Hint extends string = never,
    Fields extends string = '*',
    Excludes extends string = never,
  >(
    entityName: EntityName<Entity>,
    where: FilterQuery<NoInfer<Entity>>,
    options?: FindOneOrFailOptions<Entity, Hint, Fields, Excludes>,
  ): Promise<Loaded<Entity, Hint, Fields, Excludes>>;
  /**
   * Creates or updates the entity, based on whether it is already present in the database.
   * This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed
   * entity instance. The method accepts either `entityName` together with the entity `data`, or just entity instance.
   *
   * ```ts
   * // insert into "author" ("age", "email") values (33, 'foo@bar.com') on conflict ("email") do update set "age" = 41
   * const author = await em.upsert(Author, { email: 'foo@bar.com', age: 33 });
   * ```
   *
   * 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:
   *
   * ```ts
   * // insert into "author" ("age", "email") values (33, 'foo@bar.com') on conflict ("email") do update set "age" = 41
   * // select "id" from "author" where "email" = 'foo@bar.com'
   * const author = await em.upsert(Author, { email: 'foo@bar.com', age: 33 });
   * ```
   *
   * 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`.
   *
   * 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.
   */
  upsert<Entity extends object, Fields extends string = any>(
    entityNameOrEntity: EntityName<Entity> | Entity,
    data?: EntityData<Entity> | NoInfer<Entity>,
    options?: UpsertOptions<Entity, Fields>,
  ): Promise<Entity>;
  /**
   * Creates or updates the entity, based on whether it is already present in the database.
   * This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed
   * entity instance. The method accepts either `entityName` together with the entity `data`, or just entity instance.
   *
   * ```ts
   * // insert into "author" ("age", "email") values (33, 'foo@bar.com') on conflict ("email") do update set "age" = 41
   * const authors = await em.upsertMany(Author, [{ email: 'foo@bar.com', age: 33 }, ...]);
   * ```
   *
   * 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:
   *
   * ```ts
   * // insert into "author" ("age", "email") values (33, 'foo@bar.com'), (666, 'lol@lol.lol') on conflict ("email") do update set "age" = excluded."age"
   * // select "id" from "author" where "email" = 'foo@bar.com'
   * const author = await em.upsertMany(Author, [
   *   { email: 'foo@bar.com', age: 33 },
   *   { email: 'lol@lol.lol', age: 666 },
   * ]);
   * ```
   *
   * 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`.
   *
   * 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.
   */
  upsertMany<Entity extends object, Fields extends string = any>(
    entityNameOrEntity: EntityName<Entity> | Entity[],
    data?: (EntityData<Entity> | NoInfer<Entity>)[],
    options?: UpsertManyOptions<Entity, Fields>,
  ): Promise<Entity[]>;
  /**
   * Runs your callback wrapped inside a database transaction.
   *
   * If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
   * can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
   * should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
   * method automatically creates an async context for the transaction.
   *
   * **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
   * `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
   * between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
   * the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
   *
   * **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
   * parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
   * and then call this method on the fork.
   *
   * **Example:**
   * ```ts
   * await em.transactional(async (em) => {
   *   const author = new Author('Jon');
   *   em.persist(author);
   *   // flush is called automatically at the end of the callback
   * });
   * ```
   */
  transactional<T>(cb: (em: this) => T | Promise<T>, options?: TransactionOptions): Promise<T>;
  /**
   * Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
   */
  begin(options?: Omit<TransactionOptions, 'ignoreNestedTransactions'>): Promise<void>;
  /**
   * Commits the transaction bound to this EntityManager. Flushes before doing the actual commit query.
   */
  commit(): Promise<void>;
  /**
   * Rollbacks the transaction bound to this EntityManager.
   */
  rollback(): Promise<void>;
  /**
   * Runs your callback wrapped inside a database transaction.
   */
  lock<T extends object>(entity: T, lockMode: LockMode, options?: LockOptions | number | Date): Promise<void>;
  /**
   * Fires native insert query. Calling this has no side effects on the context (identity map).
   */
  insert<Entity extends object>(
    entityNameOrEntity: EntityName<Entity> | Entity,
    data?: RequiredEntityData<Entity> | Entity,
    options?: NativeInsertUpdateOptions<Entity>,
  ): Promise<Primary<Entity>>;
  /**
   * Fires native multi-insert query. Calling this has no side effects on the context (identity map).
   */
  insertMany<Entity extends object>(
    entityNameOrEntities: EntityName<Entity> | Entity[],
    data?: RequiredEntityData<Entity>[] | Entity[],
    options?: NativeInsertUpdateOptions<Entity>,
  ): Promise<Primary<Entity>[]>;
  /**
   * Fires native update query. Calling this has no side effects on the context (identity map).
   */
  nativeUpdate<Entity extends object>(
    entityName: EntityName<Entity>,
    where: FilterQuery<NoInfer<Entity>>,
    data: EntityData<Entity>,
    options?: UpdateOptions<Entity>,
  ): Promise<number>;
  /**
   * Fires native delete query. Calling this has no side effects on the context (identity map).
   */
  nativeDelete<Entity extends object>(
    entityName: EntityName<Entity>,
    where: FilterQuery<NoInfer<Entity>>,
    options?: DeleteOptions<Entity>,
  ): Promise<number>;
  /**
   * Maps raw database result to an entity and merges it to this EntityManager.
   */
  map<Entity extends object>(
    entityName: EntityName<Entity>,
    result: EntityDictionary<Entity>,
    options?: {
      schema?: string;
    },
  ): Entity;
  /**
   * Merges given entity to this EntityManager so it becomes managed. You can force refreshing of existing entities
   * via second parameter. By default, it will return already loaded entities without modifying them.
   */
  merge<Entity extends object>(entity: Entity, options?: MergeOptions): Entity;
  /**
   * Merges given entity to this EntityManager so it becomes managed. You can force refreshing of existing entities
   * via second parameter. By default, it will return already loaded entities without modifying them.
   */
  merge<Entity extends object>(
    entityName: EntityName<Entity>,
    data: EntityData<Entity> | EntityDTO<Entity>,
    options?: MergeOptions,
  ): Entity;
  /**
   * Creates new instance of given entity and populates it with given data.
   * The entity constructor will be used unless you provide `{ managed: true }` in the `options` parameter.
   * The constructor will be given parameters based on the defined constructor of the entity. If the constructor
   * parameter matches a property name, its value will be extracted from `data`. If no matching property exists,
   * the whole `data` parameter will be passed. This means we can also define `constructor(data: Partial<T>)` and
   * `em.create()` will pass the data into it (unless we have a property named `data` too).
   *
   * The parameters are strictly checked, you need to provide all required properties. You can use `OptionalProps`
   * symbol to omit some properties from this check without making them optional. Alternatively, use `partial: true`
   * in the options to disable the strict checks for required properties. This option has no effect on runtime.
   *
   * The newly created entity will be automatically marked for persistence via `em.persist` unless you disable this
   * behavior, either locally via `persist: false` option, or globally via `persistOnCreate` ORM config option.
   */
  create<
    Entity extends object,
    Convert extends boolean = false,
    Data extends RequiredEntityData<Entity, never, Convert> = RequiredEntityData<Entity, never, Convert>,
  >(
    entityName: EntityName<Entity>,
    data: Data & IsSubset<RequiredEntityData<Entity, never, Convert>, Data>,
    options?: CreateOptions<Convert>,
  ): Entity;
  /**
   * Creates new instance of given entity and populates it with given data.
   * The entity constructor will be used unless you provide `{ managed: true }` in the `options` parameter.
   * The constructor will be given parameters based on the defined constructor of the entity. If the constructor
   * parameter matches a property name, its value will be extracted from `data`. If no matching property exists,
   * the whole `data` parameter will be passed. This means we can also define `constructor(data: Partial<T>)` and
   * `em.create()` will pass the data into it (unless we have a property named `data` too).
   *
   * The parameters are strictly checked, you need to provide all required properties. You can use `OptionalProps`
   * symbol to omit some properties from this check without making them optional. Alternatively, use `partial: true`
   * in the options to disable the strict checks for required properties. This option has no effect on runtime.
   *
   * The newly created entity will be automatically marked for persistence via `em.persist` unless you disable this
   * behavior, either locally via `persist: false` option, or globally via `persistOnCreate` ORM config option.
   */
  create<
    Entity extends object,
    Convert extends boolean = false,
    Data extends EntityData<Entity, Convert> = EntityData<Entity, Convert>,
  >(
    entityName: EntityName<Entity>,
    data: Data & IsSubset<EntityData<Entity, Convert>, Data>,
    options: CreateOptions<Convert> & {
      partial: true;
    },
  ): Entity;
  /**
   * Shortcut for `wrap(entity).assign(data, { em })`
   */
  assign<
    Entity extends object,
    Naked extends FromEntityType<Entity> = FromEntityType<Entity>,
    Convert extends boolean = false,
    Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> =
      | EntityData<Naked, Convert>
      | Partial<EntityDTO<Naked>>,
  >(
    entity: Entity | Partial<Entity>,
    data: Data & IsSubset<EntityData<Naked, Convert>, Data>,
    options?: AssignOptions<Convert>,
  ): MergeSelected<Entity, Naked, keyof Data & string>;
  /**
   * Gets a reference to the entity identified by the given type and alternate key property without actually loading it.
   * The key option specifies which property to use for identity map lookup instead of the primary key.
   */
  getReference<Entity extends object, K extends string & keyof Entity>(
    entityName: EntityName<Entity>,
    id: Entity[K],
    options: Omit<GetReferenceOptions, 'key' | 'wrapped'> & {
      key: K;
      wrapped: true;
    },
  ): Ref<Entity>;
  /**
   * Gets a reference to the entity identified by the given type and alternate key property without actually loading it.
   * The key option specifies which property to use for identity map lookup instead of the primary key.
   */
  getReference<Entity extends object, K extends string & keyof Entity>(
    entityName: EntityName<Entity>,
    id: Entity[K],
    options: Omit<GetReferenceOptions, 'key'> & {
      key: K;
      wrapped?: false;
    },
  ): Entity;
  /**
   * Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
   */
  getReference<Entity extends object>(
    entityName: EntityName<Entity>,
    id: Primary<Entity>,
    options: Omit<GetReferenceOptions, 'wrapped' | 'key'> & {
      wrapped: true;
    },
  ): Ref<Entity>;
  /**
   * Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
   */
  getReference<Entity extends object>(entityName: EntityName<Entity>, id: Primary<Entity> | Primary<Entity>[]): Entity;
  /**
   * Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
   */
  getReference<Entity extends object>(
    entityName: EntityName<Entity>,
    id: Primary<Entity>,
    options: Omit<GetReferenceOptions, 'wrapped' | 'key'> & {
      wrapped: false;
    },
  ): Entity;
  /**
   * Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
   */
  getReference<Entity extends object>(
    entityName: EntityName<Entity>,
    id: Primary<Entity>,
    options?: GetReferenceOptions,
  ): Entity | Reference<Entity>;
  /**
   * Returns total number of entities matching your `where` query.
   */
  count<Entity extends object, Hint extends string = never>(
    entityName: EntityName<Entity>,
    where?: FilterQuery<NoInfer<Entity>>,
    options?: CountOptions<Entity, Hint>,
  ): Promise<number>;
  /**
   * Tells the EntityManager to make an instance managed and persistent.
   * The entity will be entered into the database at or before transaction commit or as a result of the flush operation.
   */
  persist<Entity extends object>(entity: Entity | Reference<Entity> | Iterable<Entity | Reference<Entity>>): this;
  /**
   * Marks entity for removal.
   * A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
   *
   * To remove entities by condition, use `em.nativeDelete()`.
   */
  remove<Entity extends object>(entity: Entity | Reference<Entity> | Iterable<Entity | Reference<Entity>>): this;
  /**
   * Flushes all changes to objects that have been queued up to now to the database.
   * This effectively synchronizes the in-memory state of managed objects with the database.
   */
  flush(): Promise<void>;
  /**
   * @internal
   */
  tryFlush<Entity extends object>(
    entityName: EntityName<Entity>,
    options: {
      flushMode?: FlushMode | AnyString;
    },
  ): Promise<void>;
  /**
   * Clears the EntityManager. All entities that are currently managed by this EntityManager become detached.
   */
  clear(): void;
  /**
   * Checks whether given property can be populated on the entity.
   */
  canPopulate<Entity extends object>(entityName: EntityName<Entity>, property: string): boolean;
  /**
   * Loads specified relations in batch. This will execute one query for each relation, that will populate it on all the specified entities.
   */
  populate<
    Entity extends object,
    Naked extends FromEntityType<UnboxArray<Entity>> = FromEntityType<UnboxArray<Entity>>,
    Hint extends string = never,
    Fields extends string = never,
    Excludes extends string = never,
  >(
    entities: Entity,
    populate: readonly AutoPath<Naked, Hint, PopulatePath.ALL>[] | false,
    options?: EntityLoaderOptions<Naked, Fields, Excludes>,
  ): Promise<
    Entity extends object[]
      ? MergeLoaded<ArrayElement<Entity>, Naked, Hint, Fields, Excludes>[]
      : MergeLoaded<Entity, Naked, Hint, Fields, Excludes>
  >;
  /**
   * Returns new EntityManager instance with its own identity map
   */
  fork(options?: ForkOptions): this;
  /**
   * Gets the UnitOfWork used by the EntityManager to coordinate operations.
   */
  getUnitOfWork(useContext?: boolean): UnitOfWork;
  /**
   * Gets the EntityFactory used by the EntityManager.
   */
  getEntityFactory(): EntityFactory;
  /**
   * @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
   */
  getEntityLoader(): EntityLoader;
  /**
   * Gets the Hydrator used by the EntityManager.
   */
  getHydrator(): IHydrator;
  /**
   * Gets the EntityManager based on current transaction/request context.
   * @internal
   */
  getContext(validate?: boolean): this;
  /** Gets the EventManager instance used by this EntityManager. */
  getEventManager(): EventManager;
  /**
   * Checks whether this EntityManager is currently operating inside a database transaction.
   */
  isInTransaction(): boolean;
  /**
   * Gets the transaction context (driver dependent object used to make sure queries are executed on same connection).
   */
  getTransactionContext<T extends Transaction = Transaction>(): T | undefined;
  /**
   * Sets the transaction context.
   */
  setTransactionContext(ctx?: Transaction): void;
  /**
   * Resets the transaction context.
   */
  resetTransactionContext(): void;
  /**
   * Gets the `MetadataStorage`.
   */
  getMetadata(): MetadataStorage;
  /**
   * Gets the `EntityMetadata` instance when provided with the `entityName` parameter.
   */
  getMetadata<Entity extends object>(entityName: EntityName<Entity>): EntityMetadata<Entity>;
  /**
   * Gets the EntityComparator.
   */
  getComparator(): EntityComparator;
  private checkLockRequirements;
  private lockAndPopulate;
  private buildFields;
  /** @internal */
  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>[]>;
  /**
   * when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
   * some additional lazy properties, if so, we reload and merge the data from database
   */
  protected shouldRefresh<T extends object, P extends string = never, F extends string = '*', E extends string = never>(
    meta: EntityMetadata<T>,
    entity: T,
    options: FindOneOptions<T, P, F, E>,
  ): boolean;
  protected prepareOptions(
    options: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any> | CountOptions<any, any>,
  ): void;
  /**
   * @internal
   */
  cacheKey<T extends object>(
    entityName: EntityName<T>,
    options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any> | CountOptions<T, any>,
    method: string,
    where: FilterQuery<T>,
  ): unknown[];
  /**
   * @internal
   */
  tryCache<T extends object, R>(
    entityName: EntityName<T>,
    config: boolean | number | [string, number] | undefined,
    key: unknown,
    refresh?: boolean,
    merge?: boolean,
  ): Promise<
    | {
        data?: R | null;
        key: string;
      }
    | undefined
  >;
  /**
   * @internal
   */
  storeCache<T>(
    config: boolean | number | [string, number] | undefined,
    key: {
      key: string;
    },
    data: T | (() => T),
  ): Promise<void>;
  /**
   * Clears result cache for given cache key. If we want to be able to call this method,
   * we need to set the cache key explicitly when storing the cache.
   *
   * ```ts
   * // set the cache key to 'book-cache-key', with expiration of 60s
   * const res = await em.find(Book, { ... }, { cache: ['book-cache-key', 60_000] });
   *
   * // clear the cache key by name
   * await em.clearCache('book-cache-key');
   * ```
   */
  clearCache(cacheKey: string): Promise<void>;
  /**
   * Returns the default schema of this EntityManager. Respects the context, so global EM will give you the contextual schema
   * if executed inside request context handler.
   */
  get schema(): string | undefined;
  /**
   * Sets the default schema of this EntityManager. Respects the context, so global EM will set the contextual schema
   * if executed inside request context handler.
   */
  set schema(schema: string | null | undefined);
  /** @internal */
  getDataLoader(type: 'ref' | '1:m' | 'm:n'): Promise<any>;
  /**
   * Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
   * if executed inside request context handler.
   */
  get id(): number;
}
export interface CreateOptions<Convert extends boolean> {
  /** creates a managed entity instance instead, bypassing the constructor call */
  managed?: boolean;
  /** create entity in a specific schema - alternatively, use `wrap(entity).setSchema()` */
  schema?: string;
  /** persist the entity automatically - this is the default behavior and is also configurable globally via `persistOnCreate` option */
  persist?: boolean;
  /** this option disables the strict typing which requires all mandatory properties to have value, it has no effect on runtime */
  partial?: boolean;
  /** convert raw database values based on mapped types (by default, already converted values are expected) */
  convertCustomTypes?: Convert;
  /**
   * Property `onCreate` hooks are normally executed during `flush` operation.
   * With this option, they will be processed early inside `em.create()` method.
   */
  processOnCreateHooksEarly?: boolean;
}
export interface MergeOptions {
  refresh?: boolean;
  convertCustomTypes?: boolean;
  schema?: string;
  disableContextResolution?: boolean;
  validate?: boolean;
  cascade?: boolean /** @default true */;
}
export interface ForkOptions {
  /** do we want a clear identity map? defaults to true */
  clear?: boolean;
  /** use request context? should be used only for top level request scope EM, defaults to false */
  useContext?: boolean;
  /** do we want to use fresh EventManager instance? defaults to false (global instance) */
  freshEventManager?: boolean;
  /** do we want to clone current EventManager instance? defaults to false (global instance) */
  cloneEventManager?: boolean;
  /** use this flag to ignore the current async context - this is required if we want to call `em.fork()` inside the `getContext` handler */
  disableContextResolution?: boolean;
  /** set flush mode for this fork, overrides the global option can be overridden locally via FindOptions */
  flushMode?: FlushMode | `${FlushMode}`;
  /** disable transactions for this fork */
  disableTransactions?: boolean;
  /** should we keep the transaction context of the parent EM? */
  keepTransactionContext?: boolean;
  /** default schema to use for this fork */
  schema?: string;
  /** default logger context, can be overridden via {@apilink FindOptions} */
  loggerContext?: Dictionary;
}
