export interface AST { /** Current object key */ key : string ; /** Current depth */ depth : number ; /** The parent node of the current one */ parent : AST | undefined ; } interface ASTChildren { /** Children */ children : DeferredNode [ ] ; /** Whether it's an Object prototype */ isPrototype ? : true ; } /** A type to describe objects with all deferred children loaded */ interface ASTResolvedChildren { /** Children */ children : ASTNode [ ] ; /** Whether it's an Object prototype */ isPrototype ? : true ; } export type DeferredNode = ( ) => Promise < ASTNode > ; export interface ASTObject extends AST , ASTChildren { /** Type */ type : "object" ; /** Value */ value : object ; } export interface ResolvedASTObject extends AST , ASTResolvedChildren { /** Type */ type : "object" ; /** Value */ value : object ; } export interface ASTArray extends AST , ASTChildren { /** Type */ type : "array" ; /** Value */ value : any [ ] ; } export interface ResolvedASTArray extends AST , ASTResolvedChildren { /** Type */ type : "array" ; /** Value */ value : any [ ] ; } export interface ASTFunction extends AST , ASTChildren { /** Type */ type : "function" ; /** Value */ value : Function ; } export interface ResolvedASTFunction extends AST , ASTResolvedChildren { /** Type */ type : "function" ; /** Value */ value : Function ; } export type PromiseState = "pending" | "fulfilled" | "rejected" ; export interface ASTPromise extends AST , ASTChildren { /** Type */ type : "promise" ; /** Value */ value : Promise < any > ; } export interface ResolvedASTPromise extends AST , ASTResolvedChildren { /** Type */ type : "promise" ; /** Value */ value : Promise < any > ; } export interface ASTMap extends AST , ASTChildren { /** Type */ type : "map" ; /** Value */ value : Map < any , any > ; } export interface ResolvedASTMap extends AST , ASTResolvedChildren { /** Type */ type : "map" ; /** Value */ value : Map < any , any > ; } export interface ASTSet extends AST , ASTChildren { /** Type */ type : "set" ; /** Value */ value : Set < any > ; } export interface ResolvedASTSet extends AST , ASTResolvedChildren { /** Type */ type : "set" ; /** Value */ value : Set < any > ; } export interface ASTValue extends AST { /** Type */ type : "value" ; /** Value */ value : boolean | null | number | BigInt | string | symbol | undefined | Date | RegExp | Error | WeakMap < any , any > | WeakSet < any > | Promise < any > ; /** It's not a prototype */ isPrototype ? : false ; } export type SupportedTypes = boolean | null | number | string | Error | symbol | undefined | Date | RegExp | object | Map < any , any > | WeakMap < any , any > | Set < any > | WeakSet < any > | Promise < any > | any [ ] | Function ; export type ObjectTypes = "object" | "function" | "array" | "promise" | "map" | "set" ; export type ASTNode = ASTObject | ASTArray | ASTFunction | ASTPromise | ASTMap | ASTSet | ASTValue ; export type ResolvedASTNode = ResolvedASTObject | ResolvedASTArray | ResolvedASTFunction | ResolvedASTPromise | ResolvedASTMap | ResolvedASTSet ; /**
 * Determine if the current object is an array.
 */ const isArray = ( val ) : boolean => { return ( Array . isArray ( val ) || ( ArrayBuffer . isView ( val ) && ! ( val instanceof DataView ) ) ) ; } ; param val export const isObject = ( val ) : boolean => { return ( val !== null && typeof val === "object" && ! isArray ( val ) && ! ( val instanceof Date ) && ! ( val instanceof RegExp ) && ! ( val instanceof Error ) && ! ( val instanceof WeakMap ) && ! ( val instanceof WeakSet ) ) ; } ; /** Check for objects we know how to enumerate */ export const isKnownObject = ( val ) : boolean => { return ( isObject ( val ) || isArray ( val ) || typeof val === "function" || val instanceof Promise ) ; } ; param promise export const getPromiseState = ( promise ) : Promise < [ "pending" ] | [ "rejected" , any ] | [ "fulfilled" , any ] > => { const unique = /unique/ ; return Promise . race ( [ promise , unique ] ) . then ( ( result ) => ( result === unique ? [ "pending" ] : [ "fulfilled" , result ] ) , ( e ) => [ "rejected" , e ] ) ; } ; param key param value param depth param sortKeys const buildAST = async ( key , value , depth , sortKeys , isPrototype ? , showPrototype ? ) : Promise < ASTNode > => { const astNode = { key , depth , value , type : "value" , parent : undefined , } ; if ( value && isKnownObject ( value ) && depth < 100 ) { const children = [ ] ; let t = "object" ; if ( isArray ( value ) ) { for ( let i = 0 ; i < value . length ; i ++ ) { children . push ( async ( ) => { const child = await buildAST ( i . toString ( ) , value [ i ] , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; } t = "array" ; } else { const keys = Object . getOwnPropertyNames ( value ) ; if ( sortKeys ) keys . sort ( ) ; for ( let i = 0 ; i < keys . length ; i ++ ) { let safeValue ; try { safeValue = value [ keys [ i ] ] ; } catch ( e ) { } children . push ( async ( ) => { const child = await buildAST ( keys [ i ] , safeValue , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; } if ( typeof value === "function" ) { t = "function" ; } if ( value instanceof Promise ) { const [ status , result ] = await getPromiseState ( value ) ; children . push ( async ( ) => { const child = await buildAST ( "<state>" , status , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; if ( status !== "pending" ) { children . push ( async ( ) => { const child = await buildAST ( "<value>" , result , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; } t = "promise" ; } if ( value instanceof Map ) { const entries = Array . from ( value . entries ( ) ) ; const parsedEntries = entries . map ( ( entry ) => { const [ entryKey , entryValue ] = entry ; return { "<key>" : entryKey , "<value>" : entryValue , } ; } ) ; children . push ( async ( ) => { const child = await buildAST ( "<entries>" , parsedEntries , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; children . push ( async ( ) => { const child = await buildAST ( "size" , value . size , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; t = "map" ; } if ( value instanceof Set ) { const entries = Array . from ( value . entries ( ) ) ; const parsedEntries = entries . map ( ( entry ) => { return entry [ 1 ] ; } ) ; children . push ( async ( ) => { const child = await buildAST ( "<entries>" , parsedEntries , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; children . push ( async ( ) => { const child = await buildAST ( "size" , value . size , depth + 1 , sortKeys ) ; child . parent = astNode ; return child ; } ) ; t = "set" ; } } if ( value !== Object . prototype && showPrototype ) { children . push ( async ( ) => { const child = await buildAST ( "<prototype>" , Object . getPrototypeOf ( value ) , depth + 1 , sortKeys , true ) ; child . parent = astNode ; return child ; } ) ; } astNode . type = t ; ( ( astNode as any ) as ASTChildren ) . children = children ; ( ( astNode as any ) as ASTChildren ) . isPrototype = isPrototype ; } return astNode as ASTNode ; } ; param data export const parse = ( data , sortKeys ? , includePrototypes ? ) => { const keys = sortKeys === false ? sortKeys : true ; const prototypes = includePrototypes === false ? includePrototypes : true ; return buildAST ( "root" , data , 0 , keys , undefined , prototypes ) ; } ; export default parse ;