import { useContext , useEffect , useRef } from 'react' ; import { observable , makeAutoObservable , configure , reaction , when as _when , computed as _computed } from 'mobx' ; import { enableStaticRendering , MobXProviderContext , useLocalObservable } from 'mobx-react' ; /**
 * Enable SSR if it is running on the server.
 */ enableStaticRendering ( typeof window === 'undefined' ) ; /**
 * Allow use of loose action.
 */ configure ( { enforceActions : 'never' } ) ; /**
 * Collects instances of the store instances of the store.
 */ let storeInstancesForCSR = null ; export type UniversalBindType = 'normal' | 'noWrap' | 'unset' ; /**
 * Wrap the Observer box storage class instance.
 */ const convertToObservableBox = < T extends { new ( ) : InstanceType < T > ; } > ( StoreClass ) => { const instance = new StoreClass ( ) ; const overrideOptions = { } ; const instanceKeys = Object . keys ( instance ) ; let bindOption = 'normal' as UniversalBindType ; if ( typeof instance [ '__bindOption' ] === 'string' ) { bindOption = instance [ '__bindOption' ] as UniversalBindType ; } if ( bindOption === 'normal' ) { const ignoreVariables = instanceKeys . filter ( ( value ) => { return value . startsWith ( '__' ) || value === 'store' ; } ) ; for ( const ignoreVariable of ignoreVariables ) overrideOptions [ ignoreVariable ] = false ; } else if ( bindOption === 'noWrap' ) { for ( const instanceKey of instanceKeys ) overrideOptions [ instanceKey ] = false ; } makeAutoObservable ( instance , overrideOptions ) ; const observableWrap = observable . box < T > ( ) ; observableWrap . set ( instance as T ) ; return observableWrap . get ( ) as InstanceType < T > ; } ; export type StoreType < T extends { [ storeName in string ] : { new ( ) : InstanceType < T [ storeName ] > ; } ; } > = { [ storeName in keyof T ] : InstanceType < T [ storeName ] > ; } ; /**
 * Receive class instances of the stores
 * and wrap them in the Mobx Observer box.
 */ const defineStores = < T extends { [ storeName in string ] : { new ( ) : InstanceType < T [ storeName ] > ; } ; } > ( stores ) : { [ storeName in keyof T ] : InstanceType < T [ storeName ] > ; } => { const bindedStores = { } as StoreType < T > ; for ( const storeName in stores ) { const bindedStore = convertToObservableBox ( stores [ storeName ] ) ; bindedStores [ storeName ] = bindedStore ; } for ( const bindedStore of Object . values ( bindedStores ) ) bindedStore . store = bindedStores ; return bindedStores ; } ; /**
 * Provides a state store within the context of a react.
 */ export const StoreProvider = ( context ) => { const { children , ssr , structure } = context ; if ( process . browser ) { if ( storeInstancesForCSR === null ) { storeInstancesForCSR = defineStores ( structure ) ; if ( typeof ssr === 'object' ) { if ( ssr !== undefined ) { for ( const storeName in storeInstancesForCSR ) { if ( typeof ssr [ storeName ] !== 'undefined' ) { for ( const propertyName in storeInstancesForCSR [ storeName ] ) { if ( typeof ssr [ storeName ] [ propertyName ] !== 'undefined' ) { if ( propertyName !== 'store' && ssr [ storeName ] [ propertyName ] && typeof ssr [ storeName ] [ propertyName ] === 'object' && Object . keys ( ssr [ storeName ] [ propertyName ] ) . length === 0 ) continue ; storeInstancesForCSR [ storeName ] [ propertyName ] = ssr [ storeName ] [ propertyName ] ; } } } } } } } for ( const bindedStoreName of Object . keys ( storeInstancesForCSR ) ) storeInstancesForCSR [ bindedStoreName ] . store = storeInstancesForCSR ; return ( < MobXProviderContext . Provider value = { storeInstancesForCSR } > { children } < /MobXProviderContext.Provider>); } else { const stores = useRef ( null as any ) ; if ( stores . current === null ) stores . current = defineStores ( structure ) ; if ( typeof ssr === 'object' ) { for ( const storeName in stores . current ) { if ( typeof ssr [ storeName ] !== 'undefined' ) { for ( const propertyName in stores . current [ storeName ] ) { if ( typeof ssr [ storeName ] [ propertyName ] !== 'undefined' ) { if ( propertyName !== 'store' && ssr [ storeName ] [ propertyName ] && typeof ssr [ storeName ] [ propertyName ] === 'object' && Object . keys ( ssr [ storeName ] [ propertyName ] ) . length === 0 ) continue ; stores . current [ storeName ] [ propertyName ] = ssr [ storeName ] [ propertyName ] ; } } } } } return ( < MobXProviderContext . Provider value = { stores . current } > { children } < /MobXProviderContext.Provider>); } } ; /**
 * Type information about the converted state stores.
 */ type IStore < T extends { [ storeName in string ] : { new ( ) : InstanceType < T [ storeName ] > ; } ; } > = { [ storeName in keyof T ] : InstanceType < T [ storeName ] > ; } ; /**
 * Returns a state store available within a component.
 */ export const useStore = < T extends { [ storeName in string ] : { new ( ) : InstanceType < T [ storeName ] > ; } ; } > ( ) : IStore < T > => { if ( process . browser ) return storeInstancesForCSR ; const store = useContext ( MobXProviderContext ) ; if ( ! store ) throw new Error ( `Invalid context access. useStore cannot be called from the server.\n` + `If you are using getInitialProps and onPrefetch, Use context.store to access.` ) ; return store as IStore < T > ; } ; /**
 * Create simple local state.
 */ export const state = < T , _ > ( data ) => { try { return useLocalObservable ( ( ) => observable ( data ) ) ; } catch ( error ) { return observable ( data ) ; } } ; /**
 * Create simple local computed value.
 */ export const computed = < T , _ > ( data ) => { try { return useLocalObservable ( ( ) => _computed ( data ) ) . get ( ) ; } catch ( error ) { return _computed ( data ) . get ( ) ; } } ; const _watch = < T , B > ( source , callback , options = { immediate : false } ) => { let unsubscribe ; if ( options && options . immediate ) unsubscribe = callback ( source ( ) ) ; const cleanupReaction = reaction < T > ( source , ( args ) => { if ( typeof unsubscribe === 'function' ) unsubscribe ( ) ; unsubscribe = callback ( args ) ; } ) ; return ( ) => cleanupReaction ( ) ; } ; description example param source param callback param options export const watch = < T , B > ( source , callback , options = { immediate : false } ) => { try { const store = useContext ( MobXProviderContext ) ; if ( ! store ) throw new Error ( 'Not found StoreProvider' ) ; useEffect ( ( ) => { return _watch ( source , callback , options ) ; } , [ ] ) ; } catch ( e ) { _watch ( source , callback , options ) ; } } ; description example export const when = _when ; /**
 * Global Prefetchers
 */ const _globalPrefetchers = [ ] ; export const onPrefetch = < T , _ = void > ( callback , component ? ) => { if ( component ) component . onPrefetch = callback ; else _globalPrefetchers . push ( callback ) ; } ; /**
 * Global Server Prefetchers
 */ const _globalServerPrefetchers = [ ] ; export const onServerPrefetch = < T , _ = void > ( callback , component ? ) => { if ( component ) component . onServerPrefetch = callback ; else _globalServerPrefetchers . push ( callback ) ; } ; /**
 * Global Client Prefetchers
 */ const _globalClientPrefetchers = [ ] ; /**
 * This function runs only once before rendering occurs on the client.
 */ export const onClientPrefetch = ( callback ) => { _globalClientPrefetchers . push ( callback ) ; } ; /**
 * Function for performing SSR from next to store.
 */ export const useSSR = async < ContextType extends { Component ; ctx ; } , IStores extends { [ storeName in string ] : { new ( ) : InstanceType < IStores [ storeName ] > ; } ; } > ( appContext , Stores ) => { /**
     * Create Server Side Rendered stores data.
     */ const stores = process . browser ? storeInstancesForCSR : defineStores ( Stores ) ; appContext . ctx . store = stores ; if ( typeof window === 'undefined' ) { for ( const globalServerPrefetcher of _globalServerPrefetchers ) await globalServerPrefetcher ( appContext ) ; const pageServerPrefetcher = ( appContext . Component as any ) . onServerPrefetch ; if ( typeof pageServerPrefetcher !== 'undefined' ) await pageServerPrefetcher ( appContext ) ; } for ( const globalPrefetcher of _globalPrefetchers ) await globalPrefetcher ( appContext ) ; const pagePrefetcher = ( appContext . Component as any ) . onPrefetch ; if ( typeof pagePrefetcher !== 'undefined' ) await pagePrefetcher ( appContext ) ; return stores ; } ; param stateJSON export const makeStateJSON = ( stateJSON ) => { for ( const storeName in stateJSON ) { for ( const propertyName in stateJSON [ storeName ] ) { if ( propertyName === 'store' ) delete stateJSON [ storeName ] [ propertyName ] ; else if ( typeof stateJSON [ storeName ] [ propertyName ] === 'function' ) delete stateJSON [ storeName ] [ propertyName ] ; } } return stateJSON ; } ; export const cloneStore = < T , _ = void > ( store ) => { const clonedStore = { } as any ; for ( const propertyName of Object . keys ( store ) ) { if ( propertyName === 'store' ) continue ; clonedStore [ propertyName ] = store [ propertyName ] ; } return JSON . parse ( JSON . stringify ( clonedStore ) ) as T ; } ;