/**
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * Modifications copyright 2021 Datadog, Inc.
 *
 * The original file was part of aws-lambda-nodejs-runtime-interface-client
 * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/main/src/utils/UserFunction.ts
 *
 * This module defines the functions for loading the user's code as specified
 * in a handler string.
 */ "use strict" ; import path from "path" ; import fs from "fs" ; import { HandlerNotFound , MalformedHandlerName , ImportModuleError , UserCodeSyntaxError , ExtendedError , } from "./errors" ; const FUNCTION_EXPR = /^([^.]*)\.(.*)$/ ; const RELATIVE_PATH_SUBSTRING = ".." ; /**
 * Break the full handler string into two pieces, the module root and the actual
 * handler string.
 * Given './somepath/something/module.nestedobj.handler' this returns
 * ['./somepath/something', 'module.nestedobj.handler']
 */ function _moduleRootAndHandler ( fullHandlerString ) { const handlerString = path . basename ( fullHandlerString ) ; const moduleRoot = fullHandlerString . substring ( 0 , fullHandlerString . indexOf ( handlerString ) ) ; return [ moduleRoot , handlerString ] ; } /**
 * Split the handler string into two pieces: the module name and the path to
 * the handler function.
 */ function _splitHandlerString ( handler ) { const match = handler . match ( FUNCTION_EXPR ) ; if ( ! match || match . length != 3 ) { throw new MalformedHandlerName ( "Bad handler" ) ; } return [ match [ 1 ] , match [ 2 ] ] ; } /**
 * Resolve the user's handler function from the module.
 */ function _resolveHandler ( object , nestedProperty ) { return nestedProperty . split ( "." ) . reduce ( ( nested , key ) => { return nested && nested [ key ] ; } , object ) ; } param string return function _canLoadAsFile ( modulePath ) { return fs . existsSync ( modulePath ) || fs . existsSync ( modulePath + ".js" ) ; } /**
 * Attempt to load the user's module.
 * Attempts to directly resolve the module relative to the application root,
 * then falls back to the more general require().
 */ function _tryRequire ( appRoot , moduleRoot , module ) { const lambdaStylePath = path . resolve ( appRoot , moduleRoot , module ) ; if ( _canLoadAsFile ( lambdaStylePath ) ) { return require ( lambdaStylePath ) ; } else { const nodeStylePath = require . resolve ( module , { paths : [ appRoot , moduleRoot ] , } ) ; return require ( nodeStylePath ) ; } } throws function _loadUserApp ( appRoot , moduleRoot , module ) { try { return _tryRequire ( appRoot , moduleRoot , module ) ; } catch ( e ) { if ( e instanceof SyntaxError ) { throw new UserCodeSyntaxError ( < any > e ) ; } else if ( e . code !== undefined && e . code === "MODULE_NOT_FOUND" ) { throw new ImportModuleError ( e ) ; } else { throw e ; } } } function _throwIfInvalidHandler ( fullHandlerString ) { if ( fullHandlerString . includes ( RELATIVE_PATH_SUBSTRING ) ) { throw new MalformedHandlerName ( `'${ fullHandlerString }' is not a valid handler name. Use absolute paths when specifying root directories in handler names.` ) ; } } param appRoot { string } param handlerString { string } return throws export const load = function ( appRoot , fullHandlerString ) { _throwIfInvalidHandler ( fullHandlerString ) ; const [ moduleRoot , moduleAndHandler ] = _moduleRootAndHandler ( fullHandlerString ) ; const [ module , handlerPath ] = _splitHandlerString ( moduleAndHandler ) ; const userApp = _loadUserApp ( appRoot , moduleRoot , module ) ; const handlerFunc = _resolveHandler ( userApp , handlerPath ) ; if ( ! handlerFunc ) { throw new HandlerNotFound ( `${ fullHandlerString } is undefined or not exported` ) ; } if ( typeof handlerFunc !== "function" ) { throw new HandlerNotFound ( `${ fullHandlerString } is not a function` ) ; } return handlerFunc ; } ;