@mikro-orm/core
Version:
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.
129 lines (128 loc) • 4.93 kB
JavaScript
import { EntityMetadata } from '../typings.js';
import { Utils } from '../utils/Utils.js';
import { MetadataError } from '../errors.js';
import { EntityHelper } from '../entity/EntityHelper.js';
import { EntitySchema } from './EntitySchema.js';
function getGlobalStorage(namespace) {
const key = `mikro-orm-${namespace}`;
globalThis[key] = globalThis[key] || {};
return globalThis[key];
}
/** Registry that stores and provides access to entity metadata by class, name, or id. */
export class MetadataStorage {
static PATH_SYMBOL = Symbol.for('@mikro-orm/core/MetadataStorage.PATH_SYMBOL');
static #metadata = getGlobalStorage('metadata');
#metadataMap = new Map();
#idMap;
#classNameMap;
#uniqueNameMap;
constructor(metadata = {}) {
this.#idMap = {};
this.#uniqueNameMap = {};
this.#classNameMap = Utils.copy(metadata, false);
for (const meta of Object.values(this.#classNameMap)) {
this.#idMap[meta._id] = meta;
this.#uniqueNameMap[meta.uniqueName] = meta;
this.#metadataMap.set(meta.class, meta);
}
}
static getMetadata(entity, path) {
const key = entity && path ? entity + '-' + Utils.hash(path) : null;
if (key && !MetadataStorage.#metadata[key]) {
MetadataStorage.#metadata[key] = new EntityMetadata({ className: entity, path });
}
if (key) {
return MetadataStorage.#metadata[key];
}
return MetadataStorage.#metadata;
}
/** Checks whether an entity with the given class name exists in the global metadata. */
static isKnownEntity(name) {
return !!Object.values(this.#metadata).find(meta => meta.className === name);
}
/** Clears all entries from the global metadata registry. */
static clear() {
Object.keys(this.#metadata).forEach(k => delete this.#metadata[k]);
}
/** Returns the map of all registered entity metadata. */
getAll() {
return this.#metadataMap;
}
/** Returns metadata for the given entity, optionally initializing it if not found. */
get(entityName, init = false) {
const exists = this.find(entityName);
if (exists) {
return exists;
}
const className = Utils.className(entityName);
if (!init) {
throw MetadataError.missingMetadata(className);
}
const meta = new EntityMetadata({ class: entityName, name: className });
this.set(entityName, meta);
return meta;
}
/** Finds metadata for the given entity, returning undefined if not registered. */
find(entityName) {
if (!entityName) {
return;
}
const meta = this.#metadataMap.get(entityName);
if (meta) {
return meta;
}
if (EntitySchema.is(entityName)) {
return this.#metadataMap.get(entityName.meta.class) ?? entityName.meta;
}
return this.#classNameMap[Utils.className(entityName)];
}
/** Checks whether metadata exists for the given entity. */
has(entityName) {
return this.#metadataMap.has(entityName);
}
/** Registers metadata for the given entity. */
set(entityName, meta) {
this.#metadataMap.set(entityName, meta);
this.#idMap[meta._id] = meta;
this.#uniqueNameMap[meta.uniqueName] = meta;
this.#classNameMap[Utils.className(entityName)] = meta;
return meta;
}
/** Removes metadata for the given entity from all internal maps. */
reset(entityName) {
const meta = this.find(entityName);
if (meta) {
this.#metadataMap.delete(meta.class);
delete this.#idMap[meta._id];
delete this.#uniqueNameMap[meta.uniqueName];
delete this.#classNameMap[meta.className];
}
}
/** Decorates all entity prototypes with helper methods (e.g. init, toJSON). */
decorate(em) {
[...this.#metadataMap.values()].filter(meta => meta.prototype).forEach(meta => EntityHelper.decorate(meta, em));
}
*[Symbol.iterator]() {
for (const meta of this.#metadataMap.values()) {
yield meta;
}
}
/** Returns metadata by its internal numeric id. */
getById(id) {
return this.#idMap[id];
}
/** Returns metadata by class name, optionally throwing if not found. */
getByClassName(className, validate = true) {
return this.validate(this.#classNameMap[className], className, validate);
}
/** Returns metadata by unique name, optionally throwing if not found. */
getByUniqueName(uniqueName, validate = true) {
return this.validate(this.#uniqueNameMap[uniqueName], uniqueName, validate);
}
validate(meta, id, validate) {
if (!meta && validate) {
throw MetadataError.missingMetadata(id);
}
return meta;
}
}