import { SearchAdapter } from '@empathyco/x-adapter' ; import { deepMerge } from '@empathyco/x-deep-merge' ; import { PluginObject , VueConstructor } from 'vue' ; import Vuex , { Module , Store } from 'vuex' ; import { FILTERS_REGISTRY } from '../filters/filters.registry' ; import { AnyXStoreModule , RootXStoreState } from '../store/store.types' ; import { cleanGettersProxyCache } from '../store/utils/getters-proxy.utils' ; import { RootXStoreModule } from '../store/x.module' ; import { Dictionary , forEach } from '../utils' ; import { AnyWire } from '../wiring/wiring.types' ; import { AnyXModule , XModuleName } from '../x-modules/x-modules.types' ; import { bus } from './x-bus' ; import { XBus } from './x-bus.types' ; import { registerStoreEmitters } from './x-emitters' ; import { createXComponentAPIMixin } from './x-plugin.mixin' ; import { AnyXStoreModuleOption , XModuleOptions , XPluginOptions } from './x-plugin.types' ; import { assertXPluginOptionsAreValid } from './x-plugin.utils' ; public export class XPlugin implements PluginObject < XPluginOptions > { /**
     *  {@link @empathyco/x-adapter#SearchAdapter | SearchAdapter} Is the middleware between
     * the components and our API where data can be mapped to client needs.
     * This property is only available after installing the plugin.
     *
     *  returns throws public public static get adapter ( ) : SearchAdapter { return this . getInstance ( ) . adapter ; } /**
     * Exposed  XBus , so any kind of application can subscribe to  XEventsTypes * without having to pass through a component.
     * This property is only available after installing the plugin.
     *
     *  returns throws public public static get bus ( ) : XBus { return this . getInstance ( ) . bus ; } /**
     *  https Is the place where all shared data
     * is saved.
     *
     *  returns throws public public static get store ( ) : Store < RootXStoreState > { return this . getInstance ( ) . store ; } returns throws internal protected static getInstance ( ) { if ( ! this . instance ) { throw Error ( "XPlugin must be installed before accessing it's API." ) ; } return this . instance ; } internal protected static pendingXModules : Partial < Record < XModuleName , AnyXModule > > = { } ; internal protected static instance ? : XPlugin ; internal protected bus : XBus ; internal protected adapter ! : SearchAdapter ; /**
     * Set of the already installed  XModule to avoid re-registering them.
     *
     *  internal protected installedXModules = new Set < string > ( ) ; /**
     * True if the plugin has been installed in a Vue instance, in this case
     *  XModule will be installed immediately. False otherwise, in this case
     *  XModule will be installed lazily when the  XPlugin method
     * is called.
     *
     *  internal protected isInstalled = false ; /**
     * The install options of the plugin, where all the customization of
     *  XModule is done.
     *
     *  internal protected options ! : XPluginOptions ; internal protected store ! : Store < any > ; /**
     * The global Vue, passed by the install method. Used to apply the global mixin
     *  createXComponentAPIMixin , and install the  https * plugin.
     *
     *  internal protected vue ! : VueConstructor ; param bus - The  XBus implementation to use for the plugin.
     *
     *  public public constructor ( bus ) { this . bus = bus ; } /**
     * If the plugin has already been installed, it immediately registers a  XModule . If it
     * has not been installed yet, it stores the module in a list until the plugin is installed.
     *
     *  param xModule public static registerXModule ( xModule ) { if ( this . instance ) { this . instance . registerXModule ( xModule ) ; } else { this . lazyRegisterXModule ( xModule ) ; } } remarks internal static resetInstance ( ) { cleanGettersProxyCache ( ) ; this . instance = undefined ; } /**
     * Stores the  XModule in a dictionary, so it can be registered later in the install
     * process.
     *
     *  param xModule internal protected static lazyRegisterXModule ( xModule ) { this . pendingXModules [ xModule . name ] = xModule ; } param vue param options throws internal install ( vue , options ? ) { if ( this . isInstalled ) { throw new Error ( 'XPlugin has already been installed' ) ; } assertXPluginOptionsAreValid ( options ) ; XPlugin . instance = this ; this . vue = vue ; this . options = options ; this . adapter = options . adapter ; this . createAdapterConfigChangedListener ( ) ; this . registerStore ( ) ; this . applyMixins ( ) ; this . registerFilters ( ) ; this . registerInitialModules ( ) ; this . registerPendingXModules ( ) ; this . isInstalled = true ; } /**
     * Performs the registration of a  XModule .
     *
     *  param xModule internal protected registerXModule ( xModule ) { if ( ! this . installedXModules . has ( xModule . name ) ) { const customizedXModule = this . customizeXModule ( xModule ) ; this . registerStoreModule ( customizedXModule ) ; this . registerWiring ( customizedXModule ) ; this . registerStoreEmitters ( customizedXModule ) ; this . installedXModules . add ( xModule . name ) ; } } /**
     * Performs a customization of a  XModule using the XPlugin public and private options.
     *
     *  param xModule returns internal protected customizeXModule ( { name , wiring , storeModule , storeEmitters , ... restXModule } ) { const { wiring : wiringOptions , config } = this . options . xModules ?. [ name ] ?? { } ; const { storeModule : storeModuleOptions , storeEmitters : emittersOptions } = this . options . __PRIVATE__xModules ?. [ name ] ?? { } ; return { name , wiring : wiringOptions ? deepMerge ( { } , wiring , wiringOptions ) : wiring , storeModule : this . customizeStoreModule ( storeModule , storeModuleOptions ?? { } , config ) , storeEmitters : emittersOptions ? deepMerge ( { } , storeEmitters , emittersOptions ) : storeEmitters , ... restXModule } ; } param xModule - The  XModule to register its wiring.
     *
     *  internal protected registerWiring ( { wiring } ) { forEach ( wiring , ( event , wires ) => { const observable = this . bus . on ( event , true ) ; forEach ( wires , ( _ , wire ) => { wire ( observable , this . store , this . bus . on . bind ( this . bus ) ) ; } ) ; } ) ; } /**
     * Registers a  https store module under the 'x' module.
     *
     *  param xModule - The  XModule to register its Store Module.
     *
     *  internal protected registerStoreModule ( { name , storeModule } ) { ( storeModule as Module < any , any > ) . namespaced = true ; this . store . registerModule ( [ 'x' , name ] , storeModule ) ; } /**
     * Overrides a  https store module definition.
     *
     * Priority of configuration merging.
     * 1st  XPluginOptions . xModules .
     * 2nd  XPluginOptions . __PRIVATE__xModules .
     * 3rd  XStoreModule . state .
     *
     *  param defaultModule param moduleOptions param configOptions returns The  XStoreModule customized.
     *
     *  internal protected customizeStoreModule ( { state : defaultState , ... actionsGettersMutations } , { state : xModuleState , ... newActionsGettersMutations } , configOptions ) { const configOptionsObject = configOptions ? { config : configOptions } : { } ; const customizedModule = deepMerge ( { } , actionsGettersMutations , newActionsGettersMutations ) ; customizedModule . state = deepMerge ( defaultState ( ) , xModuleState , configOptionsObject ) ; return customizedModule ; } param xModule - The  XModule to register its Store Emitters.
     *
     *  internal protected registerStoreEmitters ( xModule ) { registerStoreEmitters ( xModule , this . bus , this . store ) ; } /**
     * Registers the  https store. If the store has not been passed
     * through the  XPluginOptions object, it creates one, and injects it in the Vue
     * prototype. Then it register an x module in the store, to safe scope all the
     *  XModule dynamically installed.
     *
     *  internal protected registerStore ( ) { this . vue . use ( Vuex ) ; this . store = this . options . store ?? new Store ( { strict : process . env . NODE_ENV !== 'production' } ) ; if ( ! this . options . store ) { this . vue . prototype . $store = this . store ; } this . store . registerModule ( 'x' , RootXStoreModule ) ; } /**
     * Applies the  createXComponentAPIMixin mixin in the global Vue.
     *
     *  internal protected applyMixins ( ) { this . vue . mixin ( createXComponentAPIMixin ( this . bus ) ) ; } /**
     * Registers the initial  XModule during the  XPlugin installation.
     *
     *  internal protected registerInitialModules ( ) { this . options . initialXModules ?. forEach ( xModule => { this . registerXModule ( xModule ) ; } ) ; } /**
     * Registers the pending  XModule , that requested to be registered before the
     * installation of the plugin.
     *
     *  internal protected registerPendingXModules ( ) { forEach ( XPlugin . pendingXModules , ( _ , xModule ) => { this . registerXModule ( xModule ) ; } ) ; XPlugin . pendingXModules = { } ; } /**
     * If the received adapter supports it, it registers a listener to emit the
     *  XEventsTypes . AdapterConfigChanged event whenever the config of it changes.
     *
     *  internal protected createAdapterConfigChangedListener ( ) { this . options . adapter . addConfigChangedListener ?. ( newAdapterConfig => { this . bus . emit ( 'AdapterConfigChanged' , newAdapterConfig ) ; } ) ; } internal protected registerFilters ( ) { forEach ( FILTERS_REGISTRY , ( filterName , filterFunction ) => this . vue . filter ( filterName , filterFunction ) ) ; } } /**
 * Vue plugin that modifies each component instance, extending them with the
 *  XComponentAPI .
 *
 *  example example If you are using  https in your project you must install its
 *   plugin, and instantiate an store before installing the XPlugin:
 * ```typescript
 * Vue.use(Vuex);
 * const store = new Store({ ... });
 * Vue.use(xPlugin, { adapter, store });
 * ```
 *  public export const xPlugin = new XPlugin ( bus ) ;