"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "installDevValidationWorker", {
    enumerable: true,
    get: function() {
        return installDevValidationWorker;
    }
});
const _jestworker = require("next/dist/compiled/jest-worker");
const _devvalidationworkerglobals = require("../app-render/dev-validation-worker-globals");
const _requirecache = require("./require-cache");
const _utils = require("../lib/utils");
const _needsexperimentalreact = require("../../lib/needs-experimental-react");
function installDevValidationWorker(options) {
    const { distDir, buildId, deploymentId, nextConfig } = options;
    // A single worker, not a pool. Validation for one navigation runs its depth
    // loop sequentially, and a newer navigation supersedes the previous one
    // (aborting it mid-run) rather than running concurrently, so there's no
    // per-request fan-out to parallelize (unlike the `'use cache'` probe, where
    // one request fans out into concurrent probes). One worker already frees the
    // main thread, which is the whole point; it also keeps each request's CLI
    // marker block contiguous in the piped output. Torn down on HMR (stale user
    // modules) and on crash.
    //
    // TODO(dev-validation-worker): raise `numWorkers` if concurrent navigations
    // across independent requests (e.g. multiple tabs) show a validation-latency
    // tail.
    let pool;
    const getPool = ()=>{
        if (pool) {
            return pool;
        }
        // Strip `--inspect` from any inherited `NODE_OPTIONS` so the worker doesn't
        // fight the parent for the same debug port.
        const workerNodeOptions = (0, _utils.getFormattedNodeOptionsWithoutInspect)();
        // The worker is shipped as four pre-bundled dev-only artifacts
        // ({webpack,turbopack} × {stable,experimental}), one per combination of the
        // user's bundler and vendored React channel. Pick the matching artifact
        // from runtime env so the worker stays in lockstep with the user's app
        // bundle. `needsExperimentalReact` is the same predicate `define-env.ts`
        // uses to wire `__NEXT_EXPERIMENTAL_REACT`.
        const turbo = process.env.TURBOPACK ? '-turbo' : '';
        const channel = (0, _needsexperimentalreact.needsExperimentalReact)(nextConfig) ? '-experimental' : '';
        const workerPath = require.resolve(`next/dist/compiled/next-server/dev-validation-worker${turbo}${channel}.runtime.dev.js`);
        const worker = new _jestworker.Worker(workerPath, {
            maxRetries: 0,
            numWorkers: 1,
            // Always worker-threads, regardless of `experimental.workerThreads`.
            // Unlike the `'use cache'` probe (which follows the flag), validation has
            // no reason to prefer a child process: it doesn't need process-level
            // isolation (a worker thread already has its own V8 heap and module
            // registry, so the reloaded route is isolated from the main thread), and
            // threads let a superseded validation be aborted mid-run through a shared
            // `SharedArrayBuffer`, which a separate process can't receive. Threads
            // also carry the transported Flight bytes as typed arrays via structured
            // clone, with no JSON round-trip to corrupt them.
            enableWorkerThreads: true,
            // Listing the method explicitly tells jest-worker to skip the discovery
            // `require()` it would otherwise do in the parent process to enumerate
            // the module's exports. This worker's top-level imports (`require-hook`,
            // `node-environment`) run runtime setup meant only for the isolated
            // worker thread, so they must not be evaluated in the parent.
            exposedMethods: [
                'runDevValidation'
            ],
            forkOptions: {
                env: {
                    ...process.env,
                    NODE_OPTIONS: workerNodeOptions
                }
            }
        });
        worker.getStdout().pipe(process.stdout);
        worker.getStderr().pipe(process.stderr);
        pool = worker;
        return worker;
    };
    const tearDownPool = async ()=>{
        const current = pool;
        if (!current) {
            return;
        }
        pool = undefined;
        await current.end().catch(()=>{
        // The worker thread exits on its own once its work settles; a failed
        // `.end()` here just means we couldn't wait for it cleanly.
        });
    };
    const runValidation = async (snapshot, validationAbortSignal)=>{
        let activePool;
        try {
            activePool = getPool();
        } catch  {
            return null;
        }
        const message = {
            ...snapshot,
            distDir,
            buildId,
            deploymentId,
            nextConfigSerializable: {
                httpAgentOptions: nextConfig.httpAgentOptions,
                cacheLifeProfiles: nextConfig.cacheLife,
                useCacheTimeout: nextConfig.experimental.useCacheTimeout,
                staticPageGenerationTimeout: nextConfig.staticPageGenerationTimeout
            }
        };
        // The worker runs as a thread (see `enableWorkerThreads` above), so an
        // abort reaches it through a one-slot shared flag rather than the abort
        // signal directly (a signal can't cross the thread boundary). Mirror an
        // abort of `validationAbortSignal` into the buffer and wake the worker's
        // `Atomics.waitAsync` on it, so it aborts the in-flight run at its next
        // depth boundary.
        const abortBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
        const abortFlag = new Int32Array(abortBuffer);
        const propagateAbort = ()=>{
            Atomics.store(abortFlag, 0, 1);
            Atomics.notify(abortFlag, 0);
        };
        if (validationAbortSignal.aborted) {
            propagateAbort();
        } else {
            validationAbortSignal.addEventListener('abort', propagateAbort, {
                once: true
            });
        }
        try {
            return await activePool.runDevValidation(message, abortBuffer);
        } catch  {
            // Worker crash or IPC error: tear down so the next validation starts
            // fresh. The main thread treats a missing result as "nothing to deliver."
            await tearDownPool();
            return null;
        } finally{
            // `once` only auto-removes the listener if it fired, so remove it
            // explicitly to bound its lifetime to this run when validation completed
            // without being superseded.
            validationAbortSignal.removeEventListener('abort', propagateAbort);
        }
    };
    // The dev server can't reach into the worker to clear its `require.cache` or
    // manifest caches, so we drop the worker whenever the parent's caches are
    // invalidated (HMR, route recompile). The next validation lazy-spawns a fresh
    // worker with empty caches.
    (0, _requirecache.onCacheInvalidation)(()=>{
        void tearDownPool();
    });
    (0, _devvalidationworkerglobals.setDevValidationWorker)(runValidation);
}

//# sourceMappingURL=dev-validation-worker-pool.js.map