'use strict';

var fs = require('fs');
var path = require('path');
var querystring = require('querystring');
var Url = require('url');
var zlib = require('zlib');
var buffer = require('buffer');
var http = require('http');
var https = require('https');
var net = require('net');

class NodeFs {
    copyFile(src, dest) {
        return new Promise((resolve, reject) => {
            const readStream = fs.createReadStream(src);
            readStream.on('error', reject);
            const writeStream = fs.createWriteStream(dest);
            writeStream.on('error', reject);
            writeStream.on('close', resolve);
            readStream.pipe(writeStream);
        });
    }
    createReadStream(filePath) {
        return fs.createReadStream(filePath);
    }
    mkdir(dirPath) {
        return new Promise((resolve, reject) => {
            fs.mkdir(dirPath, (err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
    }
    mkdirSync(dirPath) {
        fs.mkdirSync(dirPath);
    }
    readdir(dirPath) {
        return new Promise((resolve, reject) => {
            fs.readdir(dirPath, (err, files) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(files);
                }
            });
        });
    }
    readFile(filePath) {
        return new Promise((resolve, reject) => {
            fs.readFile(filePath, 'utf8', (err, content) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(content);
                }
            });
        });
    }
    readFileSync(filePath) {
        return fs.readFileSync(filePath, 'utf8');
    }
    rmdir(dirPath) {
        return new Promise((resolve, reject) => {
            fs.rmdir(dirPath, (err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
    }
    stat(itemPath) {
        return new Promise((resolve, reject) => {
            fs.stat(itemPath, (err, stats) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(stats);
                }
            });
        });
    }
    statSync(itemPath) {
        return fs.statSync(itemPath);
    }
    unlink(filePath) {
        return new Promise((resolve, reject) => {
            fs.unlink(filePath, (err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
    }
    writeFile(filePath, content) {
        return new Promise((resolve, reject) => {
            fs.writeFile(filePath, content, { encoding: 'utf8' }, (err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve();
                }
            });
        });
    }
    writeFileSync(filePath, content) {
        return fs.writeFileSync(filePath, content, { encoding: 'utf8' });
    }
}

function sendMsg(process, msg) {
    process.send(msg);
}
function sendError(process, e) {
    const msg = {
        error: {
            message: e
        }
    };
    if (typeof e === 'string') {
        msg.error.message = e + '';
    }
    else if (e) {
        Object.keys(e).forEach(key => {
            try {
                msg.error[key] = e[key] + '';
            }
            catch (idk) {
                console.log(idk);
            }
        });
    }
    sendMsg(process, msg);
}
function getBrowserUrl(devServerConfig, pathname = '/') {
    const address = (devServerConfig.address === `0.0.0.0`) ? `localhost` : devServerConfig.address;
    const port = (devServerConfig.port === 80 || devServerConfig.port === 443) ? '' : (':' + devServerConfig.port);
    return `${devServerConfig.protocol}://${address}${port}${pathname}`;
}
function getContentType(devServerConfig, filePath) {
    const last = filePath.replace(/^.*[/\\]/, '').toLowerCase();
    const ext = last.replace(/^.*\./, '').toLowerCase();
    const hasPath = last.length < filePath.length;
    const hasDot = ext.length < last.length - 1;
    return ((hasDot || !hasPath) && devServerConfig.contentTypes[ext]) || 'application/octet-stream';
}
function isHtmlFile(filePath) {
    filePath = filePath.toLowerCase().trim();
    return (filePath.endsWith('.html') || filePath.endsWith('.htm'));
}
function isCssFile(filePath) {
    filePath = filePath.toLowerCase().trim();
    return filePath.endsWith('.css');
}
const TXT_EXT = ['css', 'html', 'htm', 'js', 'json', 'svg', 'xml'];
function isSimpleText(filePath) {
    const ext = filePath.toLowerCase().trim().split('.').pop();
    return TXT_EXT.includes(ext);
}
function isStaticDevClient(req) {
    return req.pathname.startsWith(DEV_SERVER_URL);
}
function isInitialDevServerLoad(pathname) {
    return pathname === UNREGISTER_SW_URL;
}
function isDevServerClient(pathname) {
    return pathname === DEV_SERVER_URL;
}
const DEV_SERVER_URL = '/~dev-server';
const UNREGISTER_SW_URL = `${DEV_SERVER_URL}-init`;
function shouldCompress(devServerConfig, req, contentLength) {
    if (!devServerConfig.gzip) {
        return false;
    }
    if (req.method !== 'GET') {
        return false;
    }
    if (contentLength < 1024) {
        return false;
    }
    const acceptEncoding = req.headers && req.headers['accept-encoding'];
    if (typeof acceptEncoding !== 'string') {
        return false;
    }
    if (!acceptEncoding.includes('gzip')) {
        return false;
    }
    return true;
}

/**
 * SSR Attribute Names
 */

function normalizePath(str) {
    // Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
    // https://github.com/sindresorhus/slash MIT
    // By Sindre Sorhus
    if (typeof str !== 'string') {
        throw new Error(`invalid path to normalize`);
    }
    str = str.trim();
    if (EXTENDED_PATH_REGEX.test(str) || NON_ASCII_REGEX.test(str)) {
        return str;
    }
    str = str.replace(SLASH_REGEX, '/');
    // always remove the trailing /
    // this makes our file cache look ups consistent
    if (str.charAt(str.length - 1) === '/') {
        const colonIndex = str.indexOf(':');
        if (colonIndex > -1) {
            if (colonIndex < str.length - 2) {
                str = str.substring(0, str.length - 1);
            }
        }
        else if (str.length > 1) {
            str = str.substring(0, str.length - 1);
        }
    }
    return str;
}
const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
const SLASH_REGEX = /\\/g;

var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function serve404(config, fs$$1, req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const headers = {
                'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
                'Expires': '0',
                'Content-Type': 'text/html',
                'X-Powered-By': 'Stencil Dev Server'
            };
            let content;
            const pathName = req.pathname.toLowerCase();
            if (pathName.endsWith('.js') || pathName.endsWith('.css')) {
                headers['Content-Type'] = 'text/plain';
                content = [
                    '404 File Not Found',
                    'Url: ' + req.pathname,
                    'File: ' + req.filePath
                ].join('\n');
            }
            else {
                const tmpl404 = yield fs$$1.readFile(path.join(config.devServerDir, 'templates', '404.html'));
                content = tmpl404.replace('{{content}}', `File not found: ${req.pathname}`);
            }
            res.writeHead(404, headers);
            res.write(content);
            res.end();
        }
        catch (e) {
            serve500(res, e);
        }
    });
}
function serve500(res, error) {
    try {
        res.writeHead(500, {
            'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
            'Expires': '0',
            'Content-Type': 'text/plain',
            'X-Powered-By': 'Stencil Dev Server'
        });
        let errorMsg = '';
        if (typeof error === 'string') {
            errorMsg = error;
        }
        else if (error) {
            if (error.message) {
                errorMsg += error.message + '\n';
            }
            if (error.stack) {
                errorMsg += error.stack + '\n';
            }
        }
        res.write(errorMsg);
        res.end();
    }
    catch (e) {
        sendError(process, 'serve500: ' + e);
    }
}

var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function serveFile(devServerConfig, fs$$1, req, res) {
    return __awaiter$1(this, void 0, void 0, function* () {
        try {
            if (isSimpleText(req.filePath)) {
                // easy text file, use the internal cache
                let content = yield fs$$1.readFile(req.filePath);
                if (isHtmlFile(req.filePath) && !isDevServerClient(req.pathname)) {
                    // auto inject our dev server script
                    content += injectDevServerClient();
                }
                else if (isCssFile(req.filePath)) {
                    content = updateStyleUrls(req.url, content);
                }
                const contentLength = buffer.Buffer.byteLength(content, 'utf8');
                if (shouldCompress(devServerConfig, req, contentLength)) {
                    // let's gzip this well known web dev text file
                    res.writeHead(200, {
                        'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
                        'Expires': '0',
                        'Content-Type': getContentType(devServerConfig, req.filePath),
                        'X-Powered-By': 'Stencil Dev Server'
                    });
                    zlib.createGzip().pipe(res);
                }
                else {
                    // let's not gzip this file
                    res.writeHead(200, {
                        'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
                        'Expires': '0',
                        'Content-Type': getContentType(devServerConfig, req.filePath),
                        'Content-Length': contentLength,
                        'X-Powered-By': 'Stencil Dev Server'
                    });
                    res.write(content);
                    res.end();
                }
            }
            else {
                // non-well-known text file or other file, probably best we use a stream
                // but don't bother trying to gzip this file for the dev server
                res.writeHead(200, {
                    'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
                    'Expires': '0',
                    'Content-Type': getContentType(devServerConfig, req.filePath),
                    'Content-Length': req.stats.size,
                    'X-Powered-By': 'Stencil Dev Server'
                });
                fs$$1.createReadStream(req.filePath).pipe(res);
            }
        }
        catch (e) {
            serve500(res, e);
        }
    });
}
function serveStaticDevClient(devServerConfig, fs$$1, req, res) {
    return __awaiter$1(this, void 0, void 0, function* () {
        try {
            if (isDevServerClient(req.pathname)) {
                req.filePath = path.join(devServerConfig.devServerDir, 'static', 'dev-server-client.html');
            }
            else if (isInitialDevServerLoad(req.pathname)) {
                req.filePath = path.join(devServerConfig.devServerDir, 'templates', 'initial-load.html');
            }
            else {
                const staticFile = req.pathname.replace(DEV_SERVER_URL + '/', '');
                req.filePath = path.join(devServerConfig.devServerDir, 'static', staticFile);
            }
            try {
                req.stats = yield fs$$1.stat(req.filePath);
                return serveFile(devServerConfig, fs$$1, req, res);
            }
            catch (e) {
                return serve404(devServerConfig, fs$$1, req, res);
            }
        }
        catch (e) {
            return serve500(res, e);
        }
    });
}
function injectDevServerClient() {
    return `\n<iframe src="${DEV_SERVER_URL}" style="width:0;height:0;border:0"></iframe>`;
}
function updateStyleUrls(cssUrl, oldCss) {
    const parsedUrl = Url.parse(cssUrl);
    const qs = querystring.parse(parsedUrl.query);
    const versionId = qs['s-hmr'];
    const hmrUrls = qs['s-hmr-urls'];
    if (versionId && hmrUrls) {
        hmrUrls.split(',').forEach(hmrUrl => {
            urlVersionIds.set(hmrUrl, versionId);
        });
    }
    const reg = /url\((['"]?)(.*)\1\)/ig;
    let result;
    let newCss = oldCss;
    while ((result = reg.exec(oldCss)) !== null) {
        const oldUrl = result[2];
        const parsedUrl = Url.parse(oldUrl);
        const fileName = path.basename(parsedUrl.pathname);
        const versionId = urlVersionIds.get(fileName);
        if (!versionId) {
            continue;
        }
        const qs = querystring.parse(parsedUrl.query);
        qs['s-hmr'] = versionId;
        parsedUrl.search = querystring.stringify(qs);
        const newUrl = Url.format(parsedUrl);
        newCss = newCss.replace(oldUrl, newUrl);
    }
    return newCss;
}
const urlVersionIds = new Map();

var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function serveDirectoryIndex(devServerConfig, fs$$1, req, res) {
    return __awaiter$2(this, void 0, void 0, function* () {
        try {
            const indexFilePath = path.join(req.filePath, 'index.html');
            req.stats = yield fs$$1.stat(indexFilePath);
            if (req.stats.isFile()) {
                req.filePath = indexFilePath;
                return serveFile(devServerConfig, fs$$1, req, res);
            }
        }
        catch (e) { }
        if (!req.pathname.endsWith('/')) {
            res.writeHead(302, {
                'location': req.pathname + '/'
            });
            return res.end();
        }
        try {
            const dirItemNames = yield fs$$1.readdir(req.filePath);
            try {
                const dirTemplatePath = path.join(devServerConfig.devServerDir, 'templates', 'directory-index.html');
                const dirTemplate = yield fs$$1.readFile(dirTemplatePath);
                const files = yield getFiles(fs$$1, req.filePath, req.pathname, dirItemNames);
                const templateHtml = dirTemplate
                    .replace('{{title}}', getTitle(req.pathname))
                    .replace('{{nav}}', getName(req.pathname))
                    .replace('{{files}}', files);
                res.writeHead(200, {
                    'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
                    'Expires': '0',
                    'Content-Type': 'text/html',
                    'X-Powered-By': 'Stencil Dev Server'
                });
                res.write(templateHtml);
                res.end();
            }
            catch (e) {
                serve500(res, e);
            }
        }
        catch (e) {
            serve404(devServerConfig, fs$$1, req, res);
        }
    });
}
function getFiles(fs$$1, filePath, urlPathName, dirItemNames) {
    return __awaiter$2(this, void 0, void 0, function* () {
        const items = yield getDirectoryItems(fs$$1, filePath, urlPathName, dirItemNames);
        if (urlPathName !== '/') {
            items.unshift({
                isDirectory: true,
                pathname: '../',
                name: '..'
            });
        }
        return items
            .map(item => {
            return (`
        <li class="${item.isDirectory ? 'directory' : 'file'}">
          <a href="${item.pathname}">
            <span class="icon"></span>
            <span>${item.name}</span>
          </a>
        </li>`);
        })
            .join('');
    });
}
function getDirectoryItems(fs$$1, filePath, urlPathName, dirItemNames) {
    return __awaiter$2(this, void 0, void 0, function* () {
        return Promise.all(dirItemNames.map((dirItemName) => __awaiter$2(this, void 0, void 0, function* () {
            const absPath = path.join(filePath, dirItemName);
            const stats = yield fs$$1.stat(absPath);
            const item = {
                name: dirItemName,
                pathname: Url.resolve(urlPathName, dirItemName),
                isDirectory: stats.isDirectory()
            };
            return item;
        })));
    });
}
function getTitle(pathName) {
    return pathName;
}
function getName(pathName) {
    const dirs = pathName.split('/');
    dirs.pop();
    let url = '';
    return dirs.map((dir, index) => {
        url += dir + '/';
        const text = (index === 0 ? `~` : dir);
        return `<a href="${url}">${text}</a>`;
    }).join('<span>/</span>') + '<span>/</span>';
}

var __awaiter$3 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function createRequestHandler(devServerConfig, fs$$1) {
    return function (incomingReq, res) {
        return __awaiter$3(this, void 0, void 0, function* () {
            try {
                const req = normalizeHttpRequest(devServerConfig, incomingReq);
                if (req.pathname === '') {
                    res.writeHead(302, { 'location': '/' });
                    return res.end();
                }
                if (isStaticDevClient(req)) {
                    return serveStaticDevClient(devServerConfig, fs$$1, req, res);
                }
                try {
                    req.stats = yield fs$$1.stat(req.filePath);
                    if (req.stats.isFile()) {
                        return serveFile(devServerConfig, fs$$1, req, res);
                    }
                    if (req.stats.isDirectory()) {
                        return serveDirectoryIndex(devServerConfig, fs$$1, req, res);
                    }
                }
                catch (e) { }
                if (isValidHistoryApi(devServerConfig, req)) {
                    try {
                        const indexFilePath = path.join(devServerConfig.root, devServerConfig.historyApiFallback.index);
                        req.stats = yield fs$$1.stat(indexFilePath);
                        if (req.stats.isFile()) {
                            req.filePath = indexFilePath;
                            return serveFile(devServerConfig, fs$$1, req, res);
                        }
                    }
                    catch (e) { }
                }
                return serve404(devServerConfig, fs$$1, req, res);
            }
            catch (e) {
                return serve500(res, e);
            }
        });
    };
}
function normalizeHttpRequest(devServerConfig, incomingReq) {
    const req = {
        method: (incomingReq.method || 'GET').toUpperCase(),
        acceptHeader: (incomingReq.headers && typeof incomingReq.headers.accept === 'string' && incomingReq.headers.accept) || '',
        url: (incomingReq.url || '').trim() || ''
    };
    const parsedUrl = Url.parse(req.url);
    const parts = (parsedUrl.pathname || '').replace(/\\/g, '/').split('/');
    req.pathname = parts.map(part => decodeURIComponent(part)).join('/');
    req.filePath = normalizePath(path.normalize(path.join(devServerConfig.root, path.relative('/', req.pathname))));
    return req;
}
function isValidHistoryApi(devServerConfig, req) {
    return !!devServerConfig.historyApiFallback &&
        req.method === 'GET' &&
        (!devServerConfig.historyApiFallback.disableDotRule && !req.pathname.includes('.')) &&
        req.acceptHeader.includes('text/html');
}

var __awaiter$4 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
// import getDevelopmentCertificate from 'devcert-san';
function getSSL() {
    return __awaiter$4(this, void 0, void 0, function* () {
        const cert = yield installSSL();
        return {
            key: fs.readFileSync(cert.keyPath, 'utf-8'),
            cert: fs.readFileSync(cert.certPath, 'utf-8')
        };
    });
}
function installSSL() {
    return __awaiter$4(this, void 0, void 0, function* () {
        // try {
        //   //  Certificates are cached by name, so two calls for getDevelopmentCertificate('foo')  will return the same key and certificate
        //   return getDevelopmentCertificate('stencil-dev-server-ssl', {
        //     installCertutil: true
        //   });
        // } catch (err) {
        //   throw new Error(`Failed to generate dev SSL certificate: ${err}\n`);
        // }
    });
}

var __awaiter$5 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function createHttpServer(devServerConfig, devServerCtx, fs$$1) {
    return __awaiter$5(this, void 0, void 0, function* () {
        const reqHandler = createRequestHandler(devServerConfig, fs$$1);
        let server;
        if (devServerConfig.protocol === 'https') {
            server = https.createServer(yield getSSL(), reqHandler);
        }
        else {
            server = http.createServer(reqHandler);
        }
        devServerCtx.httpServer = server;
        return server;
    });
}

const WebSocket = require('../sys/node/faye-websocket').fayeWebSocket;
function initWebSocketUpgrads(devServerContext, httpServer) {
    httpServer.on('upgrade', (request, socket, body) => {
        if (WebSocket.isWebSocket(request)) {
            onWebSocketConnection(devServerContext, request, socket, body);
        }
    });
}
function onWebSocketConnection(devServerContext, request, socket, body) {
    const webSocket = new WebSocket(request, socket, body, ['xmpp']);
    webSocket.on('message', (ev) => {
        // the server process has received a message from the browser
        // pass the message received from the browser to the main cli process
        process.send(JSON.parse(ev.data));
    });
    devServerContext.wsConnections.push(webSocket);
}

var __awaiter$6 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function findClosestOpenPort(host, port) {
    return __awaiter$6(this, void 0, void 0, function* () {
        function t(portToCheck) {
            return __awaiter$6(this, void 0, void 0, function* () {
                const isTaken = yield isPortTaken(host, portToCheck);
                if (!isTaken) {
                    return portToCheck;
                }
                return t(portToCheck + 1);
            });
        }
        return t(port);
    });
}
function isPortTaken(host, port) {
    return new Promise((resolve, reject) => {
        const tester = net.createServer()
            .once('error', () => {
            resolve(true);
        })
            .once('listening', () => {
            tester.once('close', () => {
                resolve(false);
            })
                .close();
        })
            .on('error', (err) => {
            reject(err);
        })
            .listen(port, host);
    });
}

var __awaiter$7 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function startDevServerWorker(devServerConfig, fs$$1) {
    return __awaiter$7(this, void 0, void 0, function* () {
        try {
            // create the context object that'll be reused throughout the server
            const devServerContext = {
                httpServer: null,
                wsConnections: []
            };
            // figure out the port to be listening on
            // by figuring out the first one available
            devServerConfig.port = yield findClosestOpenPort(devServerConfig.address, devServerConfig.port);
            // create the http server listening for and responding to requests from the browser
            const httpServer = yield createHttpServer(devServerConfig, devServerContext, fs$$1);
            // upgrade any web socket requests the server receives
            initWebSocketUpgrads(devServerContext, httpServer);
            // start listening!
            httpServer.listen(devServerConfig.port, devServerConfig.address);
            // have the server worker send a message to the main cli
            // process that the server has successfully started up
            sendMsg(process, {
                serverStated: {
                    browserUrl: getBrowserUrl(devServerConfig),
                    initialLoadUrl: getBrowserUrl(devServerConfig, UNREGISTER_SW_URL)
                }
            });
            function onMessageFromCli(msg) {
                // the server process has received a message from the cli's main thread
                // pass the data to each web socket for each browser connect
                if (devServerContext.wsConnections) {
                    devServerContext.wsConnections.forEach(webSocket => {
                        webSocket.send(JSON.stringify(msg));
                    });
                }
            }
            function onSigInt() {
                // probably recived a SIGINT message from the parent cli process
                // let's do our best to gracefully close everything down first
                if (devServerContext.wsConnections) {
                    // close every web socket connection we have open
                    devServerContext.wsConnections.forEach(webSocket => {
                        webSocket.close(1000);
                    });
                    devServerContext.wsConnections.length = 0;
                }
                if (devServerContext.httpServer) {
                    // close the http server
                    devServerContext.httpServer.close();
                    devServerContext.httpServer = null;
                }
            }
            // add our listeners to this worker process
            process.addListener('message', onMessageFromCli);
            process.once('SIGINT', onSigInt);
        }
        catch (e) {
            sendError(process, e);
        }
    });
}

var __awaiter$8 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function startServer(devServerConfig) {
    return __awaiter$8(this, void 0, void 0, function* () {
        // received a message from main to start the server
        try {
            const fs$$1 = new NodeFs();
            devServerConfig.contentTypes = yield loadContentTypes(fs$$1);
            startDevServerWorker(devServerConfig, fs$$1);
        }
        catch (e) {
            sendError(process, e);
        }
    });
}
function loadContentTypes(fs$$1) {
    return __awaiter$8(this, void 0, void 0, function* () {
        const contentTypePath = path.join(__dirname, 'content-type-db.json');
        const contentTypeJson = yield fs$$1.readFile(contentTypePath);
        return JSON.parse(contentTypeJson);
    });
}
process.on('message', (msg) => {
    if (msg.startServer) {
        startServer(msg.startServer);
    }
});
process.on('unhandledRejection', (e) => {
    console.log(e);
});
