import { MetadataDiscovery } from './metadata/MetadataDiscovery.js'; import { MetadataStorage } from './metadata/MetadataStorage.js'; import { Configuration } from './utils/Configuration.js'; import { loadEnvironmentVars } from './utils/env-vars.js'; import { Utils } from './utils/Utils.js'; import { colors } from './logging/colors.js'; async function tryRegisterExtension(name, pkg, extensions) { try { const url = import.meta.resolve(pkg); const mod = await import(url); if (mod[name]) { extensions.push(mod[name]); } } catch { // not installed } } /** @internal */ export async function loadOptionalDependencies(options) { await import('@mikro-orm/core/fs-utils').then(m => m.fs.init()).catch(() => null); const extensions = options.extensions ?? []; const exists = name => extensions.some(ext => ext.name === name); if (!exists('SeedManager')) { await tryRegisterExtension('SeedManager', '@mikro-orm/seeder', extensions); } if (!exists('Migrator')) { await tryRegisterExtension('Migrator', '@mikro-orm/migrations', extensions); } /* v8 ignore if */ if (!exists('Migrator')) { await tryRegisterExtension('Migrator', '@mikro-orm/migrations-mongodb', extensions); } if (!exists('EntityGenerator')) { await tryRegisterExtension('EntityGenerator', '@mikro-orm/entity-generator', extensions); } options.extensions = extensions; const metadataCacheEnabled = options.metadataCache?.enabled || options.metadataProvider?.useCache?.(); if (metadataCacheEnabled) { options.metadataCache ??= {}; options.metadataCache.adapter ??= await import('@mikro-orm/core/fs-utils').then(m => m.FileCacheAdapter); } } /** * The main class used to configure and bootstrap the ORM. * * @example * ```ts * // import from driver package * import { MikroORM, defineEntity, p } from '@mikro-orm/sqlite'; * * const User = defineEntity({ * name: 'User', * properties: { * id: p.integer().primary(), * name: p.string(), * }, * }); * * const orm = new MikroORM({ * entities: [User], * dbName: 'my.db', * }); * await orm.schema.update(); * * const em = orm.em.fork(); * const u1 = em.create(User, { name: 'John' }); * const u2 = em.create(User, { name: 'Ben' }); * await em.flush(); * ``` */ export class MikroORM { /** The global EntityManager instance. If you are using `RequestContext` helper, it will automatically pick the request specific context under the hood */ em; /** The database driver instance used by this ORM. */ driver; /** The ORM configuration instance. */ config; #metadata; #logger; #discovery; /** * Initialize the ORM, load entity metadata, create EntityManager and connect to the database. * If you omit the `options` parameter, your CLI config will be used. */ static async init(options) { /* v8 ignore next */ if (!options) { throw new Error(`options parameter is required`); } options = { ...options }; options.discovery ??= {}; options.discovery.skipSyncDiscovery ??= true; await loadOptionalDependencies(options); const orm = new this(options); const preferTs = orm.config.get('preferTs', Utils.detectTypeScriptSupport()); orm.#metadata = await orm.#discovery.discover(preferTs); orm.createEntityManager(); return orm; } /** * Synchronous variant of the `init` method with some limitations: * - folder-based discovery not supported * - ORM extensions are not autoloaded * - when metadata cache is enabled, `FileCacheAdapter` needs to be explicitly set in the config */ constructor(options) { const env = loadEnvironmentVars(); options = options.preferEnvVars ? Utils.merge(options, env) : Utils.merge(env, options); this.config = new Configuration(options); const discovery = this.config.get('discovery'); this.driver = this.config.getDriver(); this.#logger = this.config.getLogger(); this.#logger.log('info', `MikroORM version: ${colors.green(Utils.getORMVersion())}`); this.#discovery = new MetadataDiscovery(new MetadataStorage(), this.driver.getPlatform(), this.config); this.driver.getPlatform().init(this); for (const extension of this.config.get('extensions')) { extension.register(this); } if (!discovery.skipSyncDiscovery) { this.#metadata = this.#discovery.discoverSync(); this.createEntityManager(); } } /** * Connects to the database. */ async connect() { await this.driver.connect(); return this.driver; } /** * Reconnects, possibly to a different database. */ async reconnect(options = {}) { /* v8 ignore next */ for (const key of Utils.keys(options)) { this.config.set(key, options[key]); } await this.driver.reconnect(); } /** * Checks whether the database connection is active. */ async isConnected() { return this.driver.getConnection().isConnected(); } /** * Checks whether the database connection is active, returns the reason if not. */ async checkConnection() { return this.driver.getConnection().checkConnection(); } /** * Closes the database connection. */ async close(force = false) { await this.driver.close(force); await this.config.getMetadataCacheAdapter()?.close?.(); await this.config.getResultCacheAdapter()?.close?.(); } /** * Gets the `MetadataStorage` (without parameters) or `EntityMetadata` instance when provided with the `entityName` parameter. */ getMetadata(entityName) { if (entityName) { return this.#metadata.get(entityName); } return this.#metadata; } createEntityManager() { this.driver.setMetadata(this.#metadata); this.em = this.driver.createEntityManager(); this.em.global = true; this.#metadata.decorate(this.em); this.driver.setMetadata(this.#metadata); } /** * Allows dynamically discovering new entity by reference, handy for testing schema diffing. */ discoverEntity(entities, reset) { for (const className of Utils.asArray(reset)) { this.#metadata.reset(className); this.#discovery.reset(className); } const tmp = this.#discovery.discoverReferences(Utils.asArray(entities)); const metadata = this.#discovery.processDiscoveredEntities(tmp); for (const meta of metadata) { this.#metadata.set(meta.class, meta); meta.root = this.#metadata.get(meta.root.class); } this.#metadata.decorate(this.em); } /** * Gets the SchemaGenerator. */ get schema() { return this.config.getExtension('@mikro-orm/schema-generator'); } /** * Gets the SeedManager */ get seeder() { return this.driver.getPlatform().getExtension('SeedManager', '@mikro-orm/seeder', '@mikro-orm/seeder', this.em); } /** * Gets the Migrator. */ get migrator() { return this.driver.getPlatform().getExtension('Migrator', '@mikro-orm/migrator', '@mikro-orm/migrations', this.em); } /** * Gets the EntityGenerator. */ get entityGenerator() { return this.driver .getPlatform() .getExtension('EntityGenerator', '@mikro-orm/entity-generator', '@mikro-orm/entity-generator', this.em); } }