import { isComputedProp , isObservableProp } from "mobx" ; import { IAnyStateTreeNode , IType , IAnyModelType , getStateTreeNode , IStateTreeNode , isStateTreeNode , IJsonPatch , splitJsonPath , asArray , EMPTY_OBJECT , fail , IDisposer , resolveNodeByPath , getRelativePathBetweenNodes , freeze , IAnyType , isModelType , InvalidReferenceError , normalizeIdentifier , ReferenceIdentifier , AnyObjectNode , assertIsType , assertIsStateTreeNode , TypeOfValue , assertIsFunction , assertIsNumber , assertIsString , assertArg , assertIsValidIdentifier , IActionContext , getRunningActionContext , IAnyComplexType } from "../internal" ; hidden export type TypeOrStateTreeNodeToStateTreeNode < T extends IAnyType | IAnyStateTreeNode > = T extends IType < any , any , infer TT > ? TT & IStateTreeNode < T > : T ; param object returns export function getType ( object ) { assertIsStateTreeNode ( object , 1 ) ; return getStateTreeNode ( object ) . type ; } param object param propertyName returns export function getChildType ( object , propertyName ? ) { assertIsStateTreeNode ( object , 1 ) ; return getStateTreeNode ( object ) . getChildType ( propertyName ) ; } param target param callback returns export function onPatch ( target , callback ) { assertIsStateTreeNode ( target , 1 ) ; assertIsFunction ( callback , 2 ) ; return getStateTreeNode ( target ) . onPatch ( callback ) ; } param target param callback returns export function onSnapshot < S > ( target , callback ) { assertIsStateTreeNode ( target , 1 ) ; assertIsFunction ( callback , 2 ) ; return getStateTreeNode ( target ) . onSnapshot ( callback ) ; } param target param patch returns export function applyPatch ( target , patch ) { assertIsStateTreeNode ( target , 1 ) ; assertArg ( patch , ( p ) => typeof p === "object" , "object or array" , 2 ) ; getStateTreeNode ( target ) . applyPatches ( asArray ( patch ) ) ; } export interface IPatchRecorder { patches : ReadonlyArray < IJsonPatch > ; inversePatches : ReadonlyArray < IJsonPatch > ; reversedInversePatches : ReadonlyArray < IJsonPatch > ; readonly recording : boolean ; stop ( ) : void ; resume ( ) : void ; replay ( target ? ) : void ; undo ( target ? ) : void ; } param subject param filter returns export function recordPatches ( subject , filter ? ) { assertIsStateTreeNode ( subject , 1 ) ; interface IPatches { patches : IJsonPatch [ ] ; reversedInversePatches : IJsonPatch [ ] ; inversePatches : IJsonPatch [ ] ; } const data = { patches : [ ] , reversedInversePatches : [ ] } ; const publicData = { } ; let disposer ; const recorder = { get recording ( ) { return ! ! disposer ; } , get patches ( ) { if ( ! publicData . patches ) { publicData . patches = data . patches . slice ( ) ; } return publicData . patches ; } , get reversedInversePatches ( ) { if ( ! publicData . reversedInversePatches ) { publicData . reversedInversePatches = data . reversedInversePatches . slice ( ) ; } return publicData . reversedInversePatches ; } , get inversePatches ( ) { if ( ! publicData . inversePatches ) { publicData . inversePatches = data . reversedInversePatches . slice ( ) . reverse ( ) ; } return publicData . inversePatches ; } , stop ( ) { if ( disposer ) { disposer ( ) ; disposer = undefined ; } } , resume ( ) { if ( disposer ) return ; disposer = onPatch ( subject , ( patch , inversePatch ) => { if ( filter && ! filter ( patch , inversePatch , getRunningActionContext ( ) ) ) { return ; } data . patches . push ( patch ) ; data . reversedInversePatches . unshift ( inversePatch ) ; publicData . patches = undefined ; publicData . inversePatches = undefined ; publicData . reversedInversePatches = undefined ; } ) ; } , replay ( target ? ) { applyPatch ( target || subject , data . patches ) ; } , undo ( target ? ) { applyPatch ( target || subject , data . reversedInversePatches ) ; } } ; recorder . resume ( ) ; return recorder ; } param target export function protect ( target ) { assertIsStateTreeNode ( target , 1 ) ; const node = getStateTreeNode ( target ) ; if ( ! node . isRoot ) throw fail ( "`protect` can only be invoked on root nodes" ) ; node . isProtectionEnabled = true ; } /**
 * By default it is not allowed to directly modify a model. Models can only be modified through actions.
 * However, in some cases you don't care about the advantages (like replayability, traceability, etc) this yields.
 * For example because you are building a PoC or don't have any middleware attached to your tree.
 *
 * In that case you can disable this protection by calling `unprotect` on the root of your tree.
 *
 * Example:
 * ```ts
 * const Todo = types.model({
 *     done: false
 * }).actions(self => ({
 *     toggle() {
 *         self.done = !self.done
 *     }
 * }))
 *
 * const todo = Todo.create()
 * todo.done = true // throws!
 * todo.toggle() // OK
 * unprotect(todo)
 * todo.done = false // OK
 * ```
 */ export function unprotect ( target ) { assertIsStateTreeNode ( target , 1 ) ; const node = getStateTreeNode ( target ) ; if ( ! node . isRoot ) throw fail ( "`unprotect` can only be invoked on root nodes" ) ; node . isProtectionEnabled = false ; } /**
 * Returns true if the object is in protected mode, @see protect
 */ export function isProtected ( target ) { return getStateTreeNode ( target ) . isProtected ; } param target param snapshot returns export function applySnapshot < C > ( target , snapshot ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . applySnapshot ( snapshot ) ; } param target param applyPostProcess returns export function getSnapshot < S > ( target , applyPostProcess = true ) { assertIsStateTreeNode ( target , 1 ) ; const node = getStateTreeNode ( target ) ; if ( applyPostProcess ) return node . snapshot ; return freeze ( node . type . getSnapshot ( node , false ) ) ; } param target param depth returns export function hasParent ( target , depth = 1 ) { assertIsStateTreeNode ( target , 1 ) ; assertIsNumber ( depth , 2 , 0 ) ; let parent = getStateTreeNode ( target ) . parent ; while ( parent ) { if ( -- depth === 0 ) return true ; parent = parent . parent ; } return false ; } param target param depth returns export function getParent < IT extends IAnyStateTreeNode | IAnyComplexType > ( target , depth = 1 ) { assertIsStateTreeNode ( target , 1 ) ; assertIsNumber ( depth , 2 , 0 ) ; let d = depth ; let parent = getStateTreeNode ( target ) . parent ; while ( parent ) { if ( -- d === 0 ) return parent . storedValue as any ; parent = parent . parent ; } throw fail ( `Failed to find the parent of ${ getStateTreeNode ( target ) } at depth ${ depth }` ) ; } param target param type returns export function hasParentOfType ( target , type ) { assertIsStateTreeNode ( target , 1 ) ; assertIsType ( type , 2 ) ; let parent = getStateTreeNode ( target ) . parent ; while ( parent ) { if ( type . is ( parent . storedValue ) ) return true ; parent = parent . parent ; } return false ; } param target param type returns export function getParentOfType < IT extends IAnyComplexType > ( target , type ) { assertIsStateTreeNode ( target , 1 ) ; assertIsType ( type , 2 ) ; let parent = getStateTreeNode ( target ) . parent ; while ( parent ) { if ( type . is ( parent . storedValue ) ) return parent . storedValue ; parent = parent . parent ; } throw fail ( `Failed to find the parent of ${ getStateTreeNode ( target ) } of a given type` ) ; } param target returns export function getRoot < IT extends IAnyComplexType | IAnyStateTreeNode > ( target ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . root . storedValue ; } param target returns export function getPath ( target ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . path ; } param target returns export function getPathParts ( target ) { assertIsStateTreeNode ( target , 1 ) ; return splitJsonPath ( getStateTreeNode ( target ) . path ) ; } param target returns export function isRoot ( target ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . isRoot ; } param target param path returns export function resolvePath ( target , path ) { assertIsStateTreeNode ( target , 1 ) ; assertIsString ( path , 2 ) ; const node = resolveNodeByPath ( getStateTreeNode ( target ) , path ) ; return node ? node . value : undefined ; } param type param target param identifier returns export function resolveIdentifier < IT extends IAnyModelType > ( type , target , identifier ) { assertIsType ( type , 1 ) ; assertIsStateTreeNode ( target , 2 ) ; assertIsValidIdentifier ( identifier , 3 ) ; const node = getStateTreeNode ( target ) . root . identifierCache ! . resolve ( type , normalizeIdentifier ( identifier ) ) ; return node ?. value ; } param target returns export function getIdentifier ( target ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . identifier ; } param getter param checkIfAlive returns export function tryReference < N extends IAnyStateTreeNode > ( getter , checkIfAlive = true ) { try { const node = getter ( ) ; if ( node === undefined || node === null ) { return undefined ; } else if ( isStateTreeNode ( node ) ) { if ( ! checkIfAlive ) { return node ; } else { return isAlive ( node ) ? node : undefined ; } } else { throw fail ( "The reference to be checked is not one of node, null or undefined" ) ; } } catch ( e ) { if ( e instanceof InvalidReferenceError ) { return undefined ; } throw e ; } } param getter param checkIfAlive returns export function isValidReference < N extends IAnyStateTreeNode > ( getter , checkIfAlive = true ) { try { const node = getter ( ) ; if ( node === undefined || node === null ) { return false ; } else if ( isStateTreeNode ( node ) ) { return checkIfAlive ? isAlive ( node ) : true ; } else { throw fail ( "The reference to be checked is not one of node, null or undefined" ) ; } } catch ( e ) { if ( e instanceof InvalidReferenceError ) { return false ; } throw e ; } } param target param path returns export function tryResolve ( target , path ) { assertIsStateTreeNode ( target , 1 ) ; assertIsString ( path , 2 ) ; const node = resolveNodeByPath ( getStateTreeNode ( target ) , path , false ) ; if ( node === undefined ) return undefined ; try { return node . value ; } catch ( e ) { return undefined ; } } param base param target returns export function getRelativePath ( base , target ) { assertIsStateTreeNode ( base , 1 ) ; assertIsStateTreeNode ( target , 2 ) ; return getRelativePathBetweenNodes ( getStateTreeNode ( base ) , getStateTreeNode ( target ) ) ; } param source param keepEnvironment returns export function clone < T extends IAnyStateTreeNode > ( source , keepEnvironment = true ) { assertIsStateTreeNode ( source , 1 ) ; const node = getStateTreeNode ( source ) ; return node . type . create ( node . snapshot , keepEnvironment === true ? node . root . environment : keepEnvironment === false ? undefined : keepEnvironment ) ; } /**
 * Removes a model element from the state tree, and let it live on as a new state tree
 */ export function detach < T extends IAnyStateTreeNode > ( target ) { assertIsStateTreeNode ( target , 1 ) ; getStateTreeNode ( target ) . detach ( ) ; return target ; } /**
 * Removes a model element from the state tree, and mark it as end-of-life; the element should not be used anymore
 */ export function destroy ( target ) { assertIsStateTreeNode ( target , 1 ) ; const node = getStateTreeNode ( target ) ; if ( node . isRoot ) node . die ( ) ; else node . parent ! . removeChild ( node . subpath ) ; } param target returns export function isAlive ( target ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . observableIsAlive ; } param target param disposer returns export function addDisposer ( target , disposer ) { assertIsStateTreeNode ( target , 1 ) ; assertIsFunction ( disposer , 2 ) ; const node = getStateTreeNode ( target ) ; node . addDisposer ( disposer ) ; return disposer ; } param target returns export function getEnv < T = any > ( target ) { assertIsStateTreeNode ( target , 1 ) ; const node = getStateTreeNode ( target ) ; const env = node . root . environment ; if ( ! env ) return EMPTY_OBJECT as T ; return env ; } /**
 * Performs a depth first walk through a tree.
 */ export function walk ( target , processor ) { assertIsStateTreeNode ( target , 1 ) ; assertIsFunction ( processor , 2 ) ; const node = getStateTreeNode ( target ) ; node . getChildren ( ) . forEach ( ( child ) => { if ( isStateTreeNode ( child . storedValue ) ) walk ( child . storedValue , processor ) ; } ) ; processor ( node . storedValue ) ; } export interface IModelReflectionPropertiesData { name : string ; properties : { [ K ] : IAnyType ; } ; } param typeOrNode returns export function getPropertyMembers ( typeOrNode ) { let type ; if ( isStateTreeNode ( typeOrNode ) ) { type = getType ( typeOrNode ) as IAnyModelType ; } else { type = typeOrNode as IAnyModelType ; } assertArg ( type , ( t ) => isModelType ( t ) , "model type or model instance" , 1 ) ; return { name : type . name , properties : { ... type . properties } } ; } export interface IModelReflectionData extends IModelReflectionPropertiesData { actions : string [ ] ; views : string [ ] ; volatile : string [ ] ; } param target returns export function getMembers ( target ) { const type = ( getStateTreeNode ( target ) . type as unknown ) as IAnyModelType ; const reflected = { ... getPropertyMembers ( type ) , actions : [ ] , volatile : [ ] , views : [ ] } ; const props = Object . getOwnPropertyNames ( target ) ; props . forEach ( ( key ) => { if ( key in reflected . properties ) return ; const descriptor = Object . getOwnPropertyDescriptor ( target , key ) ! ; if ( descriptor . get ) { if ( isComputedProp ( target , key ) ) reflected . views . push ( key ) ; else reflected . volatile . push ( key ) ; return ; } if ( descriptor . value . _isMSTAction === true ) reflected . actions . push ( key ) ; else if ( isObservableProp ( target , key ) ) reflected . volatile . push ( key ) ; else reflected . views . push ( key ) ; } ) ; return reflected ; } export function cast < O extends string | number | boolean | null | undefined = never > ( snapshotOrInstance ) ; export function cast < O = never > ( snapshotOrInstance ) ; param snapshotOrInstance returns export function cast ( snapshotOrInstance ) { return snapshotOrInstance as any ; } param snapshotOrInstance returns export function castToSnapshot < I > ( snapshotOrInstance ) { return snapshotOrInstance as any ; } param instance returns export function castToReferenceSnapshot < I > ( instance ) { return instance as any ; } export param target returns export function getNodeId ( target ) { assertIsStateTreeNode ( target , 1 ) ; return getStateTreeNode ( target ) . nodeId ; }