220 lines
7.0 KiB
JavaScript
220 lines
7.0 KiB
JavaScript
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);
|
|
}
|
|
}
|