import type {StoreDefinition} from 'reflux'; import {createStore} from 'reflux'; import type {HookName, Hooks} from 'sentry/types/hooks'; interface Internals { // XXX(epurkhiser): We could type this as {[H in HookName]?: // Array}, however this causes typescript to produce a complex // union that it complains is 'too complex' hooks: any; } // TODO: Make generic and match against a map of allowed callbacks if we expand this pattern. type HookCallback = (...args: any[]) => void; interface HookStoreDefinition extends StoreDefinition, Internals { add(hookName: H, callback: Hooks[H]): void; get(hookName: H): Array; getCallback(hookName: H, key: string): HookCallback | undefined; init(): void; persistCallback( hookName: H, key: string, value: HookCallback ): void; remove(hookName: H, callback: Hooks[H]): void; } const storeConfig: HookStoreDefinition = { hooks: {}, hookCallbacks: {}, init() { // XXX: Do not use `this.listenTo` in this store. We avoid usage of reflux // listeners due to their leaky nature in tests. this.hooks = {}; this.hookCallbacks = {}; // For persisting hook pure functions / useX react hooks remotely. }, add(hookName, callback) { if (this.hooks[hookName] === undefined) { this.hooks[hookName] = []; } this.hooks[hookName].push(callback); this.trigger(hookName, this.hooks[hookName]); }, remove(hookName, callback) { if (this.hooks[hookName] === undefined) { return; } this.hooks[hookName] = this.hooks[hookName]!.filter(cb => cb !== callback); this.trigger(hookName, this.hooks[hookName]); }, get(hookName) { return this.hooks[hookName]! || []; }, persistCallback(hookName, key, value) { if (this.hookCallbacks[hookName] === undefined) { this.hookCallbacks[hookName] = {}; } if (this.hookCallbacks[hookName][key] !== value) { this.hookCallbacks[hookName][key] = value; } }, getCallback(hookName, key) { return this.hookCallbacks[hookName]?.[key]; }, }; /** * HookStore is used to allow extensibility into Sentry's frontend via * registration of 'hook functions'. * * This functionality is primarily used by the SASS sentry.io product. */ const HookStore = createStore(storeConfig); export default HookStore;