/// <reference types="node" />
import EventEmitter from 'events';
import Worker from './worker';
/**
 * Thrown when the worker pool is not started
 */
export declare class NotStartedError extends Error {
    constructor();
}
export { NotReadyError, WorkerError, UnexpectedExitError } from './worker';
interface WorkerPoolOptions {
    cwd?: string;
    args?: string[];
    env?: any;
    min?: number;
    max?: number;
    idleTimeout?: number;
    stopTimeout?: number;
    stopSignal?: 'SIGTERM' | 'SIGINT' | 'SIGHUP' | 'SIGKILL';
    strategy?: 'fewest' | 'fill' | 'round-robin' | 'random';
    full?: number;
    start?: boolean;
}
/**
 * Provides a load-balancing and (optionally) auto-scaling pool of worker
 * processes and the ability to request for worker processes to import modules,
 * call their exported functions, and reply with their return values and thrown
 * exceptions. Load balancing and auto-scaling are configurable via min/max
 * limits, strategies, and timeouts.
 * @extends EventEmitter
 */
export default class WorkerPool extends EventEmitter {
    /**
     * Emitted when an error is thrown in the constructor.
     * @event WorkerPool#error
     * @type {Error} - The error object that was thrown.
     */
    /**
     * Emitted when the worker pool starts.
     * @event WorkerPool#start
     */
    /**
     * Emitted when the worker pool recycles.
     * @event WorkerPool#recycle
     */
    /**
     * Emitted when the worker pool stops.
     * @event WorkerPool#stop
     */
    private _cwd?;
    private _args?;
    private _env?;
    private _min;
    private _max;
    private _idleTimeout;
    private _stopTimeout;
    private _stopSignal;
    private _strategy;
    private _full;
    private _workers;
    private _stopping;
    private _round;
    /**
     * The current working directory for worker processes. Takes effect after start/recycle.
     *
     * @example
     *
     * workerPool.cwd = `${versionPath}/workers`;
     *
     * await workerPool.recycle();
     *
     * @type {string}
     */
    get cwd(): string | undefined;
    set cwd(value: string | undefined);
    /**
     * Arguments to pass to worker processes. Takes effect after start/recycle.
     *
     * @example
     *
     * workerPool.args = [ '--verbose' ];
     *
     * await workerPool.recycle();
     *
     * @type {string[]}
     */
    get args(): string[] | undefined;
    set args(value: string[] | undefined);
    /**
     * Environmental variables to set for worker processes. Takes effect after start/recycle.
     *
     * @example
     *
     * workerPool.env = { TOKEN: newToken };
     *
     * await workerPool.recycle();
     *
     * @type {Object}
     */
    get env(): any;
    set env(value: any);
    /**
     * True if the worker pool is stopping
     * @type {boolean}
     */
    get isStopping(): boolean;
    /**
     * True if the worker pool has stopped
     * @type {boolean}
     */
    get isStopped(): boolean;
    /**
     * True if the worker pool has started
     * @type {boolean}
     */
    get isStarted(): boolean;
    /**
     * Gets the current number of worker processes
     * @returns {Number} The current number of worker processes
     */
    getProcessCount(): number;
    /**
     * @example
     *
     * const workerPool = new WorkerPool(
     *   cwd: `${versionPath}/workers`,
     *   args: [ '--verbose' ],
     *   env: { TOKEN: token },
     *   min: 1,
     *   max: 4,
     *   idleTimeout: 30000,
     *   stopTimeout: 1000,
     *   stopSignal: 'SIGINT'
     *   strategy: 'fill',
     *   full: 100
     * });
     *
     * @param {Object} options={} - Optional parameters
     * @param {string} options.cwd - The current working directory for worker processes
     * @param {string[]} options.args - Arguments to pass to worker processes
     * @param {Object} options.env - Environmental variables to set for worker processes
     * @param {number} options.min=0 - The minimum number of worker processes in the pool
     * @param {number} options.max=3 - The maximum number of worker processes in the pool
     * @param {number} options.idleTimeout=10000 - Milliseconds before an idle worker process will be asked to stop via options.stopSignal
     * @param {number} options.stopTimeout=10000 - Milliseconds before a worker process will receive SIGKILL after it has been asked to stop
     * @param {'SIGTERM'|'SIGINT'|'SIGHUP'|'SIGKILL'} options.stopSignal='SIGTERM' - Initial signal to send when stopping worker processes
     * @param {'fewest'|'fill'|'round-robin'|'random'} options.strategy='fewest' - The strategy to use when routing calls to workers
     * @param {number} options.full=10 - The number of requests per worker used by the 'fill' strategy
     * @param {boolean} options.start=true - Whether to automatically start this worker pool
     */
    constructor({ cwd, args, env, min, max, idleTimeout, stopTimeout, stopSignal, strategy, full, start }?: WorkerPoolOptions);
    /**
     * Starts the worker pool
     * @returns {Promise}
     * @resolves When the worker pool has started
     * @rejects {WorkerPool.NotReadyError | Error} When an error has been thrown
     */
    start(): Promise<void>;
    /**
     * Stops the worker pool, gracefully shutting down each worker process
     * @returns {Promise}
     * @resolves When the worker pool has stopped
     * @rejects {Error} When an error has been thrown
     */
    stop(): Promise<void>;
    /**
     * Recycle the worker pool, gracefully shutting down existing worker processes
     * and starting up new worker processes
     * @returns {Promise}
     * @resolves When the worker pool has recycled
     * @rejects {WorkerPool.NotReadyError | Error} When an error has been thrown
     */
    recycle(): Promise<void>;
    _createWorkers(): void;
    _startMinWorkers(): Promise<void[]>;
    _stop(workers: Worker[]): Promise<void>;
    /**
     * Routes a request to a worker in the pool asking it to import a module and call a function with the provided arguments.
     *
     * **Note**: WorkerPool#call() uses JSON serialization to communicate with worker processes, so only types/objects that can survive JSON.stringify()/JSON.parse() will be passed through unchanged.
     *
     * @example
     *
     * const result = await workerPool.call('user-module', 'hashPassword', password, salt);
     *
     * @param {string} modulePath - The module path for the worker process to import
     * @param {string} functionName - The name of a function expored by the imported module
     * @param {...any} args - Arguments to pass when calling the function
     * @returns {Promise}
     * @resolves {any} The return value of the function call when the call returns
     * @rejects {WorkerPool.UnexpectedExitError | WorkerPool.WorkerError | Error} When an error has been thrown
     */
    call(modulePath: string, functionName: string, ...args: any[]): Promise<unknown>;
    /**
     * Creates a proxy function that will call WorkerPool#call() with the provided module path, function name, and arguments. Provided as a convenience and minor performance improvement as the modulePath will only be resolved when creating the proxy, rather than with each call.
     *
     * **Note**: WorkerPool#proxy() uses JSON serialization to communicate with worker processes, so only types/objects that can survive JSON.stringify()/JSON.parse() will be passed through unchanged.
     *
     * @example
     *
     * const hashPassword = workerPool.proxy('user-module', 'hashPassword');
     *
     * const hashedPassword = await hashPassword(password, salt);
     *
     * @param {string} modulePath - The module path for the worker process to import
     * @param {string} functionName - The name of a function expored by the imported module
     * @returns {Function} A function that calls WorkerPool#call() with the provided modulePath, functionName, and args, and returns its Promise
     */
    proxy(modulePath: string, functionName: string): (...args: any[]) => Promise<unknown>;
    _call(resolvedModulePath: string, functionName: string, args: any[]): Promise<unknown>;
    _resolve(modulePath: string): string;
    _getWorker(): Worker;
    /**
     * Return the worker with the fewest number of waiting requests, favoring
     * workers that are already started
     * @private
     */
    _fewestStrategy(): Worker;
    /**
     * Return the first worker that is not full, or if they are all full, the
     * worker with the fewest number of queued requests. This does not prevent
     * workers from overfilling. It will fill each worker before moving on to
     * the next, and will fall back to the "fewest" strategy when all workers
     * are full.
     * @private
     */
    _fillStrategy(): Worker;
    /**
     * Return the next worker in the sequence
     * @private
     */
    _roundRobinStrategy(): Worker;
    /**
     * Return a random worker
     * @private
     */
    _randomStrategy(): Worker;
    _stopWhenIdle(): boolean;
}
