import type { EventsConstraint, SubscriberEntry } from "./common.ts" import type { InternalProxySubscriberEntryMap } from "./internal.ts" /** * @description 表示 ClassEventProxy 用于接入目标实例及其事件系统的适配器。 */ export interface ClassEventProxyTargetAdapter> { emit(target: Target, event: K, ...args: Parameters): boolean subscribe(target: Target, event: K, subscriber: Events[K]): void unsubscribe(target: Target, event: K, subscriber: Events[K]): void } /** * @description 表示 ClassEventProxy 的构造选项。 */ export interface ClassEventProxyOptions> { targetAdapter: ClassEventProxyTargetAdapter /** * @description 在代理管理的订阅者执行出错时接收错误与对应订阅项。 */ onSubscriberError?: ( subscriberEntry: SubscriberEntry, error: unknown, ) => void } /** * @description 将一组目标实例的事件系统适配为统一的代理订阅模型。 */ export class ClassEventProxy> { protected options: ClassEventProxyOptions protected subscribers: Map> constructor(options: ClassEventProxyOptions) { this.options = options this.subscribers = new Map() } /** * @description 检查给定目标和事件上是否已经管理指定订阅者。 */ has(target: Target, event: K, subscriber: Events[K]): boolean { const proxySubscriberEntryMap = this.subscribers.get(target) if (proxySubscriberEntryMap === undefined) { return false } const proxySubscriberEntry = proxySubscriberEntryMap[event] if (proxySubscriberEntry === undefined) { return false } const has = proxySubscriberEntry.managedSubscribers.has(subscriber) return has } protected internalSubscribe( target: Target, event: K, subscriber: Events[K], once: boolean, ): SubscriberEntry { const proxySubscriberEntryMap = this.subscribers.get(target) ?? ({} as InternalProxySubscriberEntryMap) this.subscribers.set(target, proxySubscriberEntryMap) let proxySubscriberEntry = proxySubscriberEntryMap[event] if (proxySubscriberEntry === undefined) { const managedSubscribers = new Map>() const proxySubscriber = ((...args: Parameters) => { const managedSubscriberEntries = [...managedSubscribers.values()] for (const managedSubscriberEntry of managedSubscriberEntries) { try { managedSubscriberEntry.subscriber(...args) } catch (exception) { if (this.options.onSubscriberError !== undefined) { this.options.onSubscriberError(managedSubscriberEntry, exception) } else { console.error( `Error occurred while emitting event "${String(event)}" of target:`, exception, ) } } finally { if (managedSubscriberEntry.once === true) { managedSubscriberEntry.unsubscribe() } } } }) as Events[K] proxySubscriberEntry = { proxySubscriber, managedSubscribers, } proxySubscriberEntryMap[event] = proxySubscriberEntry this.options.targetAdapter.subscribe(target, event, proxySubscriber) } const existingSubscriberEntry = proxySubscriberEntry.managedSubscribers.get(subscriber) if (existingSubscriberEntry !== undefined) { return existingSubscriberEntry } const unsubscribe = (): void => { this.unsubscribe(target, event, subscriber) } const newSubscriberEntry: SubscriberEntry = { event, once, subscriber, unsubscribe, } proxySubscriberEntry.managedSubscribers.set(subscriber, newSubscriberEntry) return newSubscriberEntry } /** * @description 为指定目标事件添加常规订阅者,并在首次接入时创建底层代理订阅。 */ subscribe( target: Target, event: K, subscriber: Events[K], ): SubscriberEntry { return this.internalSubscribe(target, event, subscriber, false) } /** * @description 为指定目标事件添加只触发一次的订阅者。 */ subscribeOnce( target: Target, event: K, listener: Events[K], ): SubscriberEntry { return this.internalSubscribe(target, event, listener, true) } /** * @description 移除指定目标事件上的给定订阅者。 */ unsubscribe(target: Target, event: K, listener: Events[K]): this { const proxySubscriberEntryMap = this.subscribers.get(target) if (proxySubscriberEntryMap === undefined) { return this } const proxySubscriberEntry = proxySubscriberEntryMap[event] if (proxySubscriberEntry === undefined) { return this } proxySubscriberEntry.managedSubscribers.delete(listener) if (proxySubscriberEntry.managedSubscribers.size === 0) { this.options.targetAdapter.unsubscribe(target, event, proxySubscriberEntry.proxySubscriber) delete proxySubscriberEntryMap[event] if (Object.keys(proxySubscriberEntryMap).length === 0) { this.subscribers.delete(target) } } return this } /** * @description 移除指定目标某个事件上的全部代理订阅者。 */ removeSubscribersOfEvent(target: Target, event: K): this { const proxySubscriberEntryMap = this.subscribers.get(target) if (proxySubscriberEntryMap === undefined) { return this } const proxySubscriberEntry = proxySubscriberEntryMap[event] if (proxySubscriberEntry === undefined) { return this } proxySubscriberEntry.managedSubscribers.clear() this.options.targetAdapter.unsubscribe(target, event, proxySubscriberEntry.proxySubscriber) delete proxySubscriberEntryMap[event] if (Object.keys(proxySubscriberEntryMap).length === 0) { this.subscribers.delete(target) } return this } /** * @description 移除指定目标上的全部代理订阅者。 */ removeSubscribersOfTarget(target: Target): this { const proxySubscriberEntryMap = this.subscribers.get(target) if (proxySubscriberEntryMap === undefined) { return this } const events = Object.keys(proxySubscriberEntryMap) for (const event of events) { this.removeSubscribersOfEvent(target, event as keyof Events) } this.subscribers.delete(target) return this } /** * @description 移除当前代理管理的全部目标订阅关系。 */ removeAllSubscribers(): this { const targets = Array.from(this.subscribers.keys()) for (const target of targets) { this.removeSubscribersOfTarget(target) } this.subscribers.clear() return this } /** * @description 通过底层适配器向指定目标派发事件。 */ emit(target: Target, event: K, ...args: Parameters): boolean { return this.options.targetAdapter.emit(target, event, ...args) } }