UNPKG

10.4 kBTypeScriptView Raw
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7/// <reference types="node" />
8
9import type {ForkOptions} from 'child_process';
10import type {ResourceLimits} from 'worker_threads';
11
12declare const CHILD_MESSAGE_CALL = 1;
13
14declare const CHILD_MESSAGE_CALL_SETUP = 4;
15
16declare const CHILD_MESSAGE_END = 2;
17
18declare const CHILD_MESSAGE_INITIALIZE = 0;
19
20declare const CHILD_MESSAGE_MEM_USAGE = 3;
21
22declare type ChildMessage =
23 | ChildMessageInitialize
24 | ChildMessageCall
25 | ChildMessageEnd
26 | ChildMessageMemUsage
27 | ChildMessageCallSetup;
28
29declare type ChildMessageCall = [
30 type: typeof CHILD_MESSAGE_CALL,
31 isProcessed: boolean,
32 methodName: string,
33 args: Array<unknown>,
34];
35
36declare type ChildMessageCallSetup = [type: typeof CHILD_MESSAGE_CALL_SETUP];
37
38declare type ChildMessageEnd = [
39 type: typeof CHILD_MESSAGE_END,
40 isProcessed: boolean,
41];
42
43declare type ChildMessageInitialize = [
44 type: typeof CHILD_MESSAGE_INITIALIZE,
45 isProcessed: boolean,
46 fileName: string,
47 setupArgs: Array<unknown>,
48 workerId: string | undefined,
49];
50
51declare type ChildMessageMemUsage = [type: typeof CHILD_MESSAGE_MEM_USAGE];
52
53declare type ComputeTaskPriorityCallback = (
54 method: string,
55 ...args: Array<unknown>
56) => number;
57
58declare type ExcludeReservedKeys<K> = Exclude<K, ReservedKeys>;
59
60/**
61 * First-in, First-out task queue that manages a dedicated pool
62 * for each worker as well as a shared queue. The FIFO ordering is guaranteed
63 * across the worker specific and shared queue.
64 */
65export declare class FifoQueue implements TaskQueue {
66 private _workerQueues;
67 private readonly _sharedQueue;
68 enqueue(task: QueueChildMessage, workerId?: number): void;
69 dequeue(workerId: number): QueueChildMessage | null;
70}
71
72declare type FunctionLike = (...args: any) => unknown;
73
74declare type HeapItem = {
75 priority: number;
76};
77
78export declare type JestWorkerFarm<T extends Record<string, unknown>> =
79 Worker_2 & WorkerModule<T>;
80
81export declare function messageParent(
82 message: unknown,
83 parentProcess?: NodeJS.Process,
84): void;
85
86declare type MethodLikeKeys<T> = {
87 [K in keyof T]: T[K] extends FunctionLike ? K : never;
88}[keyof T];
89
90declare class MinHeap<TItem extends HeapItem> {
91 private readonly _heap;
92 peek(): TItem | null;
93 add(item: TItem): void;
94 poll(): TItem | null;
95}
96
97declare type OnCustomMessage = (message: Array<unknown> | unknown) => void;
98
99declare type OnEnd = (err: Error | null, result: unknown) => void;
100
101declare type OnStart = (worker: WorkerInterface) => void;
102
103declare type OnStateChangeHandler = (
104 state: WorkerStates,
105 oldState: WorkerStates,
106) => void;
107
108declare type PoolExitResult = {
109 forceExited: boolean;
110};
111
112/**
113 * Priority queue that processes tasks in natural ordering (lower priority first)
114 * according to the priority computed by the function passed in the constructor.
115 *
116 * FIFO ordering isn't guaranteed for tasks with the same priority.
117 *
118 * Worker specific tasks with the same priority as a non-worker specific task
119 * are always processed first.
120 */
121export declare class PriorityQueue implements TaskQueue {
122 private readonly _computePriority;
123 private _queue;
124 private readonly _sharedQueue;
125 constructor(_computePriority: ComputeTaskPriorityCallback);
126 enqueue(task: QueueChildMessage, workerId?: number): void;
127 _enqueue(task: QueueChildMessage, queue: MinHeap<QueueItem>): void;
128 dequeue(workerId: number): QueueChildMessage | null;
129 _getWorkerQueue(workerId: number): MinHeap<QueueItem>;
130}
131
132export declare interface PromiseWithCustomMessage<T> extends Promise<T> {
133 UNSTABLE_onCustomMessage?: (listener: OnCustomMessage) => () => void;
134}
135
136declare type Promisify<T extends FunctionLike> = ReturnType<T> extends Promise<
137 infer R
138>
139 ? (...args: Parameters<T>) => Promise<R>
140 : (...args: Parameters<T>) => Promise<ReturnType<T>>;
141
142declare type QueueChildMessage = {
143 request: ChildMessageCall;
144 onStart: OnStart;
145 onEnd: OnEnd;
146 onCustomMessage: OnCustomMessage;
147};
148
149declare type QueueItem = {
150 task: QueueChildMessage;
151 priority: number;
152};
153
154declare type ReservedKeys =
155 | 'end'
156 | 'getStderr'
157 | 'getStdout'
158 | 'setup'
159 | 'teardown';
160
161export declare interface TaskQueue {
162 /**
163 * Enqueues the task in the queue for the specified worker or adds it to the
164 * queue shared by all workers
165 * @param task the task to queue
166 * @param workerId the id of the worker that should process this task or undefined
167 * if there's no preference.
168 */
169 enqueue(task: QueueChildMessage, workerId?: number): void;
170 /**
171 * Dequeues the next item from the queue for the specified worker
172 * @param workerId the id of the worker for which the next task should be retrieved
173 */
174 dequeue(workerId: number): QueueChildMessage | null;
175}
176
177/**
178 * The Jest farm (publicly called "Worker") is a class that allows you to queue
179 * methods across multiple child processes, in order to parallelize work. This
180 * is done by providing an absolute path to a module that will be loaded on each
181 * of the child processes, and bridged to the main process.
182 *
183 * Bridged methods are specified by using the "exposedMethods" property of the
184 * "options" object. This is an array of strings, where each of them corresponds
185 * to the exported name in the loaded module.
186 *
187 * You can also control the amount of workers by using the "numWorkers" property
188 * of the "options" object, and the settings passed to fork the process through
189 * the "forkOptions" property. The amount of workers defaults to the amount of
190 * CPUS minus one.
191 *
192 * Queueing calls can be done in two ways:
193 * - Standard method: calls will be redirected to the first available worker,
194 * so they will get executed as soon as they can.
195 *
196 * - Sticky method: if a "computeWorkerKey" method is provided within the
197 * config, the resulting string of this method will be used as a key.
198 * Every time this key is returned, it is guaranteed that your job will be
199 * processed by the same worker. This is specially useful if your workers
200 * are caching results.
201 */
202declare class Worker_2 {
203 private _ending;
204 private readonly _farm;
205 private readonly _options;
206 private readonly _workerPool;
207 constructor(workerPath: string | URL, options?: WorkerFarmOptions);
208 private _bindExposedWorkerMethods;
209 private _callFunctionWithArgs;
210 getStderr(): NodeJS.ReadableStream;
211 getStdout(): NodeJS.ReadableStream;
212 start(): Promise<void>;
213 end(): Promise<PoolExitResult>;
214}
215export {Worker_2 as Worker};
216
217declare type WorkerCallback = (
218 workerId: number,
219 request: ChildMessage,
220 onStart: OnStart,
221 onEnd: OnEnd,
222 onCustomMessage: OnCustomMessage,
223) => void;
224
225declare enum WorkerEvents {
226 STATE_CHANGE = 'state-change',
227}
228
229export declare type WorkerFarmOptions = {
230 computeWorkerKey?: (method: string, ...args: Array<unknown>) => string | null;
231 enableWorkerThreads?: boolean;
232 exposedMethods?: ReadonlyArray<string>;
233 forkOptions?: ForkOptions;
234 maxRetries?: number;
235 numWorkers?: number;
236 resourceLimits?: ResourceLimits;
237 setupArgs?: Array<unknown>;
238 taskQueue?: TaskQueue;
239 WorkerPool?: new (
240 workerPath: string,
241 options?: WorkerPoolOptions,
242 ) => WorkerPoolInterface;
243 workerSchedulingPolicy?: WorkerSchedulingPolicy;
244 idleMemoryLimit?: number;
245};
246
247declare interface WorkerInterface {
248 get state(): WorkerStates;
249 send(
250 request: ChildMessage,
251 onProcessStart: OnStart,
252 onProcessEnd: OnEnd,
253 onCustomMessage: OnCustomMessage,
254 ): void;
255 waitForExit(): Promise<void>;
256 forceExit(): void;
257 getWorkerId(): number;
258 getStderr(): NodeJS.ReadableStream | null;
259 getStdout(): NodeJS.ReadableStream | null;
260 /**
261 * Some system level identifier for the worker. IE, process id, thread id, etc.
262 */
263 getWorkerSystemId(): number;
264 getMemoryUsage(): Promise<number | null>;
265 /**
266 * Checks to see if the child worker is actually running.
267 */
268 isWorkerRunning(): boolean;
269 /**
270 * When the worker child is started and ready to start handling requests.
271 *
272 * @remarks
273 * This mostly exists to help with testing so that you don't check the status
274 * of things like isWorkerRunning before it actually is.
275 */
276 waitForWorkerReady(): Promise<void>;
277}
278
279declare type WorkerModule<T> = {
280 [K in keyof T as Extract<
281 ExcludeReservedKeys<K>,
282 MethodLikeKeys<T>
283 >]: T[K] extends FunctionLike ? Promisify<T[K]> : never;
284};
285
286declare type WorkerOptions_2 = {
287 forkOptions: ForkOptions;
288 resourceLimits: ResourceLimits;
289 setupArgs: Array<unknown>;
290 maxRetries: number;
291 workerId: number;
292 workerData?: unknown;
293 workerPath: string;
294 /**
295 * After a job has executed the memory usage it should return to.
296 *
297 * @remarks
298 * Note this is different from ResourceLimits in that it checks at idle, after
299 * a job is complete. So you could have a resource limit of 500MB but an idle
300 * limit of 50MB. The latter will only trigger if after a job has completed the
301 * memory usage hasn't returned back down under 50MB.
302 */
303 idleMemoryLimit?: number;
304 /**
305 * This mainly exists so the path can be changed during testing.
306 * https://github.com/jestjs/jest/issues/9543
307 */
308 childWorkerPath?: string;
309 /**
310 * This is useful for debugging individual tests allowing you to see
311 * the raw output of the worker.
312 */
313 silent?: boolean;
314 /**
315 * Used to immediately bind event handlers.
316 */
317 on?: {
318 [WorkerEvents.STATE_CHANGE]:
319 | OnStateChangeHandler
320 | ReadonlyArray<OnStateChangeHandler>;
321 };
322};
323
324export declare interface WorkerPoolInterface {
325 getStderr(): NodeJS.ReadableStream;
326 getStdout(): NodeJS.ReadableStream;
327 getWorkers(): Array<WorkerInterface>;
328 createWorker(options: WorkerOptions_2): WorkerInterface;
329 send: WorkerCallback;
330 start(): Promise<void>;
331 end(): Promise<PoolExitResult>;
332}
333
334export declare type WorkerPoolOptions = {
335 setupArgs: Array<unknown>;
336 forkOptions: ForkOptions;
337 resourceLimits: ResourceLimits;
338 maxRetries: number;
339 numWorkers: number;
340 enableWorkerThreads: boolean;
341 idleMemoryLimit?: number;
342};
343
344declare type WorkerSchedulingPolicy = 'round-robin' | 'in-order';
345
346declare enum WorkerStates {
347 STARTING = 'starting',
348 OK = 'ok',
349 OUT_OF_MEMORY = 'oom',
350 RESTARTING = 'restarting',
351 SHUTTING_DOWN = 'shutting-down',
352 SHUT_DOWN = 'shut-down',
353}
354
355export {};