"use strict";
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressSidecarServer = void 0;
exports.getOrCreateServer = getOrCreateServer;
exports.getServer = getServer;
exports.stopAllServers = stopAllServers;
const http = __importStar(require("http"));
// --- Server registry ---
const registry = new Map();
function getOrCreateServer(name, app, opts) {
    let server = registry.get(name);
    if (!server) {
        server = new ExpressSidecarServer(app, opts);
        registry.set(name, server);
    }
    return server;
}
function getServer(name) {
    return registry.get(name);
}
async function stopAllServers() {
    await Promise.all(Array.from(registry.values()).map((s) => s.stop()));
    registry.clear();
}
// --- Sidecar server ---
class ExpressSidecarServer {
    #app;
    #opts;
    #server = null;
    #port = 0;
    #started = false;
    #starting = null;
    constructor(app, opts) {
        this.#app = app;
        this.#opts = opts;
    }
    get port() {
        return this.#port;
    }
    get isStarted() {
        return this.#started;
    }
    async start() {
        if (this.#started)
            return;
        this.#starting ??= new Promise((resolve, reject) => {
            this.#server = http.createServer(this.#app);
            this.#server.on('error', reject);
            this.#server.listen(this.#opts.port, '127.0.0.1', () => {
                const addr = this.#server?.address();
                if (addr && typeof addr !== 'string')
                    this.#port = addr.port;
                this.#started = true;
                console.log(`[functions-express] Started on port ${this.#port}`);
                resolve();
            });
        });
        await this.#starting;
    }
    async stop() {
        if (!this.#server || !this.#started)
            return;
        return new Promise((resolve, reject) => {
            this.#server.close((err) => {
                if (err)
                    return reject(err);
                this.#started = false;
                this.#server = null;
                this.#starting = null;
                resolve();
            });
        });
    }
    /** Proxy a request to the Express server. Returns streaming or buffered body based on options. */
    async proxy(method, url, headers, body, invocationId) {
        await this.start();
        // Strip basePath prefix
        let path = url;
        if (this.#opts.basePath) {
            const base = this.#opts.basePath.replace(/\/$/, '');
            if (path.startsWith(base))
                path = path.slice(base.length) || '/';
        }
        return new Promise((resolve, reject) => {
            const req = http.request({
                hostname: '127.0.0.1',
                port: this.#port,
                path,
                method,
                headers: {
                    ...headers,
                    'x-forwarded-for': headers['x-forwarded-for'] || headers['x-azure-clientip'] || '',
                    'x-azure-functions-invocationid': invocationId,
                },
                timeout: this.#opts.timeout,
            }, (res) => {
                // Collect response headers
                const h = {};
                for (const [k, v] of Object.entries(res.headers)) {
                    if (v !== undefined)
                        h[k] = Array.isArray(v) ? v.join(', ') : v;
                }
                delete h['connection'];
                delete h['keep-alive'];
                delete h['transfer-encoding'];
                const status = res.statusCode || 200;
                if (this.#opts.enableStreaming) {
                    resolve({ status, headers: h, body: res });
                }
                else {
                    const chunks = [];
                    res.on('data', (c) => chunks.push(c));
                    res.on('end', () => resolve({ status, headers: h, body: Buffer.concat(chunks) }));
                    res.on('error', reject);
                }
            });
            req.on('error', reject);
            req.on('timeout', () => {
                req.destroy();
                reject(new Error(`Timeout after ${this.#opts.timeout}ms`));
            });
            body ? req.end(body) : req.end();
        });
    }
}
exports.ExpressSidecarServer = ExpressSidecarServer;
//# sourceMappingURL=server.js.map