"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
0 && (module.exports = {
    Worker: null,
    getNextBuildDebuggerPortOffset: null
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    Worker: function() {
        return Worker;
    },
    getNextBuildDebuggerPortOffset: function() {
        return getNextBuildDebuggerPortOffset;
    }
});
const _jestworker = require("next/dist/compiled/jest-worker");
const _stream = require("stream");
const _utils = require("../server/lib/utils");
const RESTARTED = Symbol('restarted');
const cleanupWorkers = (worker)=>{
    var _worker__workerPool;
    for (const curWorker of ((_worker__workerPool = worker._workerPool) == null ? void 0 : _worker__workerPool._workers) || []){
        var _curWorker__child;
        (_curWorker__child = curWorker._child) == null ? void 0 : _curWorker__child.kill('SIGINT');
    }
};
function getNextBuildDebuggerPortOffset(_) {
    // 0: export worker
    return 0;
}
class Worker {
    constructor(workerPath, options){
        let { enableSourceMaps, timeout, onRestart, logger = console, debuggerPortOffset, isolatedMemory, onActivity, onActivityAbort, ...farmOptions } = options;
        this._onActivity = onActivity;
        this._onActivityAbort = onActivityAbort;
        let restartPromise;
        let resolveRestartPromise;
        let activeTasks = 0;
        this._worker = undefined;
        // ensure we end workers if they weren't before exit
        process.on('exit', ()=>{
            this.close();
        });
        const nodeOptions = (0, _utils.getParsedNodeOptions)();
        const originalOptions = {
            ...nodeOptions
        };
        delete nodeOptions.inspect;
        delete nodeOptions['inspect-brk'];
        delete nodeOptions['inspect_brk'];
        if (debuggerPortOffset !== -1) {
            const nodeDebugType = (0, _utils.getNodeDebugType)(originalOptions);
            if (nodeDebugType) {
                const debuggerAddress = (0, _utils.getParsedDebugAddress)(originalOptions[nodeDebugType]);
                const address = {
                    host: debuggerAddress.host,
                    // current process runs on `address.port`
                    port: debuggerAddress.port === 0 ? 0 : debuggerAddress.port + 1 + debuggerPortOffset
                };
                nodeOptions[nodeDebugType] = (0, _utils.formatDebugAddress)(address);
            }
        }
        if (enableSourceMaps) {
            nodeOptions['enable-source-maps'] = true;
        }
        if (isolatedMemory) {
            delete nodeOptions['max-old-space-size'];
            delete nodeOptions['max_old_space_size'];
        }
        const createWorker = ()=>{
            var _farmOptions_forkOptions;
            const workerEnv = {
                ...process.env,
                ...((_farmOptions_forkOptions = farmOptions.forkOptions) == null ? void 0 : _farmOptions_forkOptions.env) || {},
                IS_NEXT_WORKER: 'true',
                NODE_OPTIONS: (0, _utils.formatNodeOptions)(nodeOptions)
            };
            if (workerEnv.FORCE_COLOR === undefined) {
                var _process_stderr;
                // Mirror the enablement heuristic from picocolors (see https://github.com/vercel/next.js/blob/6a40da0345939fe4f7b1ae519b296a86dd103432/packages/next/src/lib/picocolors.ts#L21-L24).
                // Picocolors snapshots `process.env`/`stdout.isTTY` at module load time, so when the worker
                // process bootstraps with piped stdio its own check would disable colors. Re-evaluating the
                // same conditions here lets us opt the worker into color output only when the parent would
                // have seen colors, while still respecting explicit opt-outs like NO_COLOR.
                const supportsColors = !workerEnv.NO_COLOR && !workerEnv.CI && workerEnv.TERM !== 'dumb' && (process.stdout.isTTY || ((_process_stderr = process.stderr) == null ? void 0 : _process_stderr.isTTY));
                if (supportsColors) {
                    workerEnv.FORCE_COLOR = '1';
                }
            }
            this._worker = new _jestworker.Worker(workerPath, {
                ...farmOptions,
                forkOptions: {
                    ...farmOptions.forkOptions,
                    env: workerEnv
                },
                maxRetries: 0
            });
            restartPromise = new Promise((resolve)=>resolveRestartPromise = resolve);
            /**
       * Jest Worker has two worker types, ChildProcessWorker (uses child_process) and NodeThreadWorker (uses worker_threads)
       * Next.js uses ChildProcessWorker by default, but it can be switched to NodeThreadWorker with an experimental flag
       *
       * We only want to handle ChildProcessWorker's orphan process issue, so we access the private property "_child":
       * https://github.com/facebook/jest/blob/b38d7d345a81d97d1dc3b68b8458b1837fbf19be/packages/jest-worker/src/workers/ChildProcessWorker.ts
       *
       * But this property is not available in NodeThreadWorker, so we need to check if we are using ChildProcessWorker
       */ if (!farmOptions.enableWorkerThreads) {
                var _this__worker__workerPool;
                for (const worker of ((_this__worker__workerPool = this._worker._workerPool) == null ? void 0 : _this__worker__workerPool._workers) || []){
                    var _worker__child, // if a child process emits a particular message, we track that as activity
                    // so the parent process can keep track of progress
                    _worker__child1;
                    (_worker__child = worker._child) == null ? void 0 : _worker__child.on('exit', (code, signal)=>{
                        if ((code || signal && signal !== 'SIGINT') && this._worker) {
                            logger.error(`Next.js build worker exited with code: ${code} and signal: ${signal}`);
                            // if a child process doesn't exit gracefully, we want to bubble up the exit code to the parent process
                            process.exit(code ?? 1);
                        }
                    });
                    (_worker__child1 = worker._child) == null ? void 0 : _worker__child1.on('message', ([, data])=>{
                        if (data && typeof data === 'object' && 'type' in data && data.type === 'activity') {
                            onActivityImpl();
                        }
                    });
                }
            }
            let aborted = false;
            const onActivityAbortImpl = ()=>{
                if (!aborted) {
                    this._onActivityAbort == null ? void 0 : this._onActivityAbort.call(this);
                    aborted = true;
                }
            };
            // Listen to the worker's stdout and stderr, if there's any thing logged, abort the activity first
            const abortActivityStreamOnLog = new _stream.Transform({
                transform (_chunk, _encoding, callback) {
                    onActivityAbortImpl();
                    callback();
                }
            });
            // Stop the activity if there's any output from the worker
            this._worker.getStdout().pipe(abortActivityStreamOnLog);
            this._worker.getStderr().pipe(abortActivityStreamOnLog);
            // Pipe the worker's stdout and stderr to the parent process
            this._worker.getStdout().pipe(process.stdout);
            this._worker.getStderr().pipe(process.stderr);
        };
        createWorker();
        const onHanging = ()=>{
            const worker = this._worker;
            if (!worker) return;
            const resolve = resolveRestartPromise;
            createWorker();
            logger.warn(`Sending SIGTERM signal to static worker due to timeout${timeout ? ` of ${timeout / 1000} seconds` : ''}. Subsequent errors may be a result of the worker exiting.`);
            worker.end().then(()=>{
                resolve(RESTARTED);
            });
        };
        let hangingTimer = false;
        const onActivityImpl = ()=>{
            if (hangingTimer) clearTimeout(hangingTimer);
            if (this._onActivity) this._onActivity();
            hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout);
        };
        for (const method of farmOptions.exposedMethods){
            if (method.startsWith('_')) continue;
            this[method] = timeout ? async (...args)=>{
                activeTasks++;
                try {
                    let attempts = 0;
                    for(;;){
                        onActivityImpl();
                        const result = await Promise.race([
                            this._worker[method](...args),
                            restartPromise
                        ]);
                        if (result !== RESTARTED) return result;
                        if (onRestart) onRestart(method, args, ++attempts);
                    }
                } finally{
                    activeTasks--;
                    onActivityImpl();
                }
            } : this._worker[method].bind(this._worker);
        }
    }
    setOnActivity(onActivity) {
        this._onActivity = onActivity;
    }
    setOnActivityAbort(onActivityAbort) {
        this._onActivityAbort = onActivityAbort;
    }
    end() {
        const worker = this._worker;
        if (!worker) {
            throw Object.defineProperty(new Error('Farm is ended, no more calls can be done to it'), "__NEXT_ERROR_CODE", {
                value: "E265",
                enumerable: false,
                configurable: true
            });
        }
        cleanupWorkers(worker);
        this._worker = undefined;
        return worker.end();
    }
    /**
   * Quietly end the worker if it exists
   */ close() {
        if (this._worker) {
            cleanupWorkers(this._worker);
            this._worker.end();
        }
    }
}

//# sourceMappingURL=worker.js.map