var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
    if (typeof path === "string" && /^\.\.?\//.test(path)) {
        return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
            return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
        });
    }
    return path;
};
import { ChildProcess, fork, } from 'node:child_process';
import EventEmitter from 'node:events';
import { platform } from 'node:os';
import path from 'node:path';
import { pid } from 'node:process';
/**
 * Central registry of message type names, which are used to identify
 * the payload in {@link Message} objects sent between processes. The
 * payloads themselves are implementation specific and defined in each
 * component; only the type name is reserved here to avoid collisions.
 *
 * Receiving logic can call {@link getMessage()} with the name of
 * the message of interest to filter messages to those of interest.
 */
export const MESSAGE_TYPES = {
    handoff: 'handoff',
    status: 'status',
    subscribe: 'subscribe',
    notify: 'notify',
    ready: 'ready',
};
function getMessage(type, data) {
    if (Array.isArray(data) && data.length === 2 && data[0] === type) {
        return data[1];
    }
    return null;
}
function onMessageType(e, type, handler) {
    return e.on('message', (data, sendHandle) => {
        const msg = getMessage(type, data);
        if (msg) {
            handler(msg, sendHandle);
        }
    });
}
function onceMessageType(e, type, handler) {
    const listener = (data, sendHandle) => {
        const msg = getMessage(type, data);
        if (msg) {
            e.off('message', listener);
            handler(msg, sendHandle);
        }
    };
    return e.on('message', listener);
}
/**
 * Adds the {@link Receiver.onMessageType()} and {@link Receiver.onceMessageType()}
 * methods to convert the given `EventEmitter` to a `Receiver`.
 */
function wrap(proc) {
    return new Proxy(proc, {
        get(target, prop, receiver) {
            switch (prop) {
                case 'onMessageType':
                    return (type, handler) => {
                        onMessageType(target, type, handler);
                        return receiver; // this
                    };
                case 'onceMessageType':
                    return (type, handler) => {
                        onceMessageType(target, type, handler);
                        return receiver; // this
                    };
            }
            return Reflect.get(target, prop, receiver);
        },
    });
}
/**
 * The parentWorker for forked processes, or `null` if the process was not forked.
 * (Analogous to the `parentPort: MessagePort | null` of the `"workers"` library).
 */
export const parentWorker = process.send
    ? wrap(process)
    : null;
const SINGLE_PROCESS = 'SINGLE_PROCESS';
let singleProcessOverride = false;
export function singleProcessMode() {
    return singleProcessOverride || (process.env[SINGLE_PROCESS] ?? '0') !== '0';
}
export function setSingleProcessMode(enabled = true) {
    singleProcessOverride = enabled;
}
/**
 *
 * @param modulePath Path to the module file, relative to zero-cache/src/
 */
export function childWorker(modulePath, env, ...args) {
    const ext = path.extname(import.meta.url);
    // modulePath is .ts. If we have been compiled, it should be changed to .js
    modulePath = modulePath.replace(/\.ts$/, ext);
    const moduleUrl = new URL(`../${modulePath}`, import.meta.url);
    args.push(...process.argv.slice(2));
    if (singleProcessMode()) {
        const [parent, child] = inProcChannel();
        import(__rewriteRelativeImportExtension(moduleUrl.href))
            .then(async ({ default: runWorker }) => {
            try {
                await runWorker(parent, env ?? process.env, ...args);
                child.emit('close', 0);
                return;
            }
            catch (err) {
                child.emit('error', err);
                child.emit('close', -1);
            }
        })
            .catch(err => child.emit('error', err));
        return child;
    }
    const child = fork(moduleUrl, args, {
        // For production / non-windows, set `detached` to `true` so that SIGINT is
        // not automatically propagated and graceful shutdown happens as intended.
        // For Win32, detached: true causes all subprocesses to open in separate
        // terminals and breaks inter-process kill signals, so set it to false.
        detached: platform() !== 'win32',
        serialization: 'advanced', // use structured clone for IPC
        env,
    });
    return wrap(child);
}
/**
 * Creates two connected `Worker` instances such that messages sent to one
 * via the {@link Worker.send()} method are received by the other's
 * `on('message', ...)` handler.
 *
 * This is analogous to the two `MessagePort`s of a `MessageChannel`, and
 * is useful for executing code written for inter-process communication
 * in a single process.
 */
export function inProcChannel() {
    const worker1 = new EventEmitter();
    const worker2 = new EventEmitter();
    const sendTo = (dest) => (message, sendHandle, callback) => {
        dest.emit('message', message, sendHandle);
        if (callback) {
            callback(null);
        }
        return true;
    };
    const kill = (dest) => (signal = 'SIGTERM') => dest.emit(signal, signal);
    return [
        wrap(Object.assign(worker1, { send: sendTo(worker2), kill: kill(worker2), pid })),
        wrap(Object.assign(worker2, { send: sendTo(worker1), kill: kill(worker1), pid })),
    ];
}
//# sourceMappingURL=processes.js.map