import { parsePath , Ref } from './parse' ; import { isObj , isProp , isNum , isArr } from './utils' ; export interface Scope { [ key ] : Scope | any ; } export interface GetOptions { /**
     * When set to a truthy value, we throw a `ReferenceError` for invalid paths and refs.
     * - An invalid ref specifies an array of properties that does not exist in the scope.
     * - An invalid path is a string that is parsed to an invalid ref.
     *
     * When set to a falsy value, we use an empty string for paths and refs that don't exist in the
     * scope.
     *
     * If a value does not exist in the scope, two things can happen:
     * - if `validateRef` is falsy, the value will be assumed empty string
     * - if `validateRef` is truthy, a `ReferenceError` will be thrown
     */ readonly validateRef ? : boolean ; /**
     * Drilling a nested object to get the value assigned with a ref is a relatively expensive
     * computation. Therefore you can set a value of how deep you are expecting a template to go and
     * if the nesting is deeper than that, the computation stops with an error.
     * This prevents a malicious or erroneous template with deep nesting to block the JavaScript event
     * loop. The default is 10.
     */ readonly maxRefDepth ? : number ; } see https param scope param ref throws throws throws throws returns export function getRef ( scope , ref , options = { } ) { if ( ! isObj ( options ) ) { throw new TypeError ( `get expects an object option. Got ${ typeof options }` ) ; } if ( ! isArr ( ref ) ) { throw new TypeError ( `Expected an array ref. Got ${ ref }` ) ; } const { maxRefDepth = 10 } = options ; if ( ! isNum ( maxRefDepth ) || maxRefDepth <= 0 ) { throw new RangeError ( `Expected a positive number for maxRefDepth. Got ${ maxRefDepth }` ) ; } const propNamesAsStr = ( ) => ref . join ( ' > ' ) ; if ( ref . length > maxRefDepth ) { throw new ReferenceError ( `The ref cannot be deeper than ${ maxRefDepth } levels. Got "${ propNamesAsStr ( ) }"` ) ; } let currentScope = scope ; for ( const prop of ref ) { if ( isProp ( currentScope , prop ) ) { currentScope = currentScope [ prop ] ; } else if ( options . validateRef ) { throw new ReferenceError ( `${ prop } is not defined in the scope at ref: "${ propNamesAsStr ( ) }"` ) ; } else { return ; } } return currentScope ; } param scope param path throws returns export function get ( scope , path , options = { } ) { return getRef ( scope , parsePath ( path ) , options ) ; }