poolifier 2.6.3 → 2.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,26 +34,27 @@ With poolifier you can improve your **performance** and resolve problems related
34
34
  Moreover you can execute your tasks using an API designed to improve the **developer experience**.
35
35
  Please consult our [general guidelines](#general-guidance).
36
36
 
37
- - Performance :racehorse: [benchmarks](./benchmarks/README.md)
38
- - Security :bank: :cop: [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=security_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
39
- - Easy to use :couple:
37
+ - Easy to use :white_check_mark:
38
+ - Performance [benchmarks](./benchmarks/README.md) :white_check_mark:
40
39
  - Dynamic pool size :white_check_mark:
41
40
  - Easy switch from a pool to another :white_check_mark:
42
41
  - No runtime dependencies :white_check_mark:
43
42
  - Proper async integration with node async hooks :white_check_mark:
44
- - Support for worker threads and cluster node modules :white_check_mark:
43
+ - Support CommonJS, ESM, and TypeScript :white_check_mark:
44
+ - Support for worker-threads and cluster node modules :white_check_mark:
45
45
  - Support sync and async tasks :white_check_mark:
46
46
  - Tasks distribution strategies :white_check_mark:
47
- - General guidance on pools to use :white_check_mark:
47
+ - General guidance on pool choice :white_check_mark:
48
48
  - Widely tested :white_check_mark:
49
49
  - Error handling out of the box :white_check_mark:
50
50
  - Active community :white_check_mark:
51
- - Code quality :octocat: [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=bugs)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
51
+ - Code quality [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=bugs)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
52
52
  [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=code_smells)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
53
53
  [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
54
54
  [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
55
55
  [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
56
56
  [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=sqale_index)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
57
+ - Code security [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=security_rating)](https://sonarcloud.io/dashboard?id=pioardi_poolifier) [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=pioardi_poolifier)
57
58
 
58
59
  ## Contents
59
60
 
@@ -140,9 +141,9 @@ pool.execute({}).then(res => {
140
141
 
141
142
  You can do the same with the classes ClusterWorker, FixedClusterPool and DynamicClusterPool.
142
143
 
143
- **See examples folder for more details (in particular if you want to use a pool with [multiple worker functions](./examples/multiFunctionExample.js))**.
144
+ **See [examples](./examples/) folder for more details (in particular if you want to use a pool with [multiple worker functions](./examples/multiFunctionExample.js))**.
144
145
 
145
- Remember that workers can only send and receive serializable data.
146
+ Remember that workers can only send and receive structured-cloneable data.
146
147
 
147
148
  ## Node versions
148
149
 
package/lib/index.d.ts CHANGED
@@ -8,13 +8,13 @@ export { Measurements, WorkerChoiceStrategies } from './pools/selection-strategi
8
8
  export type { IWorkerChoiceStrategy, Measurement, MeasurementOptions, MeasurementStatisticsRequirements, StrategyPolicy, TaskStatisticsRequirements, WorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './pools/selection-strategies/selection-strategies-types';
9
9
  export type { WorkerChoiceStrategyContext } from './pools/selection-strategies/worker-choice-strategy-context';
10
10
  export { DynamicThreadPool } from './pools/thread/dynamic';
11
- export { FixedThreadPool, type ThreadPoolOptions, type ThreadWorkerWithMessageChannel } from './pools/thread/fixed';
11
+ export { FixedThreadPool, type ThreadPoolOptions } from './pools/thread/fixed';
12
12
  export type { AbstractWorker } from './worker/abstract-worker';
13
13
  export { ClusterWorker } from './worker/cluster-worker';
14
14
  export { ThreadWorker } from './worker/thread-worker';
15
15
  export { KillBehaviors } from './worker/worker-options';
16
16
  export type { KillBehavior, WorkerOptions } from './worker/worker-options';
17
17
  export type { TaskFunctions, WorkerAsyncFunction, WorkerFunction, WorkerSyncFunction } from './worker/worker-functions';
18
- export type { Draft, MessageValue, PromiseResponseWrapper, TaskError, TaskPerformance, WorkerStatistics } from './utility-types';
18
+ export type { MessageValue, PromiseResponseWrapper, TaskError, TaskPerformance, WorkerStatistics } from './utility-types';
19
19
  export type { CircularArray } from './circular-array';
20
20
  export type { Queue } from './queue';
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("node:events"),t=require("node:cluster"),s=require("node:crypto"),r=require("node:perf_hooks"),i=require("node:os"),o=require("node:worker_threads"),a=require("node:async_hooks");const n=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),h=Object.freeze({cluster:"cluster",thread:"thread"});class u extends e{}const k=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),c=Object.freeze((()=>{})),l={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},d=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},g=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),m=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class p extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class w{items;offset;size;maxSize;constructor(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}}const T=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),f=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class W{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=l){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){e=e??l,this.setTaskStatisticsRequirements(e),this.opts=e}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].workerUsage.elu.active.median:this.pool.workerNodes[e].workerUsage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i.cpus().length)}}class y extends W{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===f.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class S extends W{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class x extends W{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.runTime.aggregate+s.workerUsage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class N extends W{constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class v extends W{taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class E extends W{strategyPolicy={useDynamicWorker:!0};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class I extends W{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class R{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=T.ROUND_ROBIN,s=l){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[T.ROUND_ROBIN,new(E.bind(this))(e,s)],[T.LEAST_USED,new(N.bind(this))(e,s)],[T.LEAST_BUSY,new(x.bind(this))(e,s)],[T.LEAST_ELU,new(v.bind(this))(e,s)],[T.FAIR_SHARE,new(y.bind(this))(e,s)],[T.WEIGHTED_ROUND_ROBIN,new(I.bind(this))(e,s)],[T.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(S.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class b{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new R(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===n.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!g(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??T.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??l,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(T).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!g(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(f).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!g(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.workerUsage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.workerUsage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.tasksQueue.size),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.tasksQueue.maxSize),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const e of this.workerNodes)this.setWorkerNodeTasksUsage(e,this.getWorkerUsage(e.worker)),this.setWorkerStatistics(e.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.workerUsage.tasks.executing))}async execute(e,t){const i=r.performance.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].workerUsage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].workerUsage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].workerUsage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=d(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.performance.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=d(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=d(e.elu.idle.history),e.elu.active.median=d(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===n.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??c),e.on("error",this.opts.errorHandler??c),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(k.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??c),e.on("exit",this.opts.exitHandler??c),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=m.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(t.reject(e.taskError.message),null!=this.emitter&&this.emitter.emit(k.taskError,e.taskError)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(k.busy,this.info),this.type===n.dynamic&&this.full&&this.emitter?.emit(k.full,this.info))}setWorkerNodeTasksUsage(e,t){e.workerUsage=t}pushWorkerNode(e){return this.workerNodes.push({worker:e,workerUsage:this.getWorkerUsage(e),tasksQueue:new w})}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e))}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){return{tasks:this.getTaskStatistics(e),runTime:{aggregate:0,average:0,median:0,history:new p},waitTime:{aggregate:0,average:0,median:0,history:new p},elu:{idle:{aggregate:0,average:0,median:0,history:new p},active:{aggregate:0,average:0,median:0,history:new p},utilization:0}}}getTaskStatistics(e){const t=this.workerNodes[this.getWorkerNodeKey(e)]?.tasksQueue?.size;return{executed:0,executing:0,get queued(){return t??0},failed:0}}}class C extends b{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,t){e.send(t)}registerWorkerMessageListener(e,t){e.on("message",t)}createWorker(){return t.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return n.fixed}get worker(){return h.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class O extends b{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}registerWorkerMessageListener(e,t){e.port2?.on("message",t)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV,...this.opts.workerOptions})}afterWorkerSetup(e){const{port1:t,port2:s}=new o.MessageChannel;e.postMessage({parent:t},[t]),e.port1=t,e.port2=s,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return n.fixed}get worker(){return h.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const q="default",z=6e4,U=m.SOFT;class P extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:U,maxInactiveTime:z}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??z)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??U,this.opts.maxInactiveTime=e.maxInactiveTime??z,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(q,e.bind(this));else{if(!g(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(q,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.parent?this.mainWorker=e.parent:null!=e.kill?(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy()):null!=e.statistics&&(this.statistics=e.statistics)}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??z)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())})).catch(c)}getTaskFunction(e){e=e??q;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return{timestamp:r.performance.now(),...this.statistics.elu&&{elu:r.performance.eventLoopUtilization()}}}endTaskPerformance(e){return{...e,...this.statistics.runTime&&{runTime:r.performance.now()-e.timestamp},...this.statistics.elu&&{elu:r.performance.eventLoopUtilization(e.elu)}}}}exports.ClusterWorker=class extends P{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends C{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends O{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=C,exports.FixedThreadPool=O,exports.KillBehaviors=m,exports.Measurements=f,exports.PoolEvents=k,exports.PoolTypes=n,exports.ThreadWorker=class extends P{constructor(e,t={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=T,exports.WorkerTypes=h;
1
+ "use strict";var e=require("node:events"),t=require("node:cluster"),s=require("node:crypto"),r=require("node:perf_hooks"),i=require("node:os"),o=require("node:worker_threads"),a=require("node:async_hooks");const n=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),h=Object.freeze({cluster:"cluster",thread:"thread"});class u extends e{}const k=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),c=Object.freeze((()=>{})),l={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},d=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},g=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),m=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class p extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class w{items;offset;size;maxSize;constructor(){this.clear()}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}clear(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}}const T=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),f=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class W{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=l){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){this.opts=e??l,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].workerUsage.waitTime.median:this.pool.workerNodes[e].workerUsage.waitTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].workerUsage.elu.active.median:this.pool.workerNodes[e].workerUsage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i.cpus()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i.cpus().length)}}class y extends W{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===f.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class S extends W{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class x extends W{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.runTime.aggregate+s.workerUsage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class N extends W{constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class v extends W{taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class E extends W{strategyPolicy={useDynamicWorker:!0};constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class I extends W{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=l){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class R{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=T.ROUND_ROBIN,s=l){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[T.ROUND_ROBIN,new(E.bind(this))(e,s)],[T.LEAST_USED,new(N.bind(this))(e,s)],[T.LEAST_BUSY,new(x.bind(this))(e,s)],[T.LEAST_ELU,new(v.bind(this))(e,s)],[T.FAIR_SHARE,new(y.bind(this))(e,s)],[T.WEIGHTED_ROUND_ROBIN,new(I.bind(this))(e,s)],[T.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(S.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class b{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new u),this.workerChoiceStrategyContext=new R(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===n.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!g(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??T.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??l,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(T).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!g(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(f).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!g(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.workerUsage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.workerUsage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.maxQueued),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())this.setWorkerNodeTasksUsage(t,this.getWorkerUsage(e)),this.setWorkerStatistics(t.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.workerUsage.tasks.executing))}async execute(e,t){const i=r.performance.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].workerUsage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].workerUsage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].workerUsage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=d(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.performance.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=d(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=d(e.elu.idle.history),e.elu.active.median=d(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===n.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener())}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??c),e.on("error",this.opts.errorHandler??c),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(k.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??c),e.on("exit",this.opts.exitHandler??c),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=m.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(k.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(k.busy,this.info),this.type===n.dynamic&&this.full&&this.emitter?.emit(k.full,this.info))}setWorkerNodeTasksUsage(e,t){e.workerUsage=t}pushWorkerNode(e){this.workerNodes.push({worker:e,workerUsage:this.getWorkerUsage(),tasksQueue:new w});const t=this.getWorkerNodeKey(e);return this.setWorkerNodeTasksUsage(this.workerNodes[t],this.getWorkerUsage(t)),this.workerNodes.length}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}tasksMaxQueueSize(e){return this.workerNodes[e].tasksQueue.maxSize}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].tasksQueue.clear()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){const t=e=>null!=e?this.tasksQueueSize(e):0,s=e=>null!=e?this.tasksMaxQueueSize(e):0;return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{aggregate:0,average:0,median:0,history:new p},waitTime:{aggregate:0,average:0,median:0,history:new p},elu:{idle:{aggregate:0,average:0,median:0,history:new p},active:{aggregate:0,average:0,median:0,history:new p},utilization:0}}}}class C extends b{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return n.fixed}get worker(){return h.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class O extends b{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV,...this.opts.workerOptions})}get type(){return n.fixed}get worker(){return h.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const q="default",z=6e4,U=m.SOFT;class P extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:U,maxInactiveTime:z}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??z)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??U,this.opts.maxInactiveTime=e.maxInactiveTime??z,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(q,e.bind(this));else{if(!g(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(q,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.statistics?this.statistics=e.statistics:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??z)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.performance.now())})).catch(c)}getTaskFunction(e){e=e??q;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return this.checkStatistics(),{timestamp:r.performance.now(),...this.statistics.elu&&{elu:r.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.performance.now()-e.timestamp},...this.statistics.elu&&{elu:r.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}exports.ClusterWorker=class extends P{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends C{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends O{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return n.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=C,exports.FixedThreadPool=O,exports.KillBehaviors=m,exports.Measurements=f,exports.PoolEvents=k,exports.PoolTypes=n,exports.ThreadWorker=class extends P{constructor(e,t={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=T,exports.WorkerTypes=h;
package/lib/index.mjs CHANGED
@@ -1 +1 @@
1
- import e from"node:events";import t from"node:cluster";import s from"node:crypto";import{performance as r}from"node:perf_hooks";import{cpus as i}from"node:os";import{isMainThread as o,Worker as a,SHARE_ENV as n,MessageChannel as h,parentPort as u}from"node:worker_threads";import{AsyncResource as k}from"node:async_hooks";const c=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),d=Object.freeze({cluster:"cluster",thread:"thread"});class l extends e{}const g=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),m=Object.freeze((()=>{})),p={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},w=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},T=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),f=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class W extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class y{items;offset;size;maxSize;constructor(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}}const S=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),x=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class N{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=p){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){e=e??p,this.setTaskStatisticsRequirements(e),this.opts=e}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].workerUsage.elu.active.median:this.pool.workerNodes[e].workerUsage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i().length)}}class v extends N{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===x.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class E extends N{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class I extends N{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.runTime.aggregate+s.workerUsage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class R extends N{constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class b extends N{taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class O extends N{strategyPolicy={useDynamicWorker:!0};constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class C extends N{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=p){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class z{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=S.ROUND_ROBIN,s=p){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[S.ROUND_ROBIN,new(O.bind(this))(e,s)],[S.LEAST_USED,new(R.bind(this))(e,s)],[S.LEAST_BUSY,new(I.bind(this))(e,s)],[S.LEAST_ELU,new(b.bind(this))(e,s)],[S.FAIR_SHARE,new(v.bind(this))(e,s)],[S.WEIGHTED_ROUND_ROBIN,new(C.bind(this))(e,s)],[S.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(E.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class q{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new l),this.workerChoiceStrategyContext=new z(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===c.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!T(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??S.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??p,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(S).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!T(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(x).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!T(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.workerUsage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.workerUsage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.tasksQueue.size),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.tasksQueue.maxSize),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const e of this.workerNodes)this.setWorkerNodeTasksUsage(e,this.getWorkerUsage(e.worker)),this.setWorkerStatistics(e.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.workerUsage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].workerUsage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].workerUsage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].workerUsage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=w(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=w(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=w(e.elu.idle.history),e.elu.active.median=w(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===c.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??m),e.on("error",this.opts.errorHandler??m),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(g.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??m),e.on("exit",this.opts.exitHandler??m),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=f.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(t.reject(e.taskError.message),null!=this.emitter&&this.emitter.emit(g.taskError,e.taskError)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(g.busy,this.info),this.type===c.dynamic&&this.full&&this.emitter?.emit(g.full,this.info))}setWorkerNodeTasksUsage(e,t){e.workerUsage=t}pushWorkerNode(e){return this.workerNodes.push({worker:e,workerUsage:this.getWorkerUsage(e),tasksQueue:new y})}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e))}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){return{tasks:this.getTaskStatistics(e),runTime:{aggregate:0,average:0,median:0,history:new W},waitTime:{aggregate:0,average:0,median:0,history:new W},elu:{idle:{aggregate:0,average:0,median:0,history:new W},active:{aggregate:0,average:0,median:0,history:new W},utilization:0}}}getTaskStatistics(e){const t=this.workerNodes[this.getWorkerNodeKey(e)]?.tasksQueue?.size;return{executed:0,executing:0,get queued(){return t??0},failed:0}}}class U extends q{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,t){e.send(t)}registerWorkerMessageListener(e,t){e.on("message",t)}createWorker(){return t.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return c.fixed}get worker(){return d.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class A extends U{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return c.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class P extends q{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}registerWorkerMessageListener(e,t){e.port2?.on("message",t)}createWorker(){return new a(this.filePath,{env:n,...this.opts.workerOptions})}afterWorkerSetup(e){const{port1:t,port2:s}=new h;e.postMessage({parent:t},[t]),e.port1=t,e.port2=s,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return c.fixed}get worker(){return d.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class Q extends P{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return c.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const D="default",M=6e4,_=f.SOFT;class V extends k{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:_,maxInactiveTime:M}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??M)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??_,this.opts.maxInactiveTime=e.maxInactiveTime??M,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(D,e.bind(this));else{if(!T(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(D,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.parent?this.mainWorker=e.parent:null!=e.kill?(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy()):null!=e.statistics&&(this.statistics=e.statistics)}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??M)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.now())})).catch(m)}getTaskFunction(e){e=e??D;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return{timestamp:r.now(),...this.statistics.elu&&{elu:r.eventLoopUtilization()}}}endTaskPerformance(e){return{...e,...this.statistics.runTime&&{runTime:r.now()-e.timestamp},...this.statistics.elu&&{elu:r.eventLoopUtilization(e.elu)}}}}class L extends V{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}}class j extends V{constructor(e,t={}){super("worker-thread-pool:poolifier",o,e,u,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}}export{L as ClusterWorker,A as DynamicClusterPool,Q as DynamicThreadPool,U as FixedClusterPool,P as FixedThreadPool,f as KillBehaviors,x as Measurements,g as PoolEvents,c as PoolTypes,j as ThreadWorker,S as WorkerChoiceStrategies,d as WorkerTypes};
1
+ import e from"node:events";import t from"node:cluster";import s from"node:crypto";import{performance as r}from"node:perf_hooks";import{cpus as i}from"node:os";import{isMainThread as o,Worker as a,SHARE_ENV as n,parentPort as h}from"node:worker_threads";import{AsyncResource as u}from"node:async_hooks";const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"}),c=Object.freeze({cluster:"cluster",thread:"thread"});class d extends e{}const l=Object.freeze({full:"full",busy:"busy",error:"error",taskError:"taskError"}),g=Object.freeze((()=>{})),m={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},p=e=>{if(Array.isArray(e)&&0===e.length)return 0;if(Array.isArray(e)&&1===e.length)return e[0];const t=e.slice().sort(((e,t)=>e-t));return(t[t.length-1>>1]+t[t.length>>1])/2},w=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),T=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class f extends Array{size;constructor(e=1024,...t){super(),this.checkSize(e),this.size=e,arguments.length>1&&this.push(...t)}push(...e){const t=super.push(...e);return t>this.size&&super.splice(0,t-this.size),this.length}unshift(...e){return super.unshift(...e)>this.size&&super.splice(this.size,e.length),this.length}concat(...e){const t=super.concat(e);return t.size=this.size,t.length>t.size&&t.splice(0,t.length-t.size),t}splice(e,t,...s){let r;return arguments.length>=3&&void 0!==t?(r=super.splice(e,t),this.push(...s)):r=2===arguments.length?super.splice(e,t):super.splice(e),r}resize(e){if(this.checkSize(e),0===e)this.length=0;else if(e<this.size)for(let t=e;t<this.size;t++)super.pop();this.size=e}empty(){return 0===this.length}full(){return this.length===this.size}checkSize(e){if(!Number.isSafeInteger(e))throw new TypeError(`Invalid circular array size: ${e} is not a safe integer`);if(e<0)throw new RangeError(`Invalid circular array size: ${e} < 0`)}}class W{items;offset;size;maxSize;constructor(){this.clear()}enqueue(e){return this.items.push(e),++this.size,this.size>this.maxSize&&(this.maxSize=this.size),this.size}dequeue(){if(this.size<=0)return;const e=this.items[this.offset];return 2*++this.offset>=this.items.length&&(this.items=this.items.slice(this.offset),this.offset=0),--this.size,e}peek(){if(!(this.size<=0))return this.items[this.offset]}clear(){this.items=[],this.offset=0,this.size=0,this.maxSize=0}}const y=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LEAST_USED:"LEAST_USED",LEAST_BUSY:"LEAST_BUSY",LEAST_ELU:"LEAST_ELU",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN",INTERLEAVED_WEIGHTED_ROUND_ROBIN:"INTERLEAVED_WEIGHTED_ROUND_ROBIN"}),S=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class x{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=m){this.pool=e,this.opts=t,this.choose=this.choose.bind(this)}setTaskStatisticsRequirements(e){this.taskStatisticsRequirements.runTime.average&&!0===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!1,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.runTime.median&&!1===e.runTime?.median&&(this.taskStatisticsRequirements.runTime.average=!0,this.taskStatisticsRequirements.runTime.median=e.runTime.median),this.taskStatisticsRequirements.waitTime.average&&!0===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!1,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.waitTime.median&&!1===e.waitTime?.median&&(this.taskStatisticsRequirements.waitTime.average=!0,this.taskStatisticsRequirements.waitTime.median=e.waitTime.median),this.taskStatisticsRequirements.elu.average&&!0===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!1,this.taskStatisticsRequirements.elu.median=e.elu.median),this.taskStatisticsRequirements.elu.median&&!1===e.elu?.median&&(this.taskStatisticsRequirements.elu.average=!0,this.taskStatisticsRequirements.elu.median=e.elu.median)}setOptions(e){this.opts=e??m,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].workerUsage.runTime.median:this.pool.workerNodes[e].workerUsage.runTime.average}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].workerUsage.waitTime.median:this.pool.workerNodes[e].workerUsage.waitTime.average}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].workerUsage.elu.active.median:this.pool.workerNodes[e].workerUsage.elu.active.average}computeDefaultWorkerWeight(){let e=0;for(const t of i()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/i().length)}}class N extends x{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];s<e&&(e=s,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===S.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class v extends x{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeId=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let s=this.roundId;s<this.roundWeights.length;s++)for(let r=this.nextWorkerNodeId;r<this.pool.workerNodes.length;r++){if((this.opts.weights?.[r]??this.defaultWorkerWeight)>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeId=t??0;const s=this.nextWorkerNodeId;return this.nextWorkerNodeId===this.pool.workerNodes.length-1?(this.nextWorkerNodeId=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeId=this.nextWorkerNodeId+1,s}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1)),!0}setOptions(e){super.setOptions(e),this.roundWeights=this.getRoundWeights()}getRoundWeights(){return null==this.opts.weights?[this.defaultWorkerWeight]:[...new Set(Object.values(this.opts.weights).slice().sort(((e,t)=>e-t)))]}}class E extends x{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.runTime.aggregate+s.workerUsage.waitTime.aggregate;if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class I extends x{constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage.tasks,i=r.executed+r.executing+r.queued;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class R extends x{taskStatisticsRequirements={runTime:{aggregate:!1,average:!1,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.workerUsage,i=r.elu?.active.aggregate??0;if(0===i){this.nextWorkerNodeId=t;break}i<e&&(e=i,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class b extends x{strategyPolicy={useDynamicWorker:!0};constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeId=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId;return this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1)),!0}}class O extends x{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:{aggregate:!1,average:!1,median:!1},elu:{aggregate:!1,average:!1,median:!1}};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=m){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeId=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeId,t=this.workerVirtualTaskRunTime;return t<(this.opts.weights?.[e]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=t+this.getWorkerTaskRunTime(e):(this.nextWorkerNodeId=this.nextWorkerNodeId===this.pool.workerNodes.length-1?0:this.nextWorkerNodeId+1,this.workerVirtualTaskRunTime=0),e}remove(e){return this.nextWorkerNodeId===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeId=0:this.nextWorkerNodeId>this.pool.workerNodes.length-1&&(this.nextWorkerNodeId=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}}class C{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=y.ROUND_ROBIN,s=m){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[y.ROUND_ROBIN,new(b.bind(this))(e,s)],[y.LEAST_USED,new(I.bind(this))(e,s)],[y.LEAST_BUSY,new(E.bind(this))(e,s)],[y.LEAST_ELU,new(R.bind(this))(e,s)],[y.FAIR_SHARE,new(N.bind(this))(e,s)],[y.WEIGHTED_ROUND_ROBIN,new(O.bind(this))(e,s)],[y.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(v.bind(this))(e,s)]])}getStrategyPolicy(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).strategyPolicy}getTaskStatisticsRequirements(){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).taskStatisticsRequirements}setWorkerChoiceStrategy(e){this.workerChoiceStrategy!==e&&(this.workerChoiceStrategy=e),this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()}update(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).update(e)}execute(){const e=this.workerChoiceStrategies.get(this.workerChoiceStrategy).choose();if(null==e)throw new Error("Worker node key chosen is null or undefined");return e}remove(e){return this.workerChoiceStrategies.get(this.workerChoiceStrategy).remove(e)}setOptions(e){for(const t of this.workerChoiceStrategies.values())t.setOptions(e)}}class z{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;constructor(e,t,s){if(this.numberOfWorkers=e,this.filePath=t,this.opts=s,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.chooseWorkerNode=this.chooseWorkerNode.bind(this),this.executeTask=this.executeTask.bind(this),this.enqueueTask=this.enqueueTask.bind(this),this.checkAndEmitEvents=this.checkAndEmitEvents.bind(this),!0===this.opts.enableEvents&&(this.emitter=new d),this.workerChoiceStrategyContext=new C(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker()}checkFilePath(e){if(null==e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(e){if(null==e)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(e))throw new TypeError("Cannot instantiate a pool with a non safe integer number of workers");if(e<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===k.fixed&&0===e)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){if(!w(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??y.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??m,this.checkValidWorkerChoiceStrategyOptions(this.opts.workerChoiceStrategyOptions),this.opts.restartWorkerOnError=e.restartWorkerOnError??!0,this.opts.enableEvents=e.enableEvents??!0,this.opts.enableTasksQueue=e.enableTasksQueue??!1,this.opts.enableTasksQueue&&(this.checkValidTasksQueueOptions(e.tasksQueueOptions),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e.tasksQueueOptions))}checkValidWorkerChoiceStrategy(e){if(!Object.values(y).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!w(e))throw new TypeError("Invalid worker choice strategy options: must be a plain object");if(null!=e.weights&&Object.keys(e.weights).length!==this.maxSize)throw new Error("Invalid worker choice strategy options: must have a weight for each worker node");if(null!=e.measurement&&!Object.values(S).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!w(e))throw new TypeError("Invalid tasks queue options: must be a plain object");if(null!=e?.concurrency&&!Number.isSafeInteger(e.concurrency))throw new TypeError("Invalid worker tasks concurrency: must be an integer");if(null!=e?.concurrency&&e.concurrency<=0)throw new Error(`Invalid worker tasks concurrency '${e.concurrency}'`)}get info(){return{type:this.type,worker:this.worker,minSize:this.minSize,maxSize:this.maxSize,workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.workerUsage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.workerUsage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.maxQueued),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.workerUsage.tasks.failed),0)}}getWorkerNodeKey(e){return this.workerNodes.findIndex((t=>t.worker===e))}setWorkerChoiceStrategy(e,t){this.checkValidWorkerChoiceStrategy(e),this.opts.workerChoiceStrategy=e,this.workerChoiceStrategyContext.setWorkerChoiceStrategy(this.opts.workerChoiceStrategy),null!=t&&this.setWorkerChoiceStrategyOptions(t);for(const[e,t]of this.workerNodes.entries())this.setWorkerNodeTasksUsage(t,this.getWorkerUsage(e)),this.setWorkerStatistics(t.worker)}setWorkerChoiceStrategyOptions(e){this.checkValidWorkerChoiceStrategyOptions(e),this.opts.workerChoiceStrategyOptions=e,this.workerChoiceStrategyContext.setOptions(this.opts.workerChoiceStrategyOptions)}enableTasksQueue(e,t){!0!==this.opts.enableTasksQueue||e||this.flushTasksQueues(),this.opts.enableTasksQueue=e,this.setTasksQueueOptions(t)}setTasksQueueOptions(e){!0===this.opts.enableTasksQueue?(this.checkValidTasksQueueOptions(e),this.opts.tasksQueueOptions=this.buildTasksQueueOptions(e)):null!=this.opts.tasksQueueOptions&&delete this.opts.tasksQueueOptions}buildTasksQueueOptions(e){return{concurrency:e?.concurrency??1}}get full(){return this.workerNodes.length>=this.maxSize}internalBusy(){return-1===this.workerNodes.findIndex((e=>0===e.workerUsage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),a={name:t,data:e??{},timestamp:i,id:s.randomUUID()},n=new Promise(((e,t)=>{this.promiseResponseMap.set(a.id,{resolve:e,reject:t,worker:this.workerNodes[o].worker})}));return!0===this.opts.enableTasksQueue&&(this.busy||this.workerNodes[o].workerUsage.tasks.executing>=this.opts.tasksQueueOptions.concurrency)?this.enqueueTask(o,a):this.executeTask(o,a),this.checkAndEmitEvents(),n}async destroy(){await Promise.all(this.workerNodes.map((async(e,t)=>{this.flushTasksQueue(t),await this.destroyWorker(e.worker)})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].workerUsage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const s=this.workerNodes[this.getWorkerNodeKey(e)].workerUsage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,++s.executed,null!=t.taskError&&++s.failed}updateRunTimeWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&(e.runTime.aggregate+=t.taskPerformance?.runTime??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=p(e.runTime.history)))}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate+=i??0,this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/(e.tasks.executed-e.tasks.failed)),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&null!=i&&(e.waitTime.history.push(i),e.waitTime.median=p(e.waitTime.history)))}updateEluWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate){if(null!=e.elu&&null!=t.taskPerformance?.elu?(e.elu.idle.aggregate+=t.taskPerformance.elu.idle,e.elu.active.aggregate+=t.taskPerformance.elu.active,e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2):null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=t.taskPerformance.elu.idle,e.elu.active.aggregate=t.taskPerformance.elu.active,e.elu.utilization=t.taskPerformance.elu.utilization),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed){const t=e.tasks.executed-e.tasks.failed;e.elu.idle.average=e.elu.idle.aggregate/t,e.elu.active.average=e.elu.active.aggregate/t}this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&null!=t.taskPerformance?.elu&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=p(e.elu.idle.history),e.elu.active.median=p(e.elu.active.history))}}chooseWorkerNode(){if(this.shallCreateDynamicWorker()){const e=this.createAndSetupDynamicWorker();if(this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker)return this.getWorkerNodeKey(e)}return this.workerChoiceStrategyContext.execute()}shallCreateDynamicWorker(){return this.type===k.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener())}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??g),e.on("error",this.opts.errorHandler??g),e.on("error",(e=>{null!=this.emitter&&this.emitter.emit(l.error,e),!0===this.opts.restartWorkerOnError&&this.createAndSetupWorker()})),e.on("online",this.opts.onlineHandler??g),e.on("exit",this.opts.exitHandler??g),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.setWorkerStatistics(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=T.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].workerUsage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)})),e}workerListener(){return e=>{if(null!=e.id){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(l.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const s=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(s)>0&&this.executeTask(s,this.dequeueTask(s)),this.workerChoiceStrategyContext.update(s)}}}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter?.emit(l.busy,this.info),this.type===k.dynamic&&this.full&&this.emitter?.emit(l.full,this.info))}setWorkerNodeTasksUsage(e,t){e.workerUsage=t}pushWorkerNode(e){this.workerNodes.push({worker:e,workerUsage:this.getWorkerUsage(),tasksQueue:new W});const t=this.getWorkerNodeKey(e);return this.setWorkerNodeTasksUsage(this.workerNodes[t],this.getWorkerUsage(t)),this.workerNodes.length}removeWorkerNode(e){const t=this.getWorkerNodeKey(e);-1!==t&&(this.workerNodes.splice(t,1),this.workerChoiceStrategyContext.remove(t))}executeTask(e,t){this.beforeTaskExecutionHook(e,t),this.sendToWorker(this.workerNodes[e].worker,t)}enqueueTask(e,t){return this.workerNodes[e].tasksQueue.enqueue(t)}dequeueTask(e){return this.workerNodes[e].tasksQueue.dequeue()}tasksQueueSize(e){return this.workerNodes[e].tasksQueue.size}tasksMaxQueueSize(e){return this.workerNodes[e].tasksQueue.maxSize}flushTasksQueue(e){if(this.tasksQueueSize(e)>0)for(let t=0;t<this.tasksQueueSize(e);t++)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].tasksQueue.clear()}flushTasksQueues(){for(const[e]of this.workerNodes.entries())this.flushTasksQueue(e)}setWorkerStatistics(e){this.sendToWorker(e,{statistics:{runTime:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate,elu:this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate}})}getWorkerUsage(e){const t=e=>null!=e?this.tasksQueueSize(e):0,s=e=>null!=e?this.tasksMaxQueueSize(e):0;return{tasks:{executed:0,executing:0,get queued(){return t(e)},get maxQueued(){return s(e)},failed:0},runTime:{aggregate:0,average:0,median:0,history:new f},waitTime:{aggregate:0,average:0,median:0,history:new f},elu:{idle:{aggregate:0,average:0,median:0,history:new f},active:{aggregate:0,average:0,median:0,history:new f},utilization:0}}}}class q extends z{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return k.fixed}get worker(){return c.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class U extends q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class Q extends z{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new a(this.filePath,{env:n,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return c.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class A extends Q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const P="default",D=6e4,M=T.SOFT;class _ extends u{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,i,o={killBehavior:M,maxInactiveTime:D}){super(e),this.isMain=t,this.mainWorker=i,this.opts=o,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||(this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??D)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??M,this.opts.maxInactiveTime=e.maxInactiveTime??D,delete this.opts.async}checkTaskFunctions(e){if(null==e)throw new Error("taskFunctions parameter is mandatory");if(this.taskFunctions=new Map,"function"==typeof e)this.taskFunctions.set(P,e.bind(this));else{if(!w(e))throw new TypeError("taskFunctions parameter is not a function or a plain object");{let t=!0;for(const[s,r]of Object.entries(e)){if("function"!=typeof r)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(s,r.bind(this)),t&&(this.taskFunctions.set(P,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}messageListener(e){if(null!=e.id&&null!=e.data){const t=this.getTaskFunction(e.name);"AsyncFunction"===t?.constructor.name?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}else null!=e.statistics?this.statistics=e.statistics:null!=e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??D)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}runSync(e,t){try{let s=this.beginTaskPerformance();const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})}finally{!this.isMain&&(this.lastTaskTimestamp=r.now())}}runAsync(e,t){let s=this.beginTaskPerformance();e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{message:s,data:t.data},id:t.id})})).finally((()=>{!this.isMain&&(this.lastTaskTimestamp=r.now())})).catch(g)}getTaskFunction(e){e=e??P;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(){return this.checkStatistics(),{timestamp:r.now(),...this.statistics.elu&&{elu:r.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:r.now()-e.timestamp},...this.statistics.elu&&{elu:r.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}class V extends _{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}}class j extends _{constructor(e,t={}){super("worker-thread-pool:poolifier",o,e,h,t)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}}export{V as ClusterWorker,U as DynamicClusterPool,A as DynamicThreadPool,q as FixedClusterPool,Q as FixedThreadPool,T as KillBehaviors,S as Measurements,l as PoolEvents,k as PoolTypes,j as ThreadWorker,y as WorkerChoiceStrategies,c as WorkerTypes};
@@ -7,8 +7,8 @@ import { WorkerChoiceStrategyContext } from './selection-strategies/worker-choic
7
7
  * Base class that implements some shared logic for all poolifier pools.
8
8
  *
9
9
  * @typeParam Worker - Type of worker which manages this pool.
10
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
11
- * @typeParam Response - Type of execution response. This can only be serializable data.
10
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
11
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
12
12
  */
13
13
  export declare abstract class AbstractPool<Worker extends IWorker, Data = unknown, Response = unknown> implements IPool<Worker, Data, Response> {
14
14
  protected readonly numberOfWorkers: number;
@@ -110,8 +110,8 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
110
110
  */
111
111
  protected abstract destroyWorker(worker: Worker): void | Promise<void>;
112
112
  /**
113
- * Setup hook to execute code before worker node are created in the abstract constructor.
114
- * Can be overridden
113
+ * Setup hook to execute code before worker nodes are created in the abstract constructor.
114
+ * Can be overridden.
115
115
  *
116
116
  * @virtual
117
117
  */
@@ -167,7 +167,7 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
167
167
  * @param worker - The worker which should register a listener.
168
168
  * @param listener - The message listener callback.
169
169
  */
170
- protected abstract registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
170
+ private registerWorkerMessageListener;
171
171
  /**
172
172
  * Creates a new worker.
173
173
  *
@@ -176,12 +176,11 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
176
176
  protected abstract createWorker(): Worker;
177
177
  /**
178
178
  * Function that can be hooked up when a worker has been newly created and moved to the pool worker nodes.
179
- *
180
- * Can be used to update the `maxListeners` or binding the `main-worker`\<-\>`worker` connection if not bind by default.
179
+ * Can be overridden.
181
180
  *
182
181
  * @param worker - The newly created worker.
183
182
  */
184
- protected abstract afterWorkerSetup(worker: Worker): void;
183
+ protected afterWorkerSetup(worker: Worker): void;
185
184
  /**
186
185
  * Creates a new worker and sets it up completely in the pool worker nodes.
187
186
  *
@@ -225,9 +224,9 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
225
224
  private enqueueTask;
226
225
  private dequeueTask;
227
226
  private tasksQueueSize;
227
+ private tasksMaxQueueSize;
228
228
  private flushTasksQueue;
229
229
  private flushTasksQueues;
230
230
  private setWorkerStatistics;
231
231
  private getWorkerUsage;
232
- private getTaskStatistics;
233
232
  }
@@ -6,8 +6,8 @@ import { type ClusterPoolOptions, FixedClusterPool } from './fixed';
6
6
  * This cluster pool creates new workers when the others are busy, up to the maximum number of workers.
7
7
  * When the maximum number of workers is reached and workers are busy, an event is emitted. If you want to listen to this event, use the pool's `emitter`.
8
8
  *
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  * @author [Christopher Quadflieg](https://github.com/Shinigami92)
12
12
  * @since 2.0.0
13
13
  */
@@ -23,10 +23,8 @@ export interface ClusterPoolOptions extends PoolOptions<Worker> {
23
23
  /**
24
24
  * A cluster pool with a fixed number of workers.
25
25
  *
26
- * It is possible to perform tasks in sync or asynchronous mode as you prefer.
27
- *
28
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
29
- * @typeParam Response - Type of execution response. This can only be serializable data.
26
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
27
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
30
28
  * @author [Christopher Quadflieg](https://github.com/Shinigami92)
31
29
  * @since 2.0.0
32
30
  */
@@ -49,12 +47,8 @@ export declare class FixedClusterPool<Data = unknown, Response = unknown> extend
49
47
  /** @inheritDoc */
50
48
  protected sendToWorker(worker: Worker, message: MessageValue<Data>): void;
51
49
  /** @inheritDoc */
52
- protected registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
53
- /** @inheritDoc */
54
50
  protected createWorker(): Worker;
55
51
  /** @inheritDoc */
56
- protected afterWorkerSetup(worker: Worker): void;
57
- /** @inheritDoc */
58
52
  protected get type(): PoolType;
59
53
  /** @inheritDoc */
60
54
  protected get worker(): WorkerType;
@@ -133,8 +133,8 @@ export interface PoolOptions<Worker extends IWorker> {
133
133
  * Contract definition for a poolifier pool.
134
134
  *
135
135
  * @typeParam Worker - Type of worker which manages this pool.
136
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
137
- * @typeParam Response - Type of execution response. This can only be serializable data.
136
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
137
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
138
138
  */
139
139
  export interface IPool<Worker extends IWorker, Data = unknown, Response = unknown> {
140
140
  /**
@@ -159,7 +159,7 @@ export interface IPool<Worker extends IWorker, Data = unknown, Response = unknow
159
159
  /**
160
160
  * Executes the specified function in the worker constructor with the task data input parameter.
161
161
  *
162
- * @param data - The task input data for the specified worker function. This can only be serializable data.
162
+ * @param data - The task input data for the specified worker function. This can only be structured-cloneable data.
163
163
  * @param name - The name of the worker function to execute. If not specified, the default worker function will be executed.
164
164
  * @returns Promise that will be fulfilled when the task is completed.
165
165
  */
@@ -5,8 +5,8 @@ import type { IWorkerChoiceStrategy, StrategyPolicy, TaskStatisticsRequirements,
5
5
  * Worker choice strategy abstract base class.
6
6
  *
7
7
  * @typeParam Worker - Type of worker which manages the strategy.
8
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
9
- * @typeParam Response - Type of execution response. This can only be serializable data.
8
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
9
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
10
10
  */
11
11
  export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> implements IWorkerChoiceStrategy {
12
12
  protected readonly pool: IPool<Worker, Data, Response>;
@@ -7,8 +7,8 @@ import { type IWorkerChoiceStrategy, type TaskStatisticsRequirements, type Worke
7
7
  * Loosely modeled after the fair queueing algorithm: https://en.wikipedia.org/wiki/Fair_queuing.
8
8
  *
9
9
  * @typeParam Worker - Type of worker which manages the strategy.
10
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
11
- * @typeParam Response - Type of execution response. This can only be serializable data.
10
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
11
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
12
12
  */
13
13
  export declare class FairShareWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
14
14
  /** @inheritDoc */
@@ -6,8 +6,8 @@ import type { IWorkerChoiceStrategy, StrategyPolicy, WorkerChoiceStrategyOptions
6
6
  * Selects the next worker with an interleaved weighted round robin scheduling algorithm.
7
7
  *
8
8
  * @typeParam Worker - Type of worker which manages the strategy.
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  */
12
12
  export declare class InterleavedWeightedRoundRobinWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
13
13
  /** @inheritDoc */
@@ -6,8 +6,8 @@ import type { IWorkerChoiceStrategy, TaskStatisticsRequirements, WorkerChoiceStr
6
6
  * Selects the least busy worker.
7
7
  *
8
8
  * @typeParam Worker - Type of worker which manages the strategy.
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  */
12
12
  export declare class LeastBusyWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
13
13
  /** @inheritDoc */
@@ -6,8 +6,8 @@ import type { IWorkerChoiceStrategy, TaskStatisticsRequirements, WorkerChoiceStr
6
6
  * Selects the worker with the least ELU.
7
7
  *
8
8
  * @typeParam Worker - Type of worker which manages the strategy.
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  */
12
12
  export declare class LeastEluWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
13
13
  /** @inheritDoc */
@@ -6,8 +6,8 @@ import type { IWorkerChoiceStrategy, WorkerChoiceStrategyOptions } from './selec
6
6
  * Selects the least used worker.
7
7
  *
8
8
  * @typeParam Worker - Type of worker which manages the strategy.
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  */
12
12
  export declare class LeastUsedWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
13
13
  /** @inheritDoc */
@@ -6,8 +6,8 @@ import type { IWorkerChoiceStrategy, StrategyPolicy, WorkerChoiceStrategyOptions
6
6
  * Selects the next worker in a round robin fashion.
7
7
  *
8
8
  * @typeParam Worker - Type of worker which manages the strategy.
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  */
12
12
  export declare class RoundRobinWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
13
13
  /** @inheritDoc */
@@ -7,8 +7,8 @@ import type { IWorkerChoiceStrategy, StrategyPolicy, TaskStatisticsRequirements,
7
7
  * Loosely modeled after the weighted round robin queueing algorithm: https://en.wikipedia.org/wiki/Weighted_round_robin.
8
8
  *
9
9
  * @typeParam Worker - Type of worker which manages the strategy.
10
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
11
- * @typeParam Response - Type of execution response. This can only be serializable data.
10
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
11
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
12
12
  */
13
13
  export declare class WeightedRoundRobinWorkerChoiceStrategy<Worker extends IWorker, Data = unknown, Response = unknown> extends AbstractWorkerChoiceStrategy<Worker, Data, Response> implements IWorkerChoiceStrategy {
14
14
  /** @inheritDoc */
@@ -5,8 +5,8 @@ import type { StrategyPolicy, TaskStatisticsRequirements, WorkerChoiceStrategy,
5
5
  * The worker choice strategy context.
6
6
  *
7
7
  * @typeParam Worker - Type of worker.
8
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
9
- * @typeParam Response - Type of execution response. This can only be serializable data.
8
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
9
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
10
10
  */
11
11
  export declare class WorkerChoiceStrategyContext<Worker extends IWorker, Data = unknown, Response = unknown> {
12
12
  private workerChoiceStrategy;
@@ -6,8 +6,8 @@ import { FixedThreadPool, type ThreadPoolOptions } from './fixed';
6
6
  * This thread pool creates new threads when the others are busy, up to the maximum number of threads.
7
7
  * When the maximum number of threads is reached and workers are busy, an event is emitted. If you want to listen to this event, use the pool's `emitter`.
8
8
  *
9
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
10
- * @typeParam Response - Type of execution response. This can only be serializable data.
9
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
10
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
11
11
  * @author [Alessandro Pio Ardizio](https://github.com/pioardi)
12
12
  * @since 0.0.1
13
13
  */
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
- import { MessageChannel, Worker, type WorkerOptions } from 'node:worker_threads';
3
- import type { Draft, MessageValue } from '../../utility-types';
2
+ import { Worker, type WorkerOptions } from 'node:worker_threads';
3
+ import type { MessageValue } from '../../utility-types';
4
4
  import { AbstractPool } from '../abstract-pool';
5
5
  import { type PoolOptions, type PoolType, type WorkerType } from '../pool';
6
6
  /**
@@ -14,21 +14,15 @@ export interface ThreadPoolOptions extends PoolOptions<Worker> {
14
14
  */
15
15
  workerOptions?: WorkerOptions;
16
16
  }
17
- /**
18
- * A thread worker with message channels for communication between main thread and thread worker.
19
- */
20
- export type ThreadWorkerWithMessageChannel = Worker & Draft<MessageChannel>;
21
17
  /**
22
18
  * A thread pool with a fixed number of threads.
23
19
  *
24
- * It is possible to perform tasks in sync or asynchronous mode as you prefer.
25
- *
26
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
27
- * @typeParam Response - Type of execution response. This can only be serializable data.
20
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
21
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
28
22
  * @author [Alessandro Pio Ardizio](https://github.com/pioardi)
29
23
  * @since 0.0.1
30
24
  */
31
- export declare class FixedThreadPool<Data = unknown, Response = unknown> extends AbstractPool<ThreadWorkerWithMessageChannel, Data, Response> {
25
+ export declare class FixedThreadPool<Data = unknown, Response = unknown> extends AbstractPool<Worker, Data, Response> {
32
26
  protected readonly opts: ThreadPoolOptions;
33
27
  /**
34
28
  * Constructs a new poolifier fixed thread pool.
@@ -41,15 +35,11 @@ export declare class FixedThreadPool<Data = unknown, Response = unknown> extends
41
35
  /** @inheritDoc */
42
36
  protected isMain(): boolean;
43
37
  /** @inheritDoc */
44
- protected destroyWorker(worker: ThreadWorkerWithMessageChannel): Promise<void>;
45
- /** @inheritDoc */
46
- protected sendToWorker(worker: ThreadWorkerWithMessageChannel, message: MessageValue<Data>): void;
47
- /** @inheritDoc */
48
- protected registerWorkerMessageListener<Message extends Data | Response>(worker: ThreadWorkerWithMessageChannel, listener: (message: MessageValue<Message>) => void): void;
38
+ protected destroyWorker(worker: Worker): Promise<void>;
49
39
  /** @inheritDoc */
50
- protected createWorker(): ThreadWorkerWithMessageChannel;
40
+ protected sendToWorker(worker: Worker, message: MessageValue<Data>): void;
51
41
  /** @inheritDoc */
52
- protected afterWorkerSetup(worker: ThreadWorkerWithMessageChannel): void;
42
+ protected createWorker(): Worker;
53
43
  /** @inheritDoc */
54
44
  protected get type(): PoolType;
55
45
  /** @inheritDoc */
@@ -3,11 +3,11 @@ import type { Queue } from '../queue';
3
3
  /**
4
4
  * Callback invoked if the worker has received a message.
5
5
  */
6
- export type MessageHandler<Worker extends IWorker> = (this: Worker, m: unknown) => void;
6
+ export type MessageHandler<Worker extends IWorker> = (this: Worker, message: unknown) => void;
7
7
  /**
8
8
  * Callback invoked if the worker raised an error.
9
9
  */
10
- export type ErrorHandler<Worker extends IWorker> = (this: Worker, e: Error) => void;
10
+ export type ErrorHandler<Worker extends IWorker> = (this: Worker, error: Error) => void;
11
11
  /**
12
12
  * Callback invoked when the worker has started successfully.
13
13
  */
@@ -15,11 +15,11 @@ export type OnlineHandler<Worker extends IWorker> = (this: Worker) => void;
15
15
  /**
16
16
  * Callback invoked when the worker exits successfully.
17
17
  */
18
- export type ExitHandler<Worker extends IWorker> = (this: Worker, code: number) => void;
18
+ export type ExitHandler<Worker extends IWorker> = (this: Worker, exitCode: number) => void;
19
19
  /**
20
20
  * Message object that is passed as a task between main worker and worker.
21
21
  *
22
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
22
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
23
23
  * @internal
24
24
  */
25
25
  export interface Task<Data = unknown> {
@@ -91,6 +91,10 @@ export interface TaskStatistics {
91
91
  * Number of queued tasks.
92
92
  */
93
93
  readonly queued: number;
94
+ /**
95
+ * Maximum number of queued tasks.
96
+ */
97
+ readonly maxQueued: number;
94
98
  /**
95
99
  * Number of failed tasks.
96
100
  */
@@ -142,7 +146,7 @@ export interface IWorker {
142
146
  * Worker node interface.
143
147
  *
144
148
  * @typeParam Worker - Type of worker.
145
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
149
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
146
150
  * @internal
147
151
  */
148
152
  export interface WorkerNode<Worker extends IWorker, Data = unknown> {
package/lib/queue.d.ts CHANGED
@@ -6,7 +6,9 @@
6
6
  export declare class Queue<T> {
7
7
  private items;
8
8
  private offset;
9
+ /** The size of the queue. */
9
10
  size: number;
11
+ /** The maximum size of the queue. */
10
12
  maxSize: number;
11
13
  constructor();
12
14
  /**
@@ -28,4 +30,8 @@ export declare class Queue<T> {
28
30
  * @returns The first item or `undefined` if the queue is empty.
29
31
  */
30
32
  peek(): T | undefined;
33
+ /**
34
+ * Clear the queue.
35
+ */
36
+ clear(): void;
31
37
  }
@@ -1,23 +1,11 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
- import type { Worker as ClusterWorker } from 'node:cluster';
5
- import type { MessagePort } from 'node:worker_threads';
6
2
  import type { EventLoopUtilization } from 'node:perf_hooks';
7
3
  import type { KillBehavior } from './worker/worker-options';
8
4
  import type { IWorker, Task } from './pools/worker';
9
- /**
10
- * Make all properties in T non-readonly.
11
- *
12
- * @typeParam T - Type in which properties will be non-readonly.
13
- */
14
- export type Draft<T> = {
15
- -readonly [P in keyof T]?: T[P];
16
- };
17
5
  /**
18
6
  * Task error.
19
7
  *
20
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
8
+ * @typeParam Data - Type of data sent to the worker triggering an error. This can only be structured-cloneable data.
21
9
  */
22
10
  export interface TaskError<Data = unknown> {
23
11
  /**
@@ -56,12 +44,11 @@ export interface WorkerStatistics {
56
44
  /**
57
45
  * Message object that is passed between main worker and worker.
58
46
  *
59
- * @typeParam MessageData - Type of data sent to and/or from the worker. This can only be serializable data.
60
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
61
- * @typeParam MainWorker - Type of main worker.
47
+ * @typeParam Data - Type of data sent to the worker or execution response. This can only be structured-cloneable data.
48
+ * @typeParam ErrorData - Type of data sent to the worker triggering an error. This can only be structured-cloneable data.
62
49
  * @internal
63
50
  */
64
- export interface MessageValue<MessageData = unknown, Data = unknown, MainWorker extends ClusterWorker | MessagePort = ClusterWorker | MessagePort> extends Task<MessageData> {
51
+ export interface MessageValue<Data = unknown, ErrorData = unknown> extends Task<Data> {
65
52
  /**
66
53
  * Kill code.
67
54
  */
@@ -69,15 +56,11 @@ export interface MessageValue<MessageData = unknown, Data = unknown, MainWorker
69
56
  /**
70
57
  * Task error.
71
58
  */
72
- readonly taskError?: TaskError<Data>;
59
+ readonly taskError?: TaskError<ErrorData>;
73
60
  /**
74
61
  * Task performance.
75
62
  */
76
63
  readonly taskPerformance?: TaskPerformance;
77
- /**
78
- * Reference to main worker.
79
- */
80
- readonly parent?: MainWorker;
81
64
  /**
82
65
  * Whether to compute the given statistics or not.
83
66
  */
@@ -87,7 +70,7 @@ export interface MessageValue<MessageData = unknown, Data = unknown, MainWorker
87
70
  * An object holding the execution response promise resolve/reject callbacks.
88
71
  *
89
72
  * @typeParam Worker - Type of worker.
90
- * @typeParam Response - Type of execution response. This can only be serializable data.
73
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
91
74
  * @internal
92
75
  */
93
76
  export interface PromiseResponseWrapper<Worker extends IWorker, Response = unknown> {
@@ -12,12 +12,12 @@ import type { TaskFunctions, WorkerAsyncFunction, WorkerFunction, WorkerSyncFunc
12
12
  * Base class that implements some shared logic for all poolifier workers.
13
13
  *
14
14
  * @typeParam MainWorker - Type of main worker.
15
- * @typeParam Data - Type of data this worker receives from pool's execution. This can only be serializable data.
16
- * @typeParam Response - Type of response the worker sends back to the main worker. This can only be serializable data.
15
+ * @typeParam Data - Type of data this worker receives from pool's execution. This can only be structured-cloneable data.
16
+ * @typeParam Response - Type of response the worker sends back to the main worker. This can only be structured-cloneable data.
17
17
  */
18
18
  export declare abstract class AbstractWorker<MainWorker extends Worker | MessagePort, Data = unknown, Response = unknown> extends AsyncResource {
19
19
  protected readonly isMain: boolean;
20
- protected mainWorker: MainWorker | undefined | null;
20
+ protected readonly mainWorker: MainWorker;
21
21
  protected readonly opts: WorkerOptions;
22
22
  /**
23
23
  * Task function(s) processed by the worker when the pool's `execution` function is invoked.
@@ -28,7 +28,7 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
28
28
  */
29
29
  protected lastTaskTimestamp: number;
30
30
  /**
31
- * Performance statistics computation.
31
+ * Performance statistics computation requirements.
32
32
  */
33
33
  protected statistics: WorkerStatistics;
34
34
  /**
@@ -44,7 +44,7 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
44
44
  * @param mainWorker - Reference to main worker.
45
45
  * @param opts - Options for the worker.
46
46
  */
47
- constructor(type: string, isMain: boolean, taskFunctions: WorkerFunction<Data, Response> | TaskFunctions<Data, Response>, mainWorker: MainWorker | undefined | null, opts?: WorkerOptions);
47
+ constructor(type: string, isMain: boolean, taskFunctions: WorkerFunction<Data, Response> | TaskFunctions<Data, Response>, mainWorker: MainWorker, opts?: WorkerOptions);
48
48
  private checkWorkerOptions;
49
49
  /**
50
50
  * Checks if the `taskFunctions` parameter is passed to the constructor.
@@ -57,7 +57,7 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
57
57
  *
58
58
  * @param message - Message received.
59
59
  */
60
- protected messageListener(message: MessageValue<Data, Data, MainWorker>): void;
60
+ protected messageListener(message: MessageValue<Data, Data>): void;
61
61
  /**
62
62
  * Returns the main worker.
63
63
  *
@@ -103,4 +103,5 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
103
103
  private getTaskFunction;
104
104
  private beginTaskPerformance;
105
105
  private endTaskPerformance;
106
+ private checkStatistics;
106
107
  }
@@ -13,8 +13,8 @@ import type { TaskFunctions, WorkerFunction } from './worker-functions';
13
13
  * If you use a `DynamicClusterPool` the extra workers that were created will be terminated,
14
14
  * but the minimum number of workers will be guaranteed.
15
15
  *
16
- * @typeParam Data - Type of data this worker receives from pool's execution. This can only be serializable data.
17
- * @typeParam Response - Type of response the worker sends back to the main worker. This can only be serializable data.
16
+ * @typeParam Data - Type of data this worker receives from pool's execution. This can only be structured-cloneable data.
17
+ * @typeParam Response - Type of response the worker sends back to the main worker. This can only be structured-cloneable data.
18
18
  * @author [Christopher Quadflieg](https://github.com/Shinigami92)
19
19
  * @since 2.0.0
20
20
  */
@@ -13,8 +13,8 @@ import type { TaskFunctions, WorkerFunction } from './worker-functions';
13
13
  * If you use a `DynamicThreadPool` the extra workers that were created will be terminated,
14
14
  * but the minimum number of workers will be guaranteed.
15
15
  *
16
- * @typeParam Data - Type of data this worker receives from pool's execution. This can only be serializable data.
17
- * @typeParam Response - Type of response the worker sends back to the main thread. This can only be serializable data.
16
+ * @typeParam Data - Type of data this worker receives from pool's execution. This can only be structured-cloneable data.
17
+ * @typeParam Response - Type of response the worker sends back to the main thread. This can only be structured-cloneable data.
18
18
  * @author [Alessandro Pio Ardizio](https://github.com/pioardi)
19
19
  * @since 0.0.1
20
20
  */
@@ -1,24 +1,24 @@
1
1
  /**
2
2
  * Worker synchronous function that can be executed.
3
3
  *
4
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
5
- * @typeParam Response - Type of execution response. This can only be serializable data.
4
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
5
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
6
6
  */
7
7
  export type WorkerSyncFunction<Data = unknown, Response = unknown> = (data?: Data) => Response;
8
8
  /**
9
9
  * Worker asynchronous function that can be executed.
10
10
  * This function must return a promise.
11
11
  *
12
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
13
- * @typeParam Response - Type of execution response. This can only be serializable data.
12
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
13
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
14
14
  */
15
15
  export type WorkerAsyncFunction<Data = unknown, Response = unknown> = (data?: Data) => Promise<Response>;
16
16
  /**
17
17
  * Worker function that can be executed.
18
18
  * This function can be synchronous or asynchronous.
19
19
  *
20
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
21
- * @typeParam Response - Type of execution response. This can only be serializable data.
20
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
21
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
22
22
  */
23
23
  export type WorkerFunction<Data = unknown, Response = unknown> = WorkerSyncFunction<Data, Response> | WorkerAsyncFunction<Data, Response>;
24
24
  /**
@@ -27,7 +27,7 @@ export type WorkerFunction<Data = unknown, Response = unknown> = WorkerSyncFunct
27
27
  * The key is the name of the function.
28
28
  * The value is the function itself.
29
29
  *
30
- * @typeParam Data - Type of data sent to the worker. This can only be serializable data.
31
- * @typeParam Response - Type of execution response. This can only be serializable data.
30
+ * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
31
+ * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
32
32
  */
33
33
  export type TaskFunctions<Data = unknown, Response = unknown> = Record<string, WorkerFunction<Data, Response>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poolifier",
3
- "version": "2.6.3",
3
+ "version": "2.6.5",
4
4
  "description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation",
5
5
  "license": "MIT",
6
6
  "main": "./lib/index.js",
@@ -25,8 +25,8 @@
25
25
  "pnpm": ">=8.6.0"
26
26
  },
27
27
  "volta": {
28
- "node": "20.3.0",
29
- "pnpm": "8.6.3"
28
+ "node": "20.3.1",
29
+ "pnpm": "8.6.5"
30
30
  },
31
31
  "repository": {
32
32
  "type": "git",
@@ -78,25 +78,25 @@
78
78
  "lib"
79
79
  ],
80
80
  "devDependencies": {
81
- "@commitlint/cli": "^17.6.5",
82
- "@commitlint/config-conventional": "^17.6.5",
81
+ "@commitlint/cli": "^17.6.6",
82
+ "@commitlint/config-conventional": "^17.6.6",
83
83
  "@release-it/bumper": "^4.0.2",
84
84
  "@release-it/keep-a-changelog": "^3.1.0",
85
85
  "@rollup/plugin-terser": "^0.4.3",
86
86
  "@rollup/plugin-typescript": "^11.1.1",
87
- "@types/node": "^20.3.1",
88
- "@typescript-eslint/eslint-plugin": "^5.60.0",
89
- "@typescript-eslint/parser": "^5.60.0",
87
+ "@types/node": "^20.3.2",
88
+ "@typescript-eslint/eslint-plugin": "^5.60.1",
89
+ "@typescript-eslint/parser": "^5.60.1",
90
90
  "benny": "^3.7.1",
91
91
  "c8": "^8.0.0",
92
92
  "eslint": "^8.43.0",
93
93
  "eslint-config-standard": "^17.1.0",
94
94
  "eslint-config-standard-with-typescript": "^35.0.0",
95
- "eslint-define-config": "^1.20.0",
95
+ "eslint-define-config": "^1.21.0",
96
96
  "eslint-import-resolver-typescript": "^3.5.5",
97
97
  "eslint-plugin-import": "^2.27.5",
98
- "eslint-plugin-jsdoc": "^46.2.6",
99
- "eslint-plugin-n": "^16.0.0",
98
+ "eslint-plugin-jsdoc": "^46.3.0",
99
+ "eslint-plugin-n": "^16.0.1",
100
100
  "eslint-plugin-promise": "^6.1.1",
101
101
  "eslint-plugin-spellcheck": "^0.0.20",
102
102
  "eslint-plugin-tsdoc": "^0.2.17",
@@ -108,11 +108,11 @@
108
108
  "mochawesome": "^7.1.3",
109
109
  "prettier": "^2.8.8",
110
110
  "release-it": "^15.11.0",
111
- "rollup": "^3.25.1",
111
+ "rollup": "^3.25.3",
112
112
  "rollup-plugin-analyzer": "^4.0.0",
113
113
  "rollup-plugin-command": "^1.1.3",
114
114
  "rollup-plugin-delete": "^2.0.0",
115
- "sinon": "^15.1.2",
115
+ "sinon": "^15.2.0",
116
116
  "source-map-support": "^0.5.21",
117
117
  "ts-standard": "^12.0.2",
118
118
  "typedoc": "^0.24.8",
@@ -124,9 +124,9 @@
124
124
  "build:analyze": "rollup --config --environment ANALYZE,BUILD:development",
125
125
  "build:typedoc": "rollup --config --environment DOCUMENTATION,BUILD:development",
126
126
  "build:prod": "rollup --config",
127
- "benchmark": "pnpm build && node -r source-map-support/register benchmarks/internal/bench.js",
128
- "benchmark:debug": "pnpm build && node -r source-map-support/register --inspect benchmarks/internal/bench.js",
129
- "benchmark:prod": "pnpm build:prod && node -r source-map-support/register benchmarks/internal/bench.js",
127
+ "benchmark": "pnpm build && node -r source-map-support/register benchmarks/internal/bench.mjs",
128
+ "benchmark:debug": "pnpm build && node -r source-map-support/register --inspect benchmarks/internal/bench.mjs",
129
+ "benchmark:prod": "pnpm build:prod && node -r source-map-support/register benchmarks/internal/bench.mjs",
130
130
  "test": "pnpm build && c8 mocha 'tests/**/*.test.js'",
131
131
  "test:debug": "pnpm build && mocha --no-parallel --inspect 'tests/**/*.test.js'",
132
132
  "coverage": "c8 report --reporter=lcov",