import util from 'util' ; import { RedspotError } from '../core/errors' ; import { ERRORS } from '../core/errors-list' ; const inspect = Symbol . for ( 'nodejs.util.inspect.custom' ) ; /**
 * This module provides function to implement proxy-based object, functions, and
 * classes (they are functions). They receive an initializer function that it's
 * not used until someone interacts with the lazy element.
 *
 * This functions can also be used like a lazy `require`, creating a proxy that
 * doesn't require the module until needed.
 *
 * The disadvantage of using this technique is that the type information is
 * lost wrt `import`, as `require` returns an `any. If done with enough care,
 * this can be manually fixed.
 *
 * TypeScript doesn't emit `require` calls for modules that are imported only
 * because of their types. So if one uses lazyObject or lazyFunction along with
 * a normal ESM import you can pass the module's type to this function.
 *
 * An example of this can be:
 *
 *    import findUpT from "find-up";
 *    export const findUp = lazyFunction<typeof findUpT>(() => require("find-up"));
 *
 * You can also use it with named exports:
 *
 *    import { EthT } from "web3x/eth";
 *    const Eth = lazyFunction<typeof EthT>(() => require("web3x/eth").Eth);
 */ export function lazyObject < T extends object > ( objectCreator ) { return createLazyProxy ( objectCreator , ( getRealTarget ) => ( { [ inspect ] ( ) { const realTarget = getRealTarget ( ) ; return util . inspect ( realTarget ) ; } } ) , ( object ) => { if ( object instanceof Function ) { throw new RedspotError ( ERRORS . GENERAL . UNSUPPORTED_OPERATION , { operation : 'Creating lazy functions or classes with lazyObject' } ) ; } if ( typeof object !== 'object' || object === null ) { throw new RedspotError ( ERRORS . GENERAL . UNSUPPORTED_OPERATION , { operation : 'Using lazyObject with anything other than objects' } ) ; } } ) ; } export function lazyFunction < T extends Function > ( functionCreator ) { return createLazyProxy ( functionCreator , ( getRealTarget ) => { function dummyTarget ( ) { } ( dummyTarget as any ) [ inspect ] = function ( ) { const realTarget = getRealTarget ( ) ; return util . inspect ( realTarget ) ; } ; return dummyTarget ; } , ( object ) => { if ( ! ( object instanceof Function ) ) { throw new RedspotError ( ERRORS . GENERAL . UNSUPPORTED_OPERATION , { operation : 'Using lazyFunction with anything other than functions or classes' } ) ; } } ) ; } function createLazyProxy < ActualT extends GuardT , GuardT extends object > ( targetCreator , dummyTargetCreator , validator ) { let realTarget ; const dummyTarget = dummyTargetCreator ( getRealTarget ) as any ; function getRealTarget ( ) { if ( realTarget === undefined ) { const target = targetCreator ( ) ; validator ( target ) ; const properties = Object . getOwnPropertyNames ( target ) ; for ( const property of properties ) { const descriptor = Object . getOwnPropertyDescriptor ( target , property ) ! ; Object . defineProperty ( dummyTarget , property , descriptor ) ; } Object . setPrototypeOf ( dummyTarget , Object . getPrototypeOf ( target ) ) ; if ( Object . getPrototypeOf ( target ) === null ) { throw new RedspotError ( ERRORS . GENERAL . UNSUPPORTED_OPERATION , { operation : 'Using lazyFunction or lazyObject to construct objects/functions with prototype null' } ) ; } if ( ! Object . isExtensible ( target ) ) { Object . preventExtensions ( dummyTarget ) ; } realTarget = target ; } return realTarget ; } const handler = { defineProperty ( target , property , descriptor ) { Reflect . defineProperty ( dummyTarget , property , descriptor ) ; return Reflect . defineProperty ( getRealTarget ( ) , property , descriptor ) ; } , deleteProperty ( target , property ) { Reflect . deleteProperty ( dummyTarget , property ) ; return Reflect . deleteProperty ( getRealTarget ( ) , property ) ; } , get ( target , property , receiver ) { const stack = new Error ( ) . stack ; if ( stack !== undefined && stack . includes ( 'givenProvider.js' ) && realTarget === undefined ) { return undefined ; } return Reflect . get ( getRealTarget ( ) , property , receiver ) ; } , getOwnPropertyDescriptor ( target , property ) { const descriptor = Reflect . getOwnPropertyDescriptor ( getRealTarget ( ) , property ) ; if ( descriptor !== undefined ) { Object . defineProperty ( dummyTarget , property , descriptor ) ; } return descriptor ; } , getPrototypeOf ( target ) { return Reflect . getPrototypeOf ( getRealTarget ( ) ) ; } , has ( target , property ) { return Reflect . has ( getRealTarget ( ) , property ) ; } , isExtensible ( target ) { return Reflect . isExtensible ( getRealTarget ( ) ) ; } , ownKeys ( target ) { return Reflect . ownKeys ( getRealTarget ( ) ) ; } , preventExtensions ( target ) { Object . preventExtensions ( dummyTarget ) ; return Reflect . preventExtensions ( getRealTarget ( ) ) ; } , set ( target , property , value , receiver ) { Reflect . set ( dummyTarget , property , value , receiver ) ; return Reflect . set ( getRealTarget ( ) , property , value , receiver ) ; } , setPrototypeOf ( target , prototype ) { Reflect . setPrototypeOf ( dummyTarget , prototype ) ; return Reflect . setPrototypeOf ( getRealTarget ( ) , prototype ) ; } } ; if ( dummyTarget instanceof Function ) { handler . apply = ( target , thisArg , argArray ? ) => { return Reflect . apply ( getRealTarget ( ) as Function , thisArg , argArray ) ; } ; handler . construct = ( target , argArray , newTarget ? ) => { return Reflect . construct ( getRealTarget ( ) as Function , argArray ) ; } ; } return new Proxy ( dummyTarget , handler ) ; }