UNPKG

@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.

90 lines (89 loc) 3.42 kB
import { Utils } from '../utils/Utils.js'; import { EventType, EventTypeMap } from '../enums.js'; /** Manages event subscribers and dispatches entity/flush/transaction lifecycle events. */ export class EventManager { #listeners = {}; #entities = new Map(); #cache = new Map(); #subscribers = new Set(); constructor(subscribers) { for (const subscriber of subscribers) { this.registerSubscriber(subscriber); } } /** Registers an event subscriber and indexes its subscribed entities and event types. */ registerSubscriber(subscriber) { if (this.#subscribers.has(subscriber)) { return; } this.#subscribers.add(subscriber); this.#entities.set(subscriber, this.getSubscribedEntities(subscriber)); this.#cache.clear(); Utils.keys(EventType) .filter(event => event in subscriber) .forEach(event => { this.#listeners[event] ??= new Set(); this.#listeners[event].add(subscriber); }); } /** Returns the set of all registered event subscribers. */ getSubscribers() { return this.#subscribers; } dispatchEvent(event, args, meta) { const listeners = []; const entity = args.entity; // execute lifecycle hooks first meta ??= entity?.__meta; const hooks = (meta?.hooks[event] || []); listeners.push(...hooks.map(hook => { const prototypeHook = meta?.prototype[hook]; const handler = typeof hook === 'function' ? hook : (entity[hook] ?? prototypeHook); return handler.bind(entity); })); for (const listener of this.#listeners[event] ?? new Set()) { const entities = this.#entities.get(listener); if (entities.size === 0 || !entity || entities.has(entity.constructor.name)) { listeners.push(listener[event].bind(listener)); } } if (event === EventType.onInit) { for (const listener of listeners) { void listener(args); } return; } return Utils.runSerial(listeners, listener => listener(args)); } /** Checks whether there are any listeners (hooks or subscribers) for the given event type and entity. */ hasListeners(event, meta) { const cacheKey = meta._id + EventTypeMap[event]; if (this.#cache.has(cacheKey)) { return this.#cache.get(cacheKey); } const hasHooks = meta.hooks[event]?.length; if (hasHooks) { this.#cache.set(cacheKey, true); return true; } for (const listener of this.#listeners[event] ?? new Set()) { const entities = this.#entities.get(listener); if (entities.size === 0 || entities.has(meta.className)) { this.#cache.set(cacheKey, true); return true; } } this.#cache.set(cacheKey, false); return false; } /** Creates a new EventManager with the same set of subscribers. */ clone() { return new EventManager(this.#subscribers); } getSubscribedEntities(listener) { if (!listener.getSubscribedEntities) { return new Set(); } return new Set(listener.getSubscribedEntities().map(name => Utils.className(name))); } }