src/plugin/registry.ts (77 lines of code) (raw):

import type {KeyValue, WritableKeyValue} from '../utils/object' import {objectValues} from '../utils/object' import type {PlaceId, PluginInterface, PluginRegistry as PluginRegistryInterface} from './types' type Handler = () => void declare global { interface Window { // this format should be simple and stable across library versions TEAMCITY_PLUGINS: { store: WritableKeyValue<PlaceId, Array<PluginInterface>> handlers: Array<Handler> } } } window.TEAMCITY_PLUGINS ??= { store: {}, handlers: [], } class PluginRegistry implements PluginRegistryInterface { subscribe(handler: Handler) { window.TEAMCITY_PLUGINS.handlers.push(handler) return () => this.unsubscribe(handler) } unsubscribe(handler: Handler) { window.TEAMCITY_PLUGINS.handlers = window.TEAMCITY_PLUGINS.handlers.filter(fn => handler !== fn) } getStore(): KeyValue<PlaceId, Array<PluginInterface>> { return window.TEAMCITY_PLUGINS.store } add(placeId: PlaceId, plugin: PluginInterface) { if (!window.TEAMCITY_PLUGINS.store[placeId]) { window.TEAMCITY_PLUGINS.store[placeId] = [] } const place = window.TEAMCITY_PLUGINS.store[placeId] if (Array.isArray(place)) { if (place.some(item => item.name === plugin.name)) { throw Error(`Plugin ${plugin.name} has been already registered in PlaceId ${placeId}. `) } window.TEAMCITY_PLUGINS.store[placeId] = place.concat(plugin) } window.TEAMCITY_PLUGINS.handlers.forEach(hander => hander()) } remove(placeId: PlaceId, plugin: PluginInterface) { const place = window.TEAMCITY_PLUGINS.store[placeId] if (!place?.includes(plugin)) { return } window.TEAMCITY_PLUGINS.store[placeId] = place?.filter(item => item !== plugin) window.TEAMCITY_PLUGINS.handlers.forEach(hander => hander()) } searchByPlaceId( placeId: PlaceId, pluginName?: string, ): (PluginInterface | null | undefined) | Array<PluginInterface> { const place = window.TEAMCITY_PLUGINS.store[placeId] if (place && !pluginName) { return place } return place?.find(plugin => plugin.name === pluginName) || null } findUniquePlugin(placeId: PlaceId, pluginName: string): PluginInterface | null | undefined { if (!placeId || !pluginName) { return null } const result = this.searchByPlaceId(placeId, pluginName) if (!Array.isArray(result)) { return result } else { return null } } search(pluginName: string): Array<PluginInterface> { return objectValues(window.TEAMCITY_PLUGINS.store) .flat() .filter(plugin => plugin.name === pluginName) } } const pluginRegistry: PluginRegistry = new PluginRegistry() export default pluginRegistry