"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const url_1 = require("url");
const _ = require("lodash");
const types_1 = require("./types");
const constants_1 = require("./constants");
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
const types_2 = require("./events/types");
let functionModule;
let FUNCTION_TARGET_NAME;
let FUNCTION_SIGNATURE;
let FUNCTION_DEBUG_MODE;
let developerPkgJSON;
const dynamicImport = new Function("modulePath", "return import(modulePath)");
function noOp() {
    return false;
}
function requireAsync(moduleName, opts) {
    return new Promise((res, rej) => {
        try {
            res(require(require.resolve(moduleName, opts)));
        }
        catch (e) {
            rej(e);
        }
    });
}
function requireResolveAsync(moduleName, opts) {
    return new Promise((res, rej) => {
        try {
            res(require.resolve(moduleName, opts));
        }
        catch (e) {
            rej(e);
        }
    });
}
class Proxied {
    static getOriginal(target, key) {
        const value = target[key];
        if (!Proxied.isExists(value)) {
            return undefined;
        }
        else if (Proxied.isConstructor(value) || typeof value !== "function") {
            return value;
        }
        else {
            return value.bind(target);
        }
    }
    static applyOriginal(target, thisArg, argArray) {
        return target.apply(thisArg, argArray);
    }
    static isConstructor(obj) {
        return !!obj.prototype && !!obj.prototype.constructor.name;
    }
    static isExists(obj) {
        return obj !== undefined;
    }
    constructor(original) {
        this.original = original;
        this.rewrites = {};
        this.proxy = new Proxy(this.original, {
            get: (target, key) => {
                key = key.toString();
                if (this.rewrites[key]) {
                    return this.rewrites[key](target, key);
                }
                if (this.anyValue) {
                    return this.anyValue(target, key);
                }
                return Proxied.getOriginal(target, key);
            },
            apply: (target, thisArg, argArray) => {
                if (this.appliedValue) {
                    return this.appliedValue.apply(thisArg);
                }
                else {
                    return Proxied.applyOriginal(target, thisArg, argArray);
                }
            },
        });
    }
    when(key, value) {
        this.rewrites[key] = value;
        return this;
    }
    any(value) {
        this.anyValue = value;
        return this;
    }
    applied(value) {
        this.appliedValue = value;
        return this;
    }
    finalize() {
        return this.proxy;
    }
}
async function resolveDeveloperNodeModule(name) {
    const pkg = requirePackageJson();
    if (!pkg) {
        new types_1.EmulatorLog("SYSTEM", "missing-package-json", "").log();
        throw new Error("Could not find package.json");
    }
    const dependencies = pkg.dependencies;
    const devDependencies = pkg.devDependencies;
    const isInPackageJSON = dependencies[name] || devDependencies[name];
    if (!isInPackageJSON) {
        return { declared: false, installed: false };
    }
    const resolveResult = await requireResolveAsync(name, { paths: [process.cwd()] }).catch(noOp);
    if (!resolveResult) {
        return { declared: true, installed: false };
    }
    const modPackageJSON = require(path.join((0, functionsEmulatorShared_1.findModuleRoot)(name, resolveResult), "package.json"));
    const moduleResolution = {
        declared: true,
        installed: true,
        version: modPackageJSON.version,
        resolution: resolveResult,
    };
    logDebug(`Resolved module ${name}`, moduleResolution);
    return moduleResolution;
}
async function assertResolveDeveloperNodeModule(name) {
    const resolution = await resolveDeveloperNodeModule(name);
    if (!(resolution.installed && resolution.declared && resolution.resolution && resolution.version)) {
        throw new Error(`Assertion failure: could not fully resolve ${name}: ${JSON.stringify(resolution)}`);
    }
    return resolution;
}
async function verifyDeveloperNodeModules() {
    const modBundles = [
        { name: "firebase-admin", isDev: false, minVersion: "8.9.0" },
        { name: "firebase-functions", isDev: false, minVersion: "3.13.1" },
    ];
    for (const modBundle of modBundles) {
        const resolution = await resolveDeveloperNodeModule(modBundle.name);
        if (!resolution.declared) {
            new types_1.EmulatorLog("SYSTEM", "missing-module", "", modBundle).log();
            return false;
        }
        if (!resolution.installed) {
            new types_1.EmulatorLog("SYSTEM", "uninstalled-module", "", modBundle).log();
            return false;
        }
        if ((0, functionsEmulatorUtils_1.compareVersionStrings)(resolution.version, modBundle.minVersion) < 0) {
            new types_1.EmulatorLog("SYSTEM", "out-of-date-module", "", modBundle).log();
            return false;
        }
    }
    return true;
}
function requirePackageJson() {
    if (developerPkgJSON) {
        return developerPkgJSON;
    }
    try {
        const pkg = require(`${process.cwd()}/package.json`);
        developerPkgJSON = {
            engines: pkg.engines || {},
            dependencies: pkg.dependencies || {},
            devDependencies: pkg.devDependencies || {},
        };
        return developerPkgJSON;
    }
    catch (err) {
        return;
    }
}
function initializeNetworkFiltering() {
    const networkingModules = [
        { name: "http", module: require("http"), path: ["request"] },
        { name: "http", module: require("http"), path: ["get"] },
        { name: "https", module: require("https"), path: ["request"] },
        { name: "https", module: require("https"), path: ["get"] },
        { name: "net", module: require("net"), path: ["connect"] },
    ];
    const history = {};
    const results = networkingModules.map((bundle) => {
        let obj = bundle.module;
        for (const field of bundle.path.slice(0, -1)) {
            obj = obj[field];
        }
        const method = bundle.path.slice(-1)[0];
        const original = obj[method].bind(bundle.module);
        obj[method] = function (...args) {
            const hrefs = args
                .map((arg) => {
                if (typeof arg === "string") {
                    try {
                        new url_1.URL(arg);
                        return arg;
                    }
                    catch (err) {
                        return;
                    }
                }
                else if (typeof arg === "object") {
                    return arg.href;
                }
                else {
                    return;
                }
            })
                .filter((v) => v);
            const href = (hrefs.length && hrefs[0]) || "";
            if (href && !history[href] && !(0, functionsEmulatorUtils_1.isLocalHost)(href)) {
                history[href] = true;
                if (href.indexOf("googleapis.com") !== -1) {
                    new types_1.EmulatorLog("SYSTEM", "googleapis-network-access", "", {
                        href,
                        module: bundle.name,
                    }).log();
                }
                else {
                    new types_1.EmulatorLog("SYSTEM", "unidentified-network-access", "", {
                        href,
                        module: bundle.name,
                    }).log();
                }
            }
            try {
                return original(...args);
            }
            catch (e) {
                const newed = new original(...args);
                return newed;
            }
        };
        return { name: bundle.name, status: "mocked" };
    });
    logDebug("Outgoing network have been stubbed.", results);
}
async function initializeFirebaseFunctionsStubs() {
    const firebaseFunctionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
    const firebaseFunctionsRoot = (0, functionsEmulatorShared_1.findModuleRoot)("firebase-functions", firebaseFunctionsResolution.resolution);
    const httpsProviderResolution = path.join(firebaseFunctionsRoot, "lib/providers/https");
    const httpsProviderV1Resolution = path.join(firebaseFunctionsRoot, "lib/v1/providers/https");
    let httpsProvider;
    try {
        httpsProvider = require(httpsProviderV1Resolution);
    }
    catch (e) {
        httpsProvider = require(httpsProviderResolution);
    }
    const onRequestInnerMethodName = "_onRequestWithOptions";
    const onRequestMethodOriginal = httpsProvider[onRequestInnerMethodName];
    httpsProvider[onRequestInnerMethodName] = (handler, opts) => {
        const cf = onRequestMethodOriginal(handler, opts);
        cf.__emulator_func = handler;
        return cf;
    };
    httpsProvider.onRequest = (handler) => {
        return httpsProvider[onRequestInnerMethodName](handler, {});
    };
    const onCallInnerMethodName = "_onCallWithOptions";
    const onCallMethodOriginal = httpsProvider[onCallInnerMethodName];
    if (onCallMethodOriginal.length === 3) {
        httpsProvider[onCallInnerMethodName] = (opts, handler, deployOpts) => {
            const wrapped = wrapCallableHandler(handler);
            const cf = onCallMethodOriginal(opts, wrapped, deployOpts);
            return cf;
        };
    }
    else {
        httpsProvider[onCallInnerMethodName] = (handler, opts) => {
            const wrapped = wrapCallableHandler(handler);
            const cf = onCallMethodOriginal(wrapped, opts);
            return cf;
        };
    }
    httpsProvider.onCall = function (optsOrHandler, handler) {
        if (onCallMethodOriginal.length === 3) {
            let opts;
            if (arguments.length === 1) {
                opts = {};
                handler = optsOrHandler;
            }
            else {
                opts = optsOrHandler;
            }
            return httpsProvider[onCallInnerMethodName](opts, handler, {});
        }
        else {
            return httpsProvider[onCallInnerMethodName](optsOrHandler, {});
        }
    };
}
function wrapCallableHandler(handler) {
    const newHandler = (data, context) => {
        if (context.rawRequest) {
            const authContext = context.rawRequest.header(functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER);
            if (authContext) {
                logDebug("Callable functions auth override", {
                    key: functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER,
                    value: authContext,
                });
                context.auth = JSON.parse(decodeURIComponent(authContext));
                delete context.rawRequest.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER];
            }
            else {
                logDebug("No callable functions auth found");
            }
            const originalAuth = context.rawRequest.header(functionsEmulatorShared_1.HttpConstants.ORIGINAL_AUTH_HEADER);
            if (originalAuth) {
                context.rawRequest.headers["authorization"] = originalAuth;
                delete context.rawRequest.headers[functionsEmulatorShared_1.HttpConstants.ORIGINAL_AUTH_HEADER];
            }
        }
        return handler(data, context);
    };
    return newHandler;
}
function getDefaultConfig() {
    return JSON.parse(process.env.FIREBASE_CONFIG || "{}");
}
function initializeRuntimeConfig() {
    if (!process.env.CLOUD_RUNTIME_CONFIG) {
        const configPath = `${process.cwd()}/.runtimeconfig.json`;
        try {
            const configContent = fs.readFileSync(configPath, "utf8");
            if (configContent) {
                try {
                    JSON.parse(configContent.toString());
                    logDebug(`Found local functions config: ${configPath}`);
                    process.env.CLOUD_RUNTIME_CONFIG = configContent.toString();
                }
                catch (e) {
                    new types_1.EmulatorLog("SYSTEM", "function-runtimeconfig-json-invalid", "").log();
                }
            }
        }
        catch (e) {
        }
    }
}
async function initializeFirebaseAdminStubs() {
    const adminResolution = await assertResolveDeveloperNodeModule("firebase-admin");
    const localAdminModule = require(adminResolution.resolution);
    const functionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
    const localFunctionsModule = require(functionsResolution.resolution);
    const defaultConfig = getDefaultConfig();
    const adminModuleProxy = new Proxied(localAdminModule);
    const proxiedAdminModule = adminModuleProxy
        .when("initializeApp", (adminModuleTarget) => (opts, appName) => {
        if (appName) {
            new types_1.EmulatorLog("SYSTEM", "non-default-admin-app-used", "", { appName, opts }).log();
            return adminModuleTarget.initializeApp(opts, appName);
        }
        const defaultAppOptions = opts ? opts : defaultConfig;
        new types_1.EmulatorLog("SYSTEM", "default-admin-app-used", `config=${defaultAppOptions}`, {
            opts: defaultAppOptions,
        }).log();
        const defaultApp = makeProxiedFirebaseApp(adminModuleTarget.initializeApp(defaultAppOptions));
        logDebug("initializeApp(DEFAULT)", defaultAppOptions);
        localFunctionsModule.app.setEmulatedAdminApp(defaultApp);
        if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
            if ((0, functionsEmulatorUtils_1.compareVersionStrings)(adminResolution.version, "9.3.0") < 0) {
                new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Authentication emulator is running, but your 'firebase-admin' dependency is below version 9.3.0, so calls to Firebase Authentication will affect production.").log();
            }
            else if ((0, functionsEmulatorUtils_1.compareVersionStrings)(adminResolution.version, "9.4.2") <= 0) {
                const auth = defaultApp.auth();
                if (typeof auth.setJwtVerificationEnabled === "function") {
                    logDebug("auth.setJwtVerificationEnabled(false)", {});
                    auth.setJwtVerificationEnabled(false);
                }
                else {
                    logDebug("auth.setJwtVerificationEnabled not available", {});
                }
            }
        }
        return defaultApp;
    })
        .when("firestore", (target) => {
        warnAboutFirestoreProd();
        return Proxied.getOriginal(target, "firestore");
    })
        .when("database", (target) => {
        warnAboutDatabaseProd();
        return Proxied.getOriginal(target, "database");
    })
        .when("auth", (target) => {
        warnAboutAuthProd();
        return Proxied.getOriginal(target, "auth");
    })
        .when("storage", (target) => {
        warnAboutStorageProd();
        return Proxied.getOriginal(target, "storage");
    })
        .finalize();
    require.cache[adminResolution.resolution] = Object.assign(require.cache[adminResolution.resolution], {
        exports: proxiedAdminModule,
        path: path.dirname(adminResolution.resolution),
    });
    logDebug("firebase-admin has been stubbed.", {
        adminResolution,
    });
}
function makeProxiedFirebaseApp(original) {
    const appProxy = new Proxied(original);
    return appProxy
        .when("firestore", (target) => {
        warnAboutFirestoreProd();
        return Proxied.getOriginal(target, "firestore");
    })
        .when("database", (target) => {
        warnAboutDatabaseProd();
        return Proxied.getOriginal(target, "database");
    })
        .when("auth", (target) => {
        warnAboutAuthProd();
        return Proxied.getOriginal(target, "auth");
    })
        .when("storage", (target) => {
        warnAboutStorageProd();
        return Proxied.getOriginal(target, "storage");
    })
        .finalize();
}
function warnAboutFirestoreProd() {
    if (process.env[constants_1.Constants.FIRESTORE_EMULATOR_HOST]) {
        return;
    }
    new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Cloud Firestore emulator is not running, so calls to Firestore will affect production.").log();
}
function warnAboutDatabaseProd() {
    if (process.env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST]) {
        return;
    }
    new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Realtime Database emulator is not running, so calls to Realtime Database will affect production.").log();
}
function warnAboutAuthProd() {
    if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
        return;
    }
    new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Authentication emulator is not running, so calls to Firebase Authentication will affect production.").log();
}
function warnAboutStorageProd() {
    if (process.env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST]) {
        return;
    }
    new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Storage emulator is not running, so calls to Firebase Storage will affect production.").log();
}
async function initializeFunctionsConfigHelper() {
    const functionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
    const localFunctionsModule = require(functionsResolution.resolution);
    logDebug("Checked functions.config()", {
        config: localFunctionsModule.config(),
    });
    const originalConfig = localFunctionsModule.config();
    const proxiedConfig = new Proxied(originalConfig)
        .any((parentConfig, parentKey) => {
        const isInternal = parentKey.startsWith("Symbol(") || parentKey.startsWith("inspect");
        if (!parentConfig[parentKey] && !isInternal) {
            new types_1.EmulatorLog("SYSTEM", "functions-config-missing-value", "", {
                key: parentKey,
            }).log();
        }
        return parentConfig[parentKey];
    })
        .finalize();
    const functionsModuleProxy = new Proxied(localFunctionsModule);
    const proxiedFunctionsModule = functionsModuleProxy
        .when("config", () => () => {
        return proxiedConfig;
    })
        .finalize();
    require.cache[functionsResolution.resolution] = Object.assign(require.cache[functionsResolution.resolution], {
        exports: proxiedFunctionsModule,
        path: path.dirname(functionsResolution.resolution),
    });
    logDebug("firebase-functions has been stubbed.", {
        functionsResolution,
    });
}
function rawBodySaver(req, res, buf) {
    req.rawBody = buf;
}
async function processBackground(trigger, reqBody, signature) {
    if (signature === "cloudevent") {
        return runCloudEvent(trigger, reqBody);
    }
    const data = reqBody.data;
    delete reqBody.data;
    const context = reqBody.context ? reqBody.context : reqBody;
    if (!reqBody.eventType || !reqBody.eventType.startsWith("google.storage")) {
        if (context.resource && context.resource.name) {
            logDebug("ProcessBackground: lifting resource.name from resource", context.resource);
            context.resource = context.resource.name;
        }
    }
    await runBackground(trigger, { data, context });
}
async function runFunction(func) {
    let caughtErr;
    try {
        await func();
    }
    catch (err) {
        caughtErr = err;
    }
    if (caughtErr) {
        throw caughtErr;
    }
}
async function runBackground(trigger, reqBody) {
    logDebug("RunBackground", reqBody);
    await runFunction(() => {
        return trigger(reqBody.data, reqBody.context);
    });
}
async function runCloudEvent(trigger, event) {
    logDebug("RunCloudEvent", event);
    await runFunction(() => {
        return trigger(event);
    });
}
async function runHTTPS(trigger, args) {
    if (args.length < 2) {
        throw new Error("Function must be passed 2 args.");
    }
    await runFunction(() => {
        return trigger(args[0], args[1]);
    });
}
async function moduleResolutionDetective(error) {
    const clues = {
        tsconfigJSON: await requireAsync("./tsconfig.json", { paths: [process.cwd()] }).catch(noOp),
        packageJSON: await requireAsync("./package.json", { paths: [process.cwd()] }).catch(noOp),
    };
    const isPotentially = {
        typescript: false,
        uncompiled: false,
        wrong_directory: false,
    };
    isPotentially.typescript = !!clues.tsconfigJSON;
    isPotentially.wrong_directory = !clues.packageJSON;
    isPotentially.uncompiled = !!_.get(clues.packageJSON, "scripts.build", false);
    new types_1.EmulatorLog("SYSTEM", "function-code-resolution-failed", "", {
        isPotentially,
        error: error.stack,
    }).log();
}
function logDebug(msg, data) {
    new types_1.EmulatorLog("DEBUG", "runtime-status", `[${process.pid}] ${msg}`, data).log();
}
async function initializeRuntime() {
    FUNCTION_DEBUG_MODE = process.env.FUNCTION_DEBUG_MODE || "";
    if (!FUNCTION_DEBUG_MODE) {
        FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
        if (!FUNCTION_TARGET_NAME) {
            new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_TARGET cannot be empty. This shouldn't happen.`).log();
            await flushAndExit(1);
        }
        FUNCTION_SIGNATURE = process.env.FUNCTION_SIGNATURE_TYPE || "";
        if (!FUNCTION_SIGNATURE) {
            new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_SIGNATURE_TYPE cannot be empty. This shouldn't happen.`).log();
            await flushAndExit(1);
        }
    }
    const verified = await verifyDeveloperNodeModules();
    if (!verified) {
        new types_1.EmulatorLog("INFO", "runtime-status", `Your functions could not be parsed due to an issue with your node_modules (see above)`).log();
        return;
    }
    initializeRuntimeConfig();
    initializeNetworkFiltering();
    await initializeFunctionsConfigHelper();
    await initializeFirebaseFunctionsStubs();
    await initializeFirebaseAdminStubs();
}
async function loadTriggers() {
    let triggerModule;
    try {
        triggerModule = require(process.cwd());
    }
    catch (err) {
        if (err.code !== "ERR_REQUIRE_ESM") {
            await moduleResolutionDetective(err);
            throw err;
        }
        const modulePath = require.resolve(process.cwd());
        const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
        triggerModule = await dynamicImport(moduleURL);
    }
    return triggerModule;
}
async function flushAndExit(code) {
    await types_1.EmulatorLog.waitForFlush();
    process.exit(code);
}
async function handleMessage(message) {
    let debug;
    try {
        debug = JSON.parse(message);
    }
    catch (e) {
        new types_1.EmulatorLog("FATAL", "runtime-error", `Got unexpected message body: ${message}`).log();
        await flushAndExit(1);
        return;
    }
    if (FUNCTION_DEBUG_MODE) {
        if (debug) {
            FUNCTION_TARGET_NAME = debug.functionTarget;
            FUNCTION_SIGNATURE = debug.functionSignature;
        }
        else {
            new types_1.EmulatorLog("WARN", "runtime-warning", "Expected debug payload while in debug mode.");
        }
    }
}
async function main() {
    let lastSignal = new Date().getTime();
    let signalCount = 0;
    process.on("SIGINT", () => {
        const now = new Date().getTime();
        if (now - lastSignal < 100) {
            return;
        }
        signalCount = signalCount + 1;
        lastSignal = now;
        if (signalCount >= 2) {
            process.exit(1);
        }
    });
    await initializeRuntime();
    try {
        functionModule = await loadTriggers();
    }
    catch (e) {
        new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load triggers. This shouldn't happen: ${e.message}`).log();
        await flushAndExit(1);
    }
    const app = express();
    app.enable("trust proxy");
    app.use(bodyParser.json({
        limit: "10mb",
        verify: rawBodySaver,
    }));
    app.use(bodyParser.text({
        limit: "10mb",
        verify: rawBodySaver,
    }));
    app.use(bodyParser.urlencoded({
        extended: true,
        limit: "10mb",
        verify: rawBodySaver,
    }));
    app.use(bodyParser.raw({
        type: "*/*",
        limit: "10mb",
        verify: rawBodySaver,
    }));
    app.get("/__/health", (req, res) => {
        res.status(200).send();
    });
    app.all("/favicon.ico|/robots.txt", (req, res) => {
        res.status(404).send();
    });
    app.all(`/*`, async (req, res) => {
        try {
            new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`).log();
            const trigger = FUNCTION_TARGET_NAME.split(".").reduce((mod, functionTargetPart) => {
                return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
            }, functionModule);
            if (!trigger) {
                throw new Error(`Failed to find function ${FUNCTION_TARGET_NAME} in the loaded module`);
            }
            const startHrTime = process.hrtime();
            res.on("finish", () => {
                const elapsedHrTime = process.hrtime(startHrTime);
                new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ${elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1000000}ms`).log();
            });
            switch (FUNCTION_SIGNATURE) {
                case "event":
                case "cloudevent":
                    const rawBody = req.rawBody;
                    let reqBody = JSON.parse(rawBody.toString());
                    if (types_2.EventUtils.isBinaryCloudEvent(req)) {
                        reqBody = types_2.EventUtils.extractBinaryCloudEventContext(req);
                        reqBody.data = req.body;
                    }
                    await processBackground(trigger, reqBody, FUNCTION_SIGNATURE);
                    res.send({ status: "acknowledged" });
                    break;
                case "http":
                    await runHTTPS(trigger, [req, res]);
            }
        }
        catch (err) {
            new types_1.EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log();
            res.status(500).send(err.message);
        }
    });
    const server = app.listen(process.env.PORT, () => {
        logDebug(`Listening to port: ${process.env.PORT}`);
    });
    if (!FUNCTION_DEBUG_MODE) {
        let timeout = process.env.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS || "60";
        if (timeout.endsWith("s")) {
            timeout = timeout.slice(0, -1);
        }
        const timeoutMs = parseInt(timeout, 10) * 1000;
        server.setTimeout(timeoutMs, () => {
            new types_1.EmulatorLog("FATAL", "runtime-error", `Your function timed out after ~${timeout}s. To configure this timeout, see
      https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.`).log();
            return flushAndExit(1);
        });
    }
    let messageHandlePromise = Promise.resolve();
    process.on("message", (message) => {
        messageHandlePromise = messageHandlePromise
            .then(() => {
            return handleMessage(message);
        })
            .catch((err) => {
            logDebug(`Error in handleMessage: ${message} => ${err}: ${err.stack}`);
            new types_1.EmulatorLog("FATAL", "runtime-error", err.message || err, err).log();
            return flushAndExit(1);
        });
    });
}
if (require.main === module) {
    main()
        .then(() => {
        logDebug("Functions runtime initialized.", {
            cwd: process.cwd(),
            node_version: process.versions.node,
        });
    })
        .catch((err) => {
        new types_1.EmulatorLog("FATAL", "runtime-error", err.message || err, err).log();
        return flushAndExit(1);
    });
}
//# sourceMappingURL=functionsEmulatorRuntime.js.map