poolifier 2.6.14 → 2.6.15

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
@@ -244,13 +244,13 @@ This method is available on both pool implementations and will call the terminat
244
244
  `taskFunctions` (mandatory) The task function or task functions object `{ name_1: fn_1, ..., name_n: fn_n }` that you want to execute on the worker
245
245
  `opts` (optional) An object with these properties:
246
246
 
247
- - `maxInactiveTime` (optional) - Max time to wait tasks to work on in milliseconds, after this period the new worker will die.
248
- The last active time of your worker unit will be updated when a task is submitted to a worker or when a worker terminate a task.
247
+ - `maxInactiveTime` (optional) - Maximum waiting time in milliseconds for tasks on newly created workers. After this time newly created workers will die.
248
+ The last active time of your worker will be updated when it terminates a task.
249
249
  If `killBehavior` is set to `KillBehaviors.HARD` this value represents also the timeout for the tasks that you submit to the pool, when this timeout expires your tasks is interrupted before completion and removed. The worker is killed if is not part of the minimum size of the pool.
250
250
  If `killBehavior` is set to `KillBehaviors.SOFT` your tasks have no timeout and your workers will not be terminated until your task is completed.
251
251
  Default: `60000`
252
252
 
253
- - `killBehavior` (optional) - Dictates if your async unit (worker/process) will be deleted in case that a task is active on it.
253
+ - `killBehavior` (optional) - Dictates if your worker will be deleted in case a task is active on it.
254
254
  **KillBehaviors.SOFT**: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still executing or queued, then the worker **won't** be deleted.
255
255
  **KillBehaviors.HARD**: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still executing or queued, then the worker will be deleted.
256
256
  This option only apply to the newly created workers.
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");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(s){if("default"!==s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}})),t.default=e,Object.freeze(t)}var u=n(i);const h=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class k extends e.EventEmitter{}const c=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),d="default",m=Object.freeze((()=>{})),l={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},g={aggregate:!1,average:!1,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,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},T=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),f=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),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"}),W=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class S{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:g,waitTime:g,elu:g};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].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}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 x extends S{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:g,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===W.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class N extends S{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 v extends S{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:g};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.usage.runTime?.aggregate??0)+(s.usage.waitTime?.aggregate??0);if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class I extends S{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.usage.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 E extends S{taskStatisticsRequirements={runTime:g,waitTime:g,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.usage,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 R extends S{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 b extends S{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:g,elu:g};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 C{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=y.ROUND_ROBIN,s=l){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[y.ROUND_ROBIN,new(R.bind(this))(e,s)],[y.LEAST_USED,new(I.bind(this))(e,s)],[y.LEAST_BUSY,new(v.bind(this))(e,s)],[y.LEAST_ELU,new(E.bind(this))(e,s)],[y.FAIR_SHARE,new(x.bind(this))(e,s)],[y.WEIGHTED_ROUND_ROBIN,new(b.bind(this))(e,s)],[y.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(N.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 O 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 z{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const q=Object.freeze({cluster:"cluster",thread:"thread"});class M{worker;info;usage;tasksUsage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksUsage=new Map,this.tasksQueue=new z}tasksQueueSize(){return this.tasksQueue.size}tasksQueueMaxSize(){return this.tasksQueue.maxSize}enqueueTask(e){return this.tasksQueue.enqueue(e)}dequeueTask(){return this.tasksQueue.dequeue()}clearTasksQueue(){this.tasksQueue.clear()}resetUsage(){this.usage=this.initWorkerUsage(),this.tasksUsage.clear()}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.get(e)}initWorkerInfo(e,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1}}initWorkerUsage(){const e=()=>this.tasksQueueSize(),t=()=>this.tasksQueueMaxSize();return{tasks:{executed:0,executing:0,get queued(){return e()},get maxQueued(){return t()},failed:0},runTime:{history:new O},waitTime:{history:new O},elu:{idle:{history:new O},active:{history:new O}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new O},waitTime:{history:new O},elu:{idle:{history:new O},active:{history:new O}}}}getWorkerId(e,t){return t===q.thread?e.threadId:t===q.cluster?e.id:void 0}}class P{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 k),this.workerChoiceStrategyContext=new C(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.length<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.performance.now()}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===h.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===h.dynamic){if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===e&&0===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size and a maximum pool size equal to zero");if(e===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead")}}checkPoolOptions(e){if(!T(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??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(y).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(W).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{version:"2.6.14",type:this.type,worker:this.worker,ready:this.ready,strategy:this.opts.workerChoiceStrategy,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:w(this.utilization)},workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:w(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:w(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:w(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:w(p(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:w(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:w(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:w(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:w(p(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get starting(){return this.workerNodes.length<this.minSize||this.workerNodes.length>=this.minSize&&this.workerNodes.some((e=>!e.info.ready))}get ready(){return this.workerNodes.length>=this.minSize&&this.workerNodes.every((e=>e.info.ready))}get utilization(){const e=(r.performance.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}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)e.resetUsage(),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.usage.tasks.executing))}async execute(e,t){const i=r.performance.now(),o=this.chooseWorkerNode(),a={name:t??d,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,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].usage.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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.getWorkerNodeKey(e),r=this.workerNodes[s].usage;this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t);const i=this.workerNodes[s].getTaskWorkerUsage(t.taskPerformance?.name??d);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),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.performance.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=p(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(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===h.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id}),this.setWorkerStatistics(e)}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??m),e.on("error",this.opts.errorHandler??m),e.on("error",(t=>{null!=this.emitter&&this.emitter.emit(c.error,t),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(e),!0!==this.opts.restartWorkerOnError||this.starting||(this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic?this.createAndSetupDynamicWorker():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.afterWorkerSetup(e),e}redistributeQueuedTasks(e){const t=this.getWorkerNodeKey(e);for(;this.tasksQueueSize(t)>0;){let e=t,s=1/0;for(const[r,i]of this.workerNodes.entries()){if(r!==t&&0===i.usage.tasks.queued){e=r;break}r!==t&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,e=r)}this.enqueueTask(e,this.dequeueTask(t))}}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();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].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.sendToWorker(e,{checkAlive:!0,workerId:t.id}),e}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready&&null!=e.workerId?this.handleWorkerReadyMessage(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyMessage(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(c.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(c.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(c.busy,this.info),this.type===h.dynamic&&this.full&&this.emitter.emit(c.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}pushWorkerNode(e){return this.workerNodes.push(new M(e,this.worker))}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].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}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},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class A extends P{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:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return h.fixed}get worker(){return q.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class Q extends P{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),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 h.fixed}get worker(){return q.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const U=6e4,D=f.SOFT;class F extends a.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,r,i={killBehavior:D,maxInactiveTime:U}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??D,this.opts.maxInactiveTime=e.maxInactiveTime??U,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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");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")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===d)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{return this.taskFunctions.get(e)===this.taskFunctions.get(d)&&this.taskFunctions.set(d,t.bind(this)),this.taskFunctions.set(e,t.bind(this)),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===d)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(d))throw new Error("Cannot remove the task function used as the default task function");return this.taskFunctions.delete(e)}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===d)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(d,this.taskFunctions.get(e)?.bind(this)),!0}catch{return!1}}messageListener(e){e.workerId===this.id&&(null!=e.ready?this.workerReady():null!=e.statistics?this.statistics=e.statistics:null!=e.checkAlive?e.checkAlive?this.startCheckAlive():this.stopCheckAlive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&(this.stopCheckAlive(),this.emitDestroy()))}workerReady(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckAlive(){this.lastTaskTimestamp=r.performance.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??U)/2),this.checkAlive.bind(this)()}stopCheckAlive(){null!=this.aliveInterval&&clearInterval(this.aliveInterval)}checkAlive(){r.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??U)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let s=this.beginTaskPerformance(t.name);const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??d,message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.performance.now())}}runAsync(e,t){let s=this.beginTaskPerformance(t.name);e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??d,message:s,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.performance.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(e){return this.checkStatistics(),{name:e??d,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 F{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=class extends A{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return h.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends Q{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return h.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=A,exports.FixedThreadPool=Q,exports.KillBehaviors=f,exports.Measurements=W,exports.PoolEvents=c,exports.PoolTypes=h,exports.ThreadWorker=class extends F{constructor(e,t={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,t)}get id(){return o.threadId}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=y,exports.WorkerTypes=q,exports.availableParallelism=()=>{let e=1;try{e=u.availableParallelism()}catch{const t=u.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e};
1
+ "use strict";var e=require("node:events"),t=require("node:cluster"),r=require("node:crypto"),s=require("node:perf_hooks"),i=require("node:fs"),o=require("node:os"),a=require("node:worker_threads"),n=require("node:async_hooks");function u(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var s=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,s.get?s:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var h=u(o);const k=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class c extends e.EventEmitter{}const d=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),m="default",l=Object.freeze((()=>{})),g={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},p={aggregate:!1,average:!1,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},y=(e,t=2)=>{const r=Math.pow(10,t);return Math.round(e*r*(1+Number.EPSILON))/r},T=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),f=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),W=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 S{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:p,waitTime:p,elu:p};constructor(e,t=g){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??g,this.setTaskStatisticsRequirements(this.opts)}isWorkerNodeReady(e){return this.pool.workerNodes[e].info.ready}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}computeDefaultWorkerWeight(){let e=0;for(const t of o.cpus()){const r=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,r))*Math.pow(10,r)}return Math.round(e/o.cpus().length)}}class N extends S{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){return this.fairShareNextWorkerNodeKey()}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}fairShareNextWorkerNodeKey(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const r=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeReady(t)&&r<e&&(e=r,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}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 v extends S{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeKey=0,this.roundId=0,!0}update(){return!0}choose(){let e,t;for(let r=this.roundId;r<this.roundWeights.length;r++)for(let s=this.nextWorkerNodeKey;s<this.pool.workerNodes.length;s++){const i=this.opts.weights?.[s]??this.defaultWorkerWeight;if(this.isWorkerNodeReady(s)&&i>=this.roundWeights[r]){e=r,t=s;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const r=this.nextWorkerNodeKey;return this.nextWorkerNodeKey===this.pool.workerNodes.length-1?(this.nextWorkerNodeKey=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeKey=this.nextWorkerNodeKey+1,r}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=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 R extends S{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:p};constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastBusyNextWorkerNodeKey()}remove(){return!0}leastBusyNextWorkerNodeKey(){let e=1/0;for(const[t,r]of this.pool.workerNodes.entries()){const s=(r.usage.runTime?.aggregate??0)+(r.usage.waitTime?.aggregate??0);if(this.isWorkerNodeReady(t)&&0===s){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class E extends S{constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastUsedNextWorkerNodeKey()}remove(){return!0}leastUsedNextWorkerNodeKey(){let e=1/0;for(const[t,r]of this.pool.workerNodes.entries()){const s=r.usage.tasks,i=s.executed+s.executing+s.queued;if(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class b extends S{taskStatisticsRequirements={runTime:p,waitTime:p,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastEluNextWorkerNodeKey()}remove(){return!0}leastEluNextWorkerNodeKey(){let e=1/0;for(const[t,r]of this.pool.workerNodes.entries()){const s=r.usage,i=s.elu?.active?.aggregate??0;if(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class C extends S{strategyPolicy={useDynamicWorker:!0};constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeKey=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.roundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1)),!0}roundRobinNextWorkerNodeKey(){return this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.nextWorkerNodeKey}}class I extends S{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:p,elu:p};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=g){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeKey=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.weightedRoundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}weightedRoundRobinNextWorkerNodeKey(){const e=this.workerVirtualTaskRunTime;return e<(this.opts.weights?.[this.nextWorkerNodeKey]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=e+this.getWorkerTaskRunTime(this.nextWorkerNodeKey):(this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.workerVirtualTaskRunTime=0),this.nextWorkerNodeKey}}class O{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=W.ROUND_ROBIN,r=g){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[W.ROUND_ROBIN,new(C.bind(this))(e,r)],[W.LEAST_USED,new(E.bind(this))(e,r)],[W.LEAST_BUSY,new(R.bind(this))(e,r)],[W.LEAST_ELU,new(b.bind(this))(e,r)],[W.FAIR_SHARE,new(N.bind(this))(e,r)],[W.WEIGHTED_ROUND_ROBIN,new(I.bind(this))(e,r)],[W.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(v.bind(this))(e,r)]])}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 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,...r){let s;return arguments.length>=3&&void 0!==t?(s=super.splice(e,t),this.push(...r)):s=2===arguments.length?super.splice(e,t):super.splice(e),s}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 z{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const r=e[t];return++t,{value:r,done:!1}}}}}const K=Object.freeze({cluster:"cluster",thread:"thread"});class P{worker;info;usage;tasksUsage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksUsage=new Map,this.tasksQueue=new z}tasksQueueSize(){return this.tasksQueue.size}tasksQueueMaxSize(){return this.tasksQueue.maxSize}enqueueTask(e){return this.tasksQueue.enqueue(e)}dequeueTask(){return this.tasksQueue.dequeue()}clearTasksQueue(){this.tasksQueue.clear()}resetUsage(){this.usage=this.initWorkerUsage(),this.tasksUsage.clear()}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.get(e)}initWorkerInfo(e,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1}}initWorkerUsage(){const e=()=>this.tasksQueueSize(),t=()=>this.tasksQueueMaxSize();return{tasks:{executed:0,executing:0,get queued(){return e()},get maxQueued(){return t()},failed:0},runTime:{history:new q},waitTime:{history:new q},elu:{idle:{history:new q},active:{history:new q}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const r of this.tasksQueue)r.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new q},waitTime:{history:new q},elu:{idle:{history:new q},active:{history:new q}}}}getWorkerId(e,t){return t===K.thread?e.threadId:t===K.cluster?e.id:void 0}}class M{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;constructor(e,t,r){if(this.numberOfWorkers=e,this.filePath=t,this.opts=r,!this.isMain())throw new Error("Cannot start a pool from a worker!");for(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 c),this.workerChoiceStrategyContext=new O(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=s.performance.now()}checkFilePath(e){if(null==e||"string"!=typeof e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation");if(!i.existsSync(e))throw new Error(`Cannot find the worker file '${e}'`)}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 RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===k.dynamic){if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===t)throw new RangeError("Cannot instantiate a dynamic pool with a pool size equal to zero");if(e===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead")}}checkPoolOptions(e){if(!T(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??W.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??g,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(W).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{version:"2.6.15",type:this.type,worker:this.worker,ready:this.ready,strategy:this.opts.workerChoiceStrategy,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:y(this.utilization)},workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:y(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:y(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:y(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:y(w(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:y(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:y(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:y(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:y(w(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get starting(){return this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.minSize}get ready(){return this.workerNodes.reduce(((e,t)=>!t.info.dynamic&&t.info.ready?e+1:e),0)>=this.minSize}get utilization(){const e=(s.performance.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}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)e.resetUsage(),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.usage.tasks.executing))}async execute(e,t){const i=s.performance.now(),o=this.chooseWorkerNode(),a={name:t??m,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:r.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].usage.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);const r=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await r})))}setupHook(){}beforeTaskExecutionHook(e,t){const r=this.workerNodes[e].usage;++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t);const s=this.workerNodes[e].getTaskWorkerUsage(t.name);++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t)}afterTaskExecutionHook(e,t){const r=this.getWorkerNodeKey(e),s=this.workerNodes[r].usage;this.updateTaskStatisticsWorkerUsage(s,t),this.updateRunTimeWorkerUsage(s,t),this.updateEluWorkerUsage(s,t);const i=this.workerNodes[r].getTaskWorkerUsage(t.taskPerformance?.name??m);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const r=e.tasks;--r.executing,null==t.taskError?++r.executed:++r.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const r=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+r,e.runTime.minimum=Math.min(r,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(r,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),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 r=s.performance.now(),i=r-(t.timestamp??r);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=w(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(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===k.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??l),e.on("error",this.opts.errorHandler??l),e.on("error",(t=>{const r=this.getWorkerNodeKey(e),s=this.getWorkerInfo(r);s.ready=!1,null!=this.emitter&&this.emitter.emit(d.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(s.dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(r)})),e.on("online",this.opts.onlineHandler??l),e.on("exit",this.opts.exitHandler??l),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.addWorkerNode(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const r=this.getWorkerNodeKey(e);var s;s=f.HARD,(t.kill===s||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[r].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[r].usage.tasks.executing&&0===this.tasksQueueSize(r)))&&this.destroyWorker(e)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),this.sendToWorker(e,{checkActive:!0,workerId:t.id}),e}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendWorkerStartupMessage(e),this.setWorkerStatistics(e)}sendWorkerStartupMessage(e){this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,r=1/0;for(const[s,i]of this.workerNodes.entries()){const o=this.getWorkerInfo(s);if(s!==e&&o.ready&&0===i.usage.tasks.queued){t=s;break}s!==e&&o.ready&&i.usage.tasks.queued<r&&(r=i.usage.tasks.queued,t=s)}this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyResponse(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(d.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(d.taskError,e.taskError),t.reject(e.taskError.message)):t.resolve(e.data),this.afterTaskExecutionHook(t.worker,e),this.promiseResponseMap.delete(e.id);const r=this.getWorkerNodeKey(t.worker);!0===this.opts.enableTasksQueue&&this.tasksQueueSize(r)>0&&this.executeTask(r,this.dequeueTask(r)),this.workerChoiceStrategyContext.update(r)}}checkAndEmitEvents(){null!=this.emitter&&(this.busy&&this.emitter.emit(d.busy,this.info),this.type===k.dynamic&&this.full&&this.emitter.emit(d.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new P(e,this.worker);return this.starting&&(t.info.ready=!0),this.workerNodes.push(t)}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].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}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},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class A extends M{opts;constructor(e,t,r={}){super(e,t,r),this.opts=r}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.id}),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 K.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class Q extends M{opts;constructor(e,t,r={}){super(e,t,r),this.opts=r}isMain(){return a.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new a.Worker(this.filePath,{env:a.SHARE_ENV,...this.opts.workerOptions})}get type(){return k.fixed}get worker(){return K.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}const U=6e4,D=f.SOFT;class F extends n.AsyncResource{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,r,s,i={killBehavior:D,maxInactiveTime:U}){super(e),this.isMain=t,this.mainWorker=s,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(r),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??D,this.opts.maxInactiveTime=e.maxInactiveTime??U,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(m,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[r,s]of Object.entries(e)){if("string"!=typeof r)throw new TypeError("A taskFunctions parameter object key is not a string");if("function"!=typeof s)throw new TypeError("A taskFunctions parameter object value is not a function");this.taskFunctions.set(r,s.bind(this)),t&&(this.taskFunctions.set(m,s.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===m)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{return this.taskFunctions.get(e)===this.taskFunctions.get(m)&&this.taskFunctions.set(m,t.bind(this)),this.taskFunctions.set(e,t.bind(this)),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===m)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(m))throw new Error("Cannot remove the task function used as the default task function");return this.taskFunctions.delete(e)}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===m)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(m,this.taskFunctions.get(e)?.bind(this)),!0}catch{return!1}}messageListener(e){e.workerId===this.id&&(null!=e.ready?this.sendReadyResponse():null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&(this.stopCheckActive(),this.emitDestroy()))}sendReadyResponse(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckActive(){this.lastTaskTimestamp=s.performance.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??U)/2)}stopCheckActive(){null!=this.activeInterval&&clearInterval(this.activeInterval)}checkActive(){s.performance.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??U)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let r=this.beginTaskPerformance(t.name);const s=e(t.data);r=this.endTaskPerformance(r),this.sendToMainWorker({data:s,taskPerformance:r,workerId:this.id,id:t.id})}catch(e){const r=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??m,message:r,data:t.data},workerId:this.id,id:t.id})}finally{this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=s.performance.now())}}runAsync(e,t){let r=this.beginTaskPerformance(t.name);e(t.data).then((e=>(r=this.endTaskPerformance(r),this.sendToMainWorker({data:e,taskPerformance:r,workerId:this.id,id:t.id}),null))).catch((e=>{const r=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??m,message:r,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=s.performance.now())})).catch(l)}getTaskFunction(e){e=e??m;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??m,timestamp:s.performance.now(),...this.statistics.elu&&{elu:s.performance.eventLoopUtilization()}}}endTaskPerformance(e){return this.checkStatistics(),{...e,...this.statistics.runTime&&{runTime:s.performance.now()-e.timestamp},...this.statistics.elu&&{elu:s.performance.eventLoopUtilization(e.elu)}}}checkStatistics(){if(null==this.statistics)throw new Error("Performance statistics computation requirements not set")}}exports.ClusterWorker=class extends F{constructor(e,r={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,r)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}},exports.DynamicClusterPool=class extends A{max;constructor(e,t,r,s={}){super(e,r,s),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.DynamicThreadPool=class extends Q{max;constructor(e,t,r,s={}){super(e,r,s),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return k.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}},exports.FixedClusterPool=A,exports.FixedThreadPool=Q,exports.KillBehaviors=f,exports.Measurements=x,exports.PoolEvents=d,exports.PoolTypes=k,exports.ThreadWorker=class extends F{constructor(e,t={}){super("worker-thread-pool:poolifier",a.isMainThread,e,a.parentPort,t)}get id(){return a.threadId}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}},exports.WorkerChoiceStrategies=W,exports.WorkerTypes=K,exports.availableParallelism=()=>{let e=1;try{e=h.availableParallelism()}catch{const t=h.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e};
package/lib/index.mjs CHANGED
@@ -1 +1 @@
1
- import{EventEmitter as e}from"node:events";import t from"node:cluster";import{randomUUID as s}from"node:crypto";import{performance as r}from"node:perf_hooks";import*as i from"node:os";import{cpus as o}from"node:os";import{isMainThread as a,Worker as n,SHARE_ENV as h,parentPort as u,threadId as k}from"node:worker_threads";import{AsyncResource as c}from"node:async_hooks";const d=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class m extends e{}const l=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),g="default",w=Object.freeze((()=>{})),p={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},T={aggregate:!1,average:!1,median:!1},f=()=>{let e=1;try{e=i.availableParallelism()}catch{const t=i.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},y=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,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},S=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),x=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),N=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"}),v=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class I{pool;opts;nextWorkerNodeId=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:T,waitTime:T,elu:T};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){this.opts=e??p,this.setTaskStatisticsRequirements(this.opts)}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}computeDefaultWorkerWeight(){let e=0;for(const t of o()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/o().length)}}class E extends I{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,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===v.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class R extends I{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 b extends I{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:T};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.usage.runTime?.aggregate??0)+(s.usage.waitTime?.aggregate??0);if(0===r){this.nextWorkerNodeId=t;break}r<e&&(e=r,this.nextWorkerNodeId=t)}return this.nextWorkerNodeId}remove(){return!0}}class C extends I{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.usage.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 O extends I{taskStatisticsRequirements={runTime:T,waitTime:T,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.usage,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 z extends I{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 q extends I{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:T};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 M{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=N.ROUND_ROBIN,s=p){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[N.ROUND_ROBIN,new(z.bind(this))(e,s)],[N.LEAST_USED,new(C.bind(this))(e,s)],[N.LEAST_BUSY,new(b.bind(this))(e,s)],[N.LEAST_ELU,new(O.bind(this))(e,s)],[N.FAIR_SHARE,new(E.bind(this))(e,s)],[N.WEIGHTED_ROUND_ROBIN,new(q.bind(this))(e,s)],[N.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(R.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 A 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 Q{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const P=Object.freeze({cluster:"cluster",thread:"thread"});class U{worker;info;usage;tasksUsage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksUsage=new Map,this.tasksQueue=new Q}tasksQueueSize(){return this.tasksQueue.size}tasksQueueMaxSize(){return this.tasksQueue.maxSize}enqueueTask(e){return this.tasksQueue.enqueue(e)}dequeueTask(){return this.tasksQueue.dequeue()}clearTasksQueue(){this.tasksQueue.clear()}resetUsage(){this.usage=this.initWorkerUsage(),this.tasksUsage.clear()}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.get(e)}initWorkerInfo(e,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1}}initWorkerUsage(){const e=()=>this.tasksQueueSize(),t=()=>this.tasksQueueMaxSize();return{tasks:{executed:0,executing:0,get queued(){return e()},get maxQueued(){return t()},failed:0},runTime:{history:new A},waitTime:{history:new A},elu:{idle:{history:new A},active:{history:new A}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new A},waitTime:{history:new A},elu:{idle:{history:new A},active:{history:new A}}}}getWorkerId(e,t){return t===P.thread?e.threadId:t===P.cluster?e.id:void 0}}class D{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 m),this.workerChoiceStrategyContext=new M(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.length<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.now()}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===d.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===d.dynamic){if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===e&&0===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size and a maximum pool size equal to zero");if(e===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead")}}checkPoolOptions(e){if(!S(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??N.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(N).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!S(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(v).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!S(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{version:"2.6.14",type:this.type,worker:this.worker,ready:this.ready,strategy:this.opts.workerChoiceStrategy,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:W(this.utilization)},workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:W(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:W(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:W(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:W(y(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:W(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:W(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:W(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:W(y(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get starting(){return this.workerNodes.length<this.minSize||this.workerNodes.length>=this.minSize&&this.workerNodes.some((e=>!e.info.ready))}get ready(){return this.workerNodes.length>=this.minSize&&this.workerNodes.every((e=>e.info.ready))}get utilization(){const e=(r.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}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)e.resetUsage(),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.usage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),a={name:t??g,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:s()},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].usage.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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.getWorkerNodeKey(e),r=this.workerNodes[s].usage;this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t);const i=this.workerNodes[s].getTaskWorkerUsage(t.taskPerformance?.name??g);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&null!=t.taskPerformance?.runTime&&(e.runTime.history.push(t.taskPerformance.runTime),e.runTime.median=y(e.runTime.history))}}updateWaitTimeWorkerUsage(e,t){const s=r.now(),i=s-(t.timestamp??s);this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&(e.waitTime.aggregate=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=y(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(e.elu.idle.history.push(t.taskPerformance.elu.idle),e.elu.active.history.push(t.taskPerformance.elu.active),e.elu.idle.median=y(e.elu.idle.history),e.elu.active.median=y(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===d.dynamic&&!this.full&&this.internalBusy()}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id}),this.setWorkerStatistics(e)}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??w),e.on("error",this.opts.errorHandler??w),e.on("error",(t=>{null!=this.emitter&&this.emitter.emit(l.error,t),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(e),!0!==this.opts.restartWorkerOnError||this.starting||(this.getWorkerInfo(this.getWorkerNodeKey(e)).dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker())})),e.on("online",this.opts.onlineHandler??w),e.on("exit",this.opts.exitHandler??w),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.pushWorkerNode(e),this.afterWorkerSetup(e),e}redistributeQueuedTasks(e){const t=this.getWorkerNodeKey(e);for(;this.tasksQueueSize(t)>0;){let e=t,s=1/0;for(const[r,i]of this.workerNodes.entries()){if(r!==t&&0===i.usage.tasks.queued){e=r;break}r!==t&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,e=r)}this.enqueueTask(e,this.dequeueTask(t))}}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=x.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.sendToWorker(e,{checkAlive:!0,workerId:t.id}),e}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready&&null!=e.workerId?this.handleWorkerReadyMessage(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyMessage(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(l.ready,this.info)}handleTaskExecutionResponse(e){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===d.dynamic&&this.full&&this.emitter.emit(l.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}pushWorkerNode(e){return this.workerNodes.push(new U(e,this.worker))}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].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}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},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class F extends D{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:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return d.fixed}get worker(){return P.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class _ extends F{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return d.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class V extends D{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return a}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new n(this.filePath,{env:h,...this.opts.workerOptions})}get type(){return d.fixed}get worker(){return P.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class B extends V{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return d.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const j=6e4,L=x.SOFT;class H extends c{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;aliveInterval;constructor(e,t,s,r,i={killBehavior:L,maxInactiveTime:j}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??L,this.opts.maxInactiveTime=e.maxInactiveTime??j,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(g,e.bind(this));else{if(!S(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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");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(g,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===g)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{return this.taskFunctions.get(e)===this.taskFunctions.get(g)&&this.taskFunctions.set(g,t.bind(this)),this.taskFunctions.set(e,t.bind(this)),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===g)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(g))throw new Error("Cannot remove the task function used as the default task function");return this.taskFunctions.delete(e)}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===g)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(g,this.taskFunctions.get(e)?.bind(this)),!0}catch{return!1}}messageListener(e){e.workerId===this.id&&(null!=e.ready?this.workerReady():null!=e.statistics?this.statistics=e.statistics:null!=e.checkAlive?e.checkAlive?this.startCheckAlive():this.stopCheckAlive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&(this.stopCheckAlive(),this.emitDestroy()))}workerReady(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckAlive(){this.lastTaskTimestamp=r.now(),this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??j)/2),this.checkAlive.bind(this)()}stopCheckAlive(){null!=this.aliveInterval&&clearInterval(this.aliveInterval)}checkAlive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??j)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let s=this.beginTaskPerformance(t.name);const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??g,message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.now())}}runAsync(e,t){let s=this.beginTaskPerformance(t.name);e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??g,message:s,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.isMain||null==this.aliveInterval||(this.lastTaskTimestamp=r.now())})).catch(w)}getTaskFunction(e){e=e??g;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??g,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 K extends H{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class $ extends H{constructor(e,t={}){super("worker-thread-pool:poolifier",a,e,u,t)}get id(){return k}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}}export{K as ClusterWorker,_ as DynamicClusterPool,B as DynamicThreadPool,F as FixedClusterPool,V as FixedThreadPool,x as KillBehaviors,v as Measurements,l as PoolEvents,d as PoolTypes,$ as ThreadWorker,N as WorkerChoiceStrategies,P as WorkerTypes,f as availableParallelism};
1
+ import{EventEmitter as e}from"node:events";import t from"node:cluster";import{randomUUID as s}from"node:crypto";import{performance as r}from"node:perf_hooks";import{existsSync as i}from"node:fs";import*as o from"node:os";import{cpus as a}from"node:os";import{isMainThread as n,Worker as h,SHARE_ENV as u,parentPort as k,threadId as c}from"node:worker_threads";import{AsyncResource as d}from"node:async_hooks";const m=Object.freeze({fixed:"fixed",dynamic:"dynamic"});class l extends e{}const g=Object.freeze({full:"full",ready:"ready",busy:"busy",error:"error",taskError:"taskError"}),w="default",p=Object.freeze((()=>{})),y={runTime:{median:!1},waitTime:{median:!1},elu:{median:!1}},T={aggregate:!1,average:!1,median:!1},f=()=>{let e=1;try{e=o.availableParallelism()}catch{const t=o.cpus();Array.isArray(t)&&t.length>0&&(e=t.length)}return e},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},S=(e,t=2)=>{const s=Math.pow(10,t);return Math.round(e*s*(1+Number.EPSILON))/s},x=e=>"object"==typeof e&&null!==e&&e?.constructor===Object&&"[object Object]"===Object.prototype.toString.call(e),N=Object.freeze({SOFT:"SOFT",HARD:"HARD"}),v=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"}),R=Object.freeze({runTime:"runTime",waitTime:"waitTime",elu:"elu"});class E{pool;opts;nextWorkerNodeKey=0;strategyPolicy={useDynamicWorker:!1};taskStatisticsRequirements={runTime:T,waitTime:T,elu:T};constructor(e,t=y){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??y,this.setTaskStatisticsRequirements(this.opts)}isWorkerNodeReady(e){return this.pool.workerNodes[e].info.ready}getWorkerTaskRunTime(e){return this.taskStatisticsRequirements.runTime.median?this.pool.workerNodes[e].usage.runTime?.median??0:this.pool.workerNodes[e].usage.runTime?.average??0}getWorkerTaskWaitTime(e){return this.taskStatisticsRequirements.waitTime.median?this.pool.workerNodes[e].usage.waitTime?.median??0:this.pool.workerNodes[e].usage.waitTime?.average??0}getWorkerTaskElu(e){return this.taskStatisticsRequirements.elu.median?this.pool.workerNodes[e].usage.elu.active?.median??0:this.pool.workerNodes[e].usage.elu.active?.average??0}computeDefaultWorkerWeight(){let e=0;for(const t of a()){const s=t.speed.toString().length-1;e+=1/(t.speed/Math.pow(10,s))*Math.pow(10,s)}return Math.round(e/a().length)}}class b extends E{taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:{aggregate:!0,average:!0,median:!1}};workersVirtualTaskEndTimestamp=[];constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.workersVirtualTaskEndTimestamp=[],!0}update(e){return this.computeWorkerVirtualTaskEndTimestamp(e),!0}choose(){return this.fairShareNextWorkerNodeKey()}remove(e){return this.workersVirtualTaskEndTimestamp.splice(e,1),!0}fairShareNextWorkerNodeKey(){let e=1/0;for(const[t]of this.pool.workerNodes.entries()){null==this.workersVirtualTaskEndTimestamp[t]&&this.computeWorkerVirtualTaskEndTimestamp(t);const s=this.workersVirtualTaskEndTimestamp[t];this.isWorkerNodeReady(t)&&s<e&&(e=s,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}computeWorkerVirtualTaskEndTimestamp(e){this.workersVirtualTaskEndTimestamp[e]=this.getWorkerVirtualTaskEndTimestamp(e,this.getWorkerVirtualTaskStartTimestamp(e))}getWorkerVirtualTaskEndTimestamp(e,t){return t+(this.opts.measurement===R.elu?this.getWorkerTaskElu(e):this.getWorkerTaskRunTime(e))}getWorkerVirtualTaskStartTimestamp(e){return Math.max(performance.now(),this.workersVirtualTaskEndTimestamp[e]??-1/0)}}class C extends E{strategyPolicy={useDynamicWorker:!0};roundId=0;roundWeights;defaultWorkerWeight;constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight(),this.roundWeights=this.getRoundWeights()}reset(){return this.nextWorkerNodeKey=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.nextWorkerNodeKey;r<this.pool.workerNodes.length;r++){const i=this.opts.weights?.[r]??this.defaultWorkerWeight;if(this.isWorkerNodeReady(r)&&i>=this.roundWeights[s]){e=s,t=r;break}}this.roundId=e??0,this.nextWorkerNodeKey=t??0;const s=this.nextWorkerNodeKey;return this.nextWorkerNodeKey===this.pool.workerNodes.length-1?(this.nextWorkerNodeKey=0,this.roundId=this.roundId===this.roundWeights.length-1?0:this.roundId+1):this.nextWorkerNodeKey=this.nextWorkerNodeKey+1,s}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=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 E{taskStatisticsRequirements={runTime:{aggregate:!0,average:!1,median:!1},waitTime:{aggregate:!0,average:!1,median:!1},elu:T};constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastBusyNextWorkerNodeKey()}remove(){return!0}leastBusyNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=(s.usage.runTime?.aggregate??0)+(s.usage.waitTime?.aggregate??0);if(this.isWorkerNodeReady(t)&&0===r){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&r<e&&(e=r,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class O extends E{constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastUsedNextWorkerNodeKey()}remove(){return!0}leastUsedNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage.tasks,i=r.executed+r.executing+r.queued;if(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class z extends E{taskStatisticsRequirements={runTime:T,waitTime:T,elu:{aggregate:!0,average:!1,median:!1}};constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return!0}update(){return!0}choose(){return this.leastEluNextWorkerNodeKey()}remove(){return!0}leastEluNextWorkerNodeKey(){let e=1/0;for(const[t,s]of this.pool.workerNodes.entries()){const r=s.usage,i=r.elu?.active?.aggregate??0;if(this.isWorkerNodeReady(t)&&0===i){this.nextWorkerNodeKey=t;break}this.isWorkerNodeReady(t)&&i<e&&(e=i,this.nextWorkerNodeKey=t)}return this.nextWorkerNodeKey}}class q extends E{strategyPolicy={useDynamicWorker:!0};constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts)}reset(){return this.nextWorkerNodeKey=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.roundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1)),!0}roundRobinNextWorkerNodeKey(){return this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.nextWorkerNodeKey}}class K extends E{strategyPolicy={useDynamicWorker:!0};taskStatisticsRequirements={runTime:{aggregate:!0,average:!0,median:!1},waitTime:T,elu:T};defaultWorkerWeight;workerVirtualTaskRunTime=0;constructor(e,t=y){super(e,t),this.setTaskStatisticsRequirements(this.opts),this.defaultWorkerWeight=this.computeDefaultWorkerWeight()}reset(){return this.nextWorkerNodeKey=0,this.workerVirtualTaskRunTime=0,!0}update(){return!0}choose(){const e=this.nextWorkerNodeKey;do{this.weightedRoundRobinNextWorkerNodeKey()}while(!this.isWorkerNodeReady(this.nextWorkerNodeKey));return e}remove(e){return this.nextWorkerNodeKey===e&&(0===this.pool.workerNodes.length?this.nextWorkerNodeKey=0:this.nextWorkerNodeKey>this.pool.workerNodes.length-1&&(this.nextWorkerNodeKey=this.pool.workerNodes.length-1),this.workerVirtualTaskRunTime=0),!0}weightedRoundRobinNextWorkerNodeKey(){const e=this.workerVirtualTaskRunTime;return e<(this.opts.weights?.[this.nextWorkerNodeKey]??this.defaultWorkerWeight)?this.workerVirtualTaskRunTime=e+this.getWorkerTaskRunTime(this.nextWorkerNodeKey):(this.nextWorkerNodeKey=this.nextWorkerNodeKey===this.pool.workerNodes.length-1?0:this.nextWorkerNodeKey+1,this.workerVirtualTaskRunTime=0),this.nextWorkerNodeKey}}class M{workerChoiceStrategy;workerChoiceStrategies;constructor(e,t=v.ROUND_ROBIN,s=y){this.workerChoiceStrategy=t,this.execute=this.execute.bind(this),this.workerChoiceStrategies=new Map([[v.ROUND_ROBIN,new(q.bind(this))(e,s)],[v.LEAST_USED,new(O.bind(this))(e,s)],[v.LEAST_BUSY,new(I.bind(this))(e,s)],[v.LEAST_ELU,new(z.bind(this))(e,s)],[v.FAIR_SHARE,new(b.bind(this))(e,s)],[v.WEIGHTED_ROUND_ROBIN,new(K.bind(this))(e,s)],[v.INTERLEAVED_WEIGHTED_ROUND_ROBIN,new(C.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 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 A{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}[Symbol.iterator](){const e=this.items;let t=this.offset;return{next:()=>{if(t>=e.length)return{value:void 0,done:!0};const s=e[t];return++t,{value:s,done:!1}}}}}const P=Object.freeze({cluster:"cluster",thread:"thread"});class U{worker;info;usage;tasksUsage;tasksQueue;constructor(e,t){this.worker=e,this.info=this.initWorkerInfo(e,t),this.usage=this.initWorkerUsage(),this.tasksUsage=new Map,this.tasksQueue=new A}tasksQueueSize(){return this.tasksQueue.size}tasksQueueMaxSize(){return this.tasksQueue.maxSize}enqueueTask(e){return this.tasksQueue.enqueue(e)}dequeueTask(){return this.tasksQueue.dequeue()}clearTasksQueue(){this.tasksQueue.clear()}resetUsage(){this.usage=this.initWorkerUsage(),this.tasksUsage.clear()}getTaskWorkerUsage(e){return this.tasksUsage.has(e)||this.tasksUsage.set(e,this.initTaskWorkerUsage(e)),this.tasksUsage.get(e)}initWorkerInfo(e,t){return{id:this.getWorkerId(e,t),type:t,dynamic:!1,ready:!1}}initWorkerUsage(){const e=()=>this.tasksQueueSize(),t=()=>this.tasksQueueMaxSize();return{tasks:{executed:0,executing:0,get queued(){return e()},get maxQueued(){return t()},failed:0},runTime:{history:new Q},waitTime:{history:new Q},elu:{idle:{history:new Q},active:{history:new Q}}}}initTaskWorkerUsage(e){const t=()=>{let t=0;for(const s of this.tasksQueue)s.name===e&&++t;return t};return{tasks:{executed:0,executing:0,get queued(){return t()},failed:0},runTime:{history:new Q},waitTime:{history:new Q},elu:{idle:{history:new Q},active:{history:new Q}}}}getWorkerId(e,t){return t===P.thread?e.threadId:t===P.cluster?e.id:void 0}}class D{numberOfWorkers;filePath;opts;workerNodes=[];emitter;promiseResponseMap=new Map;workerChoiceStrategyContext;startTimestamp;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!");for(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 M(this,this.opts.workerChoiceStrategy,this.opts.workerChoiceStrategyOptions),this.setupHook();this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.numberOfWorkers;)this.createAndSetupWorker();this.startTimestamp=r.now()}checkFilePath(e){if(null==e||"string"!=typeof e||"string"==typeof e&&0===e.trim().length)throw new Error("Please specify a file with a worker implementation");if(!i(e))throw new Error(`Cannot find the worker file '${e}'`)}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===m.fixed&&0===e)throw new RangeError("Cannot instantiate a fixed pool with zero worker")}checkDynamicPoolSize(e,t){if(this.type===m.dynamic){if(e>t)throw new RangeError("Cannot instantiate a dynamic pool with a maximum pool size inferior to the minimum pool size");if(0===t)throw new RangeError("Cannot instantiate a dynamic pool with a pool size equal to zero");if(e===t)throw new RangeError("Cannot instantiate a dynamic pool with a minimum pool size equal to the maximum pool size. Use a fixed pool instead")}}checkPoolOptions(e){if(!x(e))throw new TypeError("Invalid pool options: must be a plain object");this.opts.workerChoiceStrategy=e.workerChoiceStrategy??v.ROUND_ROBIN,this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy),this.opts.workerChoiceStrategyOptions=e.workerChoiceStrategyOptions??y,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(v).includes(e))throw new Error(`Invalid worker choice strategy '${e}'`)}checkValidWorkerChoiceStrategyOptions(e){if(!x(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(R).includes(e.measurement))throw new Error(`Invalid worker choice strategy options: invalid measurement '${e.measurement}'`)}checkValidTasksQueueOptions(e){if(null!=e&&!x(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{version:"2.6.15",type:this.type,worker:this.worker,ready:this.ready,strategy:this.opts.workerChoiceStrategy,minSize:this.minSize,maxSize:this.maxSize,...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{utilization:S(this.utilization)},workerNodes:this.workerNodes.length,idleWorkerNodes:this.workerNodes.reduce(((e,t)=>0===t.usage.tasks.executing?e+1:e),0),busyWorkerNodes:this.workerNodes.reduce(((e,t)=>t.usage.tasks.executing>0?e+1:e),0),executedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executed),0),executingTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.executing),0),queuedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.queued),0),maxQueuedTasks:this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.maxQueued??0)),0),failedTasks:this.workerNodes.reduce(((e,t)=>e+t.usage.tasks.failed),0),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate&&{runTime:{minimum:S(Math.min(...this.workerNodes.map((e=>e.usage.runTime?.minimum??1/0)))),maximum:S(Math.max(...this.workerNodes.map((e=>e.usage.runTime?.maximum??-1/0)))),average:S(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median&&{median:S(W(this.workerNodes.map((e=>e.usage.runTime?.median??0))))}}},...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.aggregate&&{waitTime:{minimum:S(Math.min(...this.workerNodes.map((e=>e.usage.waitTime?.minimum??1/0)))),maximum:S(Math.max(...this.workerNodes.map((e=>e.usage.waitTime?.maximum??-1/0)))),average:S(this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0)/this.workerNodes.reduce(((e,t)=>e+(t.usage.tasks?.executed??0)),0)),...this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&{median:S(W(this.workerNodes.map((e=>e.usage.waitTime?.median??0))))}}}}}get starting(){return this.workerNodes.reduce(((e,t)=>t.info.dynamic?e:e+1),0)<this.minSize}get ready(){return this.workerNodes.reduce(((e,t)=>!t.info.dynamic&&t.info.ready?e+1:e),0)>=this.minSize}get utilization(){const e=(r.now()-this.startTimestamp)*this.maxSize;return(this.workerNodes.reduce(((e,t)=>e+(t.usage.runTime?.aggregate??0)),0)+this.workerNodes.reduce(((e,t)=>e+(t.usage.waitTime?.aggregate??0)),0))/e}getWorkerById(e){return this.workerNodes.find((t=>t.info.id===e))?.worker}checkMessageWorkerId(e){if(null!=e.workerId&&null==this.getWorkerById(e.workerId))throw new Error(`Worker message received from unknown worker '${e.workerId}'`)}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)e.resetUsage(),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.usage.tasks.executing))}async execute(e,t){const i=r.now(),o=this.chooseWorkerNode(),a={name:t??w,data:e??{},timestamp:i,workerId:this.getWorkerInfo(o).id,id:s()},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].usage.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);const s=new Promise((t=>{e.worker.on("exit",(()=>{t()}))}));await this.destroyWorker(e.worker),await s})))}setupHook(){}beforeTaskExecutionHook(e,t){const s=this.workerNodes[e].usage;++s.tasks.executing,this.updateWaitTimeWorkerUsage(s,t);const r=this.workerNodes[e].getTaskWorkerUsage(t.name);++r.tasks.executing,this.updateWaitTimeWorkerUsage(r,t)}afterTaskExecutionHook(e,t){const s=this.getWorkerNodeKey(e),r=this.workerNodes[s].usage;this.updateTaskStatisticsWorkerUsage(r,t),this.updateRunTimeWorkerUsage(r,t),this.updateEluWorkerUsage(r,t);const i=this.workerNodes[s].getTaskWorkerUsage(t.taskPerformance?.name??w);this.updateTaskStatisticsWorkerUsage(i,t),this.updateRunTimeWorkerUsage(i,t),this.updateEluWorkerUsage(i,t)}updateTaskStatisticsWorkerUsage(e,t){const s=e.tasks;--s.executing,null==t.taskError?++s.executed:++s.failed}updateRunTimeWorkerUsage(e,t){if(this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.aggregate){const s=t.taskPerformance?.runTime??0;e.runTime.aggregate=(e.runTime.aggregate??0)+s,e.runTime.minimum=Math.min(s,e.runTime?.minimum??1/0),e.runTime.maximum=Math.max(s,e.runTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.average&&0!==e.tasks.executed&&(e.runTime.average=e.runTime.aggregate/e.tasks.executed),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=(e.waitTime?.aggregate??0)+i,e.waitTime.minimum=Math.min(i,e.waitTime?.minimum??1/0),e.waitTime.maximum=Math.max(i,e.waitTime?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.average&&0!==e.tasks.executed&&(e.waitTime.average=e.waitTime.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime.median&&(e.waitTime.history.push(i),e.waitTime.median=W(e.waitTime.history)))}updateEluWorkerUsage(e,t){this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.aggregate&&null!=t.taskPerformance?.elu&&(e.elu.idle.aggregate=(e.elu.idle?.aggregate??0)+t.taskPerformance.elu.idle,e.elu.active.aggregate=(e.elu.active?.aggregate??0)+t.taskPerformance.elu.active,null!=e.elu.utilization?e.elu.utilization=(e.elu.utilization+t.taskPerformance.elu.utilization)/2:e.elu.utilization=t.taskPerformance.elu.utilization,e.elu.idle.minimum=Math.min(t.taskPerformance.elu.idle,e.elu.idle?.minimum??1/0),e.elu.idle.maximum=Math.max(t.taskPerformance.elu.idle,e.elu.idle?.maximum??-1/0),e.elu.active.minimum=Math.min(t.taskPerformance.elu.active,e.elu.active?.minimum??1/0),e.elu.active.maximum=Math.max(t.taskPerformance.elu.active,e.elu.active?.maximum??-1/0),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.average&&0!==e.tasks.executed&&(e.elu.idle.average=e.elu.idle.aggregate/e.tasks.executed,e.elu.active.average=e.elu.active.aggregate/e.tasks.executed),this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu.median&&(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===m.dynamic&&!this.full&&this.internalBusy()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??p),e.on("error",this.opts.errorHandler??p),e.on("error",(t=>{const s=this.getWorkerNodeKey(e),r=this.getWorkerInfo(s);r.ready=!1,null!=this.emitter&&this.emitter.emit(g.error,t),!0!==this.opts.restartWorkerOnError||this.starting||(r.dynamic?this.createAndSetupDynamicWorker():this.createAndSetupWorker()),!0===this.opts.enableTasksQueue&&this.redistributeQueuedTasks(s)})),e.on("online",this.opts.onlineHandler??p),e.on("exit",this.opts.exitHandler??p),e.once("exit",(()=>{this.removeWorkerNode(e)})),this.addWorkerNode(e),this.afterWorkerSetup(e),e}createAndSetupDynamicWorker(){const e=this.createAndSetupWorker();this.registerWorkerMessageListener(e,(t=>{const s=this.getWorkerNodeKey(e);var r;r=N.HARD,(t.kill===r||null!=t.kill&&(!1===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing||!0===this.opts.enableTasksQueue&&0===this.workerNodes[s].usage.tasks.executing&&0===this.tasksQueueSize(s)))&&this.destroyWorker(e)}));const t=this.getWorkerInfo(this.getWorkerNodeKey(e));return t.dynamic=!0,this.workerChoiceStrategyContext.getStrategyPolicy().useDynamicWorker&&(t.ready=!0),this.sendToWorker(e,{checkActive:!0,workerId:t.id}),e}registerWorkerMessageListener(e,t){e.on("message",t)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,this.workerListener()),this.sendWorkerStartupMessage(e),this.setWorkerStatistics(e)}sendWorkerStartupMessage(e){this.sendToWorker(e,{ready:!1,workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}redistributeQueuedTasks(e){for(;this.tasksQueueSize(e)>0;){let t=e,s=1/0;for(const[r,i]of this.workerNodes.entries()){const o=this.getWorkerInfo(r);if(r!==e&&o.ready&&0===i.usage.tasks.queued){t=r;break}r!==e&&o.ready&&i.usage.tasks.queued<s&&(s=i.usage.tasks.queued,t=r)}this.enqueueTask(t,this.dequeueTask(e))}}workerListener(){return e=>{this.checkMessageWorkerId(e),null!=e.ready?this.handleWorkerReadyResponse(e):null!=e.id&&this.handleTaskExecutionResponse(e)}}handleWorkerReadyResponse(e){const t=this.getWorkerById(e.workerId);this.getWorkerInfo(this.getWorkerNodeKey(t)).ready=e.ready,null!=this.emitter&&this.ready&&this.emitter.emit(g.ready,this.info)}handleTaskExecutionResponse(e){const t=this.promiseResponseMap.get(e.id);if(null!=t){null!=e.taskError?(null!=this.emitter&&this.emitter.emit(g.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(g.busy,this.info),this.type===m.dynamic&&this.full&&this.emitter.emit(g.full,this.info))}getWorkerInfo(e){return this.workerNodes[e].info}addWorkerNode(e){const t=new U(e,this.worker);return this.starting&&(t.info.ready=!0),this.workerNodes.push(t)}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].enqueueTask(t)}dequeueTask(e){return this.workerNodes[e].dequeueTask()}tasksQueueSize(e){return this.workerNodes[e].tasksQueueSize()}flushTasksQueue(e){for(;this.tasksQueueSize(e)>0;)this.executeTask(e,this.dequeueTask(e));this.workerNodes[e].clearTasksQueue()}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},workerId:this.getWorkerInfo(this.getWorkerNodeKey(e)).id})}}class F extends D{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:!0,workerId:e.id}),e.on("disconnect",(()=>{e.kill()})),e.disconnect()}sendToWorker(e,t){e.send(t)}createWorker(){return t.fork(this.opts.env)}get type(){return m.fixed}get worker(){return P.cluster}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class _ extends F{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return m.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}class V extends D{opts;constructor(e,t,s={}){super(e,t,s),this.opts=s}isMain(){return n}async destroyWorker(e){this.sendToWorker(e,{kill:!0,workerId:e.threadId}),await e.terminate()}sendToWorker(e,t){e.postMessage(t)}createWorker(){return new h(this.filePath,{env:u,...this.opts.workerOptions})}get type(){return m.fixed}get worker(){return P.thread}get minSize(){return this.numberOfWorkers}get maxSize(){return this.numberOfWorkers}get busy(){return this.internalBusy()}}class B extends V{max;constructor(e,t,s,r={}){super(e,s,r),this.max=t,this.checkDynamicPoolSize(this.numberOfWorkers,this.max)}get type(){return m.dynamic}get maxSize(){return this.max}get busy(){return this.full&&this.internalBusy()}}const j=6e4,L=N.SOFT;class H extends d{isMain;mainWorker;opts;taskFunctions;lastTaskTimestamp;statistics;activeInterval;constructor(e,t,s,r,i={killBehavior:L,maxInactiveTime:j}){super(e),this.isMain=t,this.mainWorker=r,this.opts=i,this.checkWorkerOptions(this.opts),this.checkTaskFunctions(s),this.isMain||this.mainWorker?.on("message",this.messageListener.bind(this))}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??L,this.opts.maxInactiveTime=e.maxInactiveTime??j,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(w,e.bind(this));else{if(!x(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("string"!=typeof s)throw new TypeError("A taskFunctions parameter object key is not a string");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(w,r.bind(this)),t=!1)}if(t)throw new Error("taskFunctions parameter object is empty")}}}hasTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");return this.taskFunctions.has(e)}addTaskFunction(e,t){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===w)throw new Error("Cannot add a task function with the default reserved name");if("function"!=typeof t)throw new TypeError("fn parameter is not a function");try{return this.taskFunctions.get(e)===this.taskFunctions.get(w)&&this.taskFunctions.set(w,t.bind(this)),this.taskFunctions.set(e,t.bind(this)),!0}catch{return!1}}removeTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===w)throw new Error("Cannot remove the task function with the default reserved name");if(this.taskFunctions.get(e)===this.taskFunctions.get(w))throw new Error("Cannot remove the task function used as the default task function");return this.taskFunctions.delete(e)}setDefaultTaskFunction(e){if("string"!=typeof e)throw new TypeError("name parameter is not a string");if(e===w)throw new Error("Cannot set the default task function reserved name as the default task function");if(!this.taskFunctions.has(e))throw new Error("Cannot set the default task function to a non-existing task function");try{return this.taskFunctions.set(w,this.taskFunctions.get(e)?.bind(this)),!0}catch{return!1}}messageListener(e){e.workerId===this.id&&(null!=e.ready?this.sendReadyResponse():null!=e.statistics?this.statistics=e.statistics:null!=e.checkActive?e.checkActive?this.startCheckActive():this.stopCheckActive():null!=e.id&&null!=e.data?this.run(e):!0===e.kill&&(this.stopCheckActive(),this.emitDestroy()))}sendReadyResponse(){!this.isMain&&this.sendToMainWorker({ready:!0,workerId:this.id})}startCheckActive(){this.lastTaskTimestamp=r.now(),this.activeInterval=setInterval(this.checkActive.bind(this),(this.opts.maxInactiveTime??j)/2)}stopCheckActive(){null!=this.activeInterval&&clearInterval(this.activeInterval)}checkActive(){r.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??j)&&this.sendToMainWorker({kill:this.opts.killBehavior,workerId:this.id})}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker not set");return this.mainWorker}handleError(e){return e instanceof Error?e.message:e}run(e){const t=this.getTaskFunction(e.name);(e=>"function"==typeof e&&"AsyncFunction"===e.constructor.name)(t)?this.runInAsyncScope(this.runAsync.bind(this),this,t,e):this.runInAsyncScope(this.runSync.bind(this),this,t,e)}runSync(e,t){try{let s=this.beginTaskPerformance(t.name);const r=e(t.data);s=this.endTaskPerformance(s),this.sendToMainWorker({data:r,taskPerformance:s,workerId:this.id,id:t.id})}catch(e){const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??w,message:s,data:t.data},workerId:this.id,id:t.id})}finally{this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=r.now())}}runAsync(e,t){let s=this.beginTaskPerformance(t.name);e(t.data).then((e=>(s=this.endTaskPerformance(s),this.sendToMainWorker({data:e,taskPerformance:s,workerId:this.id,id:t.id}),null))).catch((e=>{const s=this.handleError(e);this.sendToMainWorker({taskError:{name:t.name??w,message:s,data:t.data},workerId:this.id,id:t.id})})).finally((()=>{this.isMain||null==this.activeInterval||(this.lastTaskTimestamp=r.now())})).catch(p)}getTaskFunction(e){e=e??w;const t=this.taskFunctions.get(e);if(null==t)throw new Error(`Task function '${e}' not found`);return t}beginTaskPerformance(e){return this.checkStatistics(),{name:e??w,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 $ extends H{constructor(e,s={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,s)}get id(){return this.getMainWorker().id}sendToMainWorker(e){this.getMainWorker().send(e)}}class G extends H{constructor(e,t={}){super("worker-thread-pool:poolifier",n,e,k,t)}get id(){return c}sendToMainWorker(e){this.getMainWorker().postMessage(e)}handleError(e){return e}}export{$ as ClusterWorker,_ as DynamicClusterPool,B as DynamicThreadPool,F as FixedClusterPool,V as FixedThreadPool,N as KillBehaviors,R as Measurements,g as PoolEvents,m as PoolTypes,G as ThreadWorker,v as WorkerChoiceStrategies,P as WorkerTypes,f as availableParallelism};
@@ -85,6 +85,12 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
85
85
  * @returns The worker if found in the pool worker nodes, `undefined` otherwise.
86
86
  */
87
87
  private getWorkerById;
88
+ /**
89
+ * Checks if the worker id sent in the received message from a worker is valid.
90
+ *
91
+ * @param message - The received message.
92
+ * @throws {@link https://nodejs.org/api/errors.html#class-error} If the worker id is invalid.
93
+ */
88
94
  private checkMessageWorkerId;
89
95
  /**
90
96
  * Gets the given worker its worker node key.
@@ -182,46 +188,47 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
182
188
  * @param message - The message.
183
189
  */
184
190
  protected abstract sendToWorker(worker: Worker, message: MessageValue<Data>): void;
185
- /**
186
- * Registers a listener callback on the given worker.
187
- *
188
- * @param worker - The worker which should register a listener.
189
- * @param listener - The message listener callback.
190
- */
191
- private registerWorkerMessageListener;
192
191
  /**
193
192
  * Creates a new worker.
194
193
  *
195
194
  * @returns Newly created worker.
196
195
  */
197
196
  protected abstract createWorker(): Worker;
198
- /**
199
- * Function that can be hooked up when a worker has been newly created and moved to the pool worker nodes.
200
- * Can be overridden.
201
- *
202
- * @param worker - The newly created worker.
203
- */
204
- protected afterWorkerSetup(worker: Worker): void;
205
197
  /**
206
198
  * Creates a new worker and sets it up completely in the pool worker nodes.
207
199
  *
208
200
  * @returns New, completely set up worker.
209
201
  */
210
202
  protected createAndSetupWorker(): Worker;
211
- private redistributeQueuedTasks;
212
203
  /**
213
204
  * Creates a new dynamic worker and sets it up completely in the pool worker nodes.
214
205
  *
215
206
  * @returns New, completely set up dynamic worker.
216
207
  */
217
208
  protected createAndSetupDynamicWorker(): Worker;
209
+ /**
210
+ * Registers a listener callback on the given worker.
211
+ *
212
+ * @param worker - The worker which should register a listener.
213
+ * @param listener - The message listener callback.
214
+ */
215
+ private registerWorkerMessageListener;
216
+ /**
217
+ * Function that can be hooked up when a worker has been newly created and moved to the pool worker nodes.
218
+ * Can be overridden.
219
+ *
220
+ * @param worker - The newly created worker.
221
+ */
222
+ protected afterWorkerSetup(worker: Worker): void;
223
+ private sendWorkerStartupMessage;
224
+ private redistributeQueuedTasks;
218
225
  /**
219
226
  * This function is the listener registered for each worker message.
220
227
  *
221
228
  * @returns The listener function to execute when a message is received from a worker.
222
229
  */
223
230
  protected workerListener(): (message: MessageValue<Response>) => void;
224
- private handleWorkerReadyMessage;
231
+ private handleWorkerReadyResponse;
225
232
  private handleTaskExecutionResponse;
226
233
  private checkAndEmitEvents;
227
234
  /**
@@ -231,18 +238,24 @@ export declare abstract class AbstractPool<Worker extends IWorker, Data = unknow
231
238
  */
232
239
  private getWorkerInfo;
233
240
  /**
234
- * Pushes the given worker in the pool worker nodes.
241
+ * Adds the given worker in the pool worker nodes.
235
242
  *
236
243
  * @param worker - The worker.
237
244
  * @returns The worker nodes length.
238
245
  */
239
- private pushWorkerNode;
246
+ private addWorkerNode;
240
247
  /**
241
248
  * Removes the given worker from the pool worker nodes.
242
249
  *
243
250
  * @param worker - The worker.
244
251
  */
245
252
  private removeWorkerNode;
253
+ /**
254
+ * Executes the given task on the given worker.
255
+ *
256
+ * @param worker - The worker.
257
+ * @param task - The task to execute.
258
+ */
246
259
  private executeTask;
247
260
  private enqueueTask;
248
261
  private dequeueTask;
@@ -12,9 +12,9 @@ export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IWorke
12
12
  protected readonly pool: IPool<Worker, Data, Response>;
13
13
  protected opts: WorkerChoiceStrategyOptions;
14
14
  /**
15
- * Id of the next worker node.
15
+ * The next worker node key.
16
16
  */
17
- protected nextWorkerNodeId: number;
17
+ protected nextWorkerNodeKey: number;
18
18
  /** @inheritDoc */
19
19
  readonly strategyPolicy: StrategyPolicy;
20
20
  /** @inheritDoc */
@@ -37,6 +37,7 @@ export declare abstract class AbstractWorkerChoiceStrategy<Worker extends IWorke
37
37
  abstract remove(workerNodeKey: number): boolean;
38
38
  /** @inheritDoc */
39
39
  setOptions(opts: WorkerChoiceStrategyOptions): void;
40
+ protected isWorkerNodeReady(workerNodeKey: number): boolean;
40
41
  /**
41
42
  * Gets the worker task runtime.
42
43
  * If the task statistics require the average runtime, the average runtime is returned.
@@ -27,6 +27,7 @@ export declare class FairShareWorkerChoiceStrategy<Worker extends IWorker, Data
27
27
  choose(): number;
28
28
  /** @inheritDoc */
29
29
  remove(workerNodeKey: number): boolean;
30
+ private fairShareNextWorkerNodeKey;
30
31
  /**
31
32
  * Computes the worker node key virtual task end timestamp.
32
33
  *
@@ -22,4 +22,5 @@ export declare class LeastBusyWorkerChoiceStrategy<Worker extends IWorker, Data
22
22
  choose(): number;
23
23
  /** @inheritDoc */
24
24
  remove(): boolean;
25
+ private leastBusyNextWorkerNodeKey;
25
26
  }
@@ -22,4 +22,5 @@ export declare class LeastEluWorkerChoiceStrategy<Worker extends IWorker, Data =
22
22
  choose(): number;
23
23
  /** @inheritDoc */
24
24
  remove(): boolean;
25
+ private leastEluNextWorkerNodeKey;
25
26
  }
@@ -20,4 +20,5 @@ export declare class LeastUsedWorkerChoiceStrategy<Worker extends IWorker, Data
20
20
  choose(): number;
21
21
  /** @inheritDoc */
22
22
  remove(): boolean;
23
+ private leastUsedNextWorkerNodeKey;
23
24
  }
@@ -22,4 +22,5 @@ export declare class RoundRobinWorkerChoiceStrategy<Worker extends IWorker, Data
22
22
  choose(): number;
23
23
  /** @inheritDoc */
24
24
  remove(workerNodeKey: number): boolean;
25
+ private roundRobinNextWorkerNodeKey;
25
26
  }
@@ -139,7 +139,7 @@ export interface TaskStatisticsRequirements {
139
139
  */
140
140
  export interface StrategyPolicy {
141
141
  /**
142
- * Expects direct usage of dynamic worker.
142
+ * Expects direct usage of the newly created dynamic worker.
143
143
  */
144
144
  readonly useDynamicWorker: boolean;
145
145
  }
@@ -33,4 +33,5 @@ export declare class WeightedRoundRobinWorkerChoiceStrategy<Worker extends IWork
33
33
  choose(): number;
34
34
  /** @inheritDoc */
35
35
  remove(workerNodeKey: number): boolean;
36
+ private weightedRoundRobinNextWorkerNodeKey;
36
37
  }
@@ -1 +1 @@
1
- export declare const version = "2.6.14";
1
+ export declare const version = "2.6.15";
@@ -213,7 +213,7 @@ export interface IWorkerNode<Worker extends IWorker, Data = unknown> {
213
213
  */
214
214
  readonly resetUsage: () => void;
215
215
  /**
216
- * Gets task usage statistics.
216
+ * Gets task worker usage statistics.
217
217
  */
218
218
  readonly getTaskWorkerUsage: (name: string) => WorkerUsage | undefined;
219
219
  }
@@ -110,9 +110,9 @@ export interface MessageValue<Data = unknown, ErrorData = unknown> extends Task<
110
110
  */
111
111
  readonly ready?: boolean;
112
112
  /**
113
- * Whether the worker starts or stops its aliveness check.
113
+ * Whether the worker starts or stops its activity check.
114
114
  */
115
- readonly checkAlive?: boolean;
115
+ readonly checkActive?: boolean;
116
116
  }
117
117
  /**
118
118
  * An object holding the execution response promise resolve/reject callbacks.
@@ -125,11 +125,11 @@ export interface PromiseResponseWrapper<Worker extends IWorker, Response = unkno
125
125
  /**
126
126
  * Resolve callback to fulfill the promise.
127
127
  */
128
- readonly resolve: (value: Response) => void;
128
+ readonly resolve: (value: Response | PromiseLike<Response>) => void;
129
129
  /**
130
130
  * Reject callback to reject the promise.
131
131
  */
132
- readonly reject: (reason?: string) => void;
132
+ readonly reject: (reason?: unknown) => void;
133
133
  /**
134
134
  * The worker handling the execution.
135
135
  */
@@ -36,9 +36,9 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
36
36
  */
37
37
  protected statistics: WorkerStatistics;
38
38
  /**
39
- * Handler id of the `aliveInterval` worker alive check.
39
+ * Handler id of the `activeInterval` worker activity check.
40
40
  */
41
- protected aliveInterval?: NodeJS.Timeout;
41
+ protected activeInterval?: NodeJS.Timeout;
42
42
  /**
43
43
  * Constructs a new poolifier worker.
44
44
  *
@@ -99,25 +99,25 @@ export declare abstract class AbstractWorker<MainWorker extends Worker | Message
99
99
  /**
100
100
  * Worker message listener.
101
101
  *
102
- * @param message - Message received.
102
+ * @param message - The received message.
103
103
  */
104
104
  protected messageListener(message: MessageValue<Data, Data>): void;
105
105
  /**
106
- * Notifies the main worker that this worker is ready to process tasks.
106
+ * Sends to the main worker the ready response.
107
107
  */
108
- protected workerReady(): void;
108
+ protected sendReadyResponse(): void;
109
109
  /**
110
- * Starts the worker alive check interval.
110
+ * Starts the worker check active interval.
111
111
  */
112
- private startCheckAlive;
112
+ private startCheckActive;
113
113
  /**
114
- * Stops the worker alive check interval.
114
+ * Stops the worker check active interval.
115
115
  */
116
- private stopCheckAlive;
116
+ private stopCheckActive;
117
117
  /**
118
118
  * Checks if the worker should be terminated, because its living too long.
119
119
  */
120
- private checkAlive;
120
+ private checkActive;
121
121
  /**
122
122
  * Returns the main worker.
123
123
  *
@@ -20,13 +20,13 @@ export type KillBehavior = keyof typeof KillBehaviors;
20
20
  */
21
21
  export interface WorkerOptions {
22
22
  /**
23
- * Maximum waiting time in milliseconds for tasks.
23
+ * Maximum waiting time in milliseconds for tasks on newly created workers.
24
24
  *
25
25
  * After this time, newly created workers will be terminated.
26
- * The last active time of your worker unit will be updated when a task is submitted to a worker or when a worker terminate a task.
26
+ * The last active time of your worker will be updated when it terminates a task.
27
27
  *
28
28
  * - If `killBehavior` is set to `KillBehaviors.HARD` this value represents also the timeout for the tasks that you submit to the pool,
29
- * when this timeout expires your tasks is interrupted and the worker is killed if is not part of the minimum size of the pool.
29
+ * when this timeout expires your tasks is interrupted before completion and removed. The worker is killed if is not part of the minimum size of the pool.
30
30
  * - If `killBehavior` is set to `KillBehaviors.SOFT` your tasks have no timeout and your workers will not be terminated until your task is completed.
31
31
  *
32
32
  * @defaultValue 60000
@@ -40,7 +40,7 @@ export interface WorkerOptions {
40
40
  */
41
41
  async?: boolean;
42
42
  /**
43
- * `killBehavior` dictates if your async unit (worker/process) will be deleted in case that a task is active on it.
43
+ * `killBehavior` dictates if your worker will be deleted in case a task is active on it.
44
44
  *
45
45
  * - SOFT: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still executing or queued, then the worker **won't** be deleted.
46
46
  * - HARD: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still executing or queued, then the worker will be deleted.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poolifier",
3
- "version": "2.6.14",
3
+ "version": "2.6.15",
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",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "volta": {
28
28
  "node": "20.4.0",
29
- "pnpm": "8.6.6"
29
+ "pnpm": "8.6.7"
30
30
  },
31
31
  "repository": {
32
32
  "type": "git",
@@ -81,12 +81,12 @@
81
81
  "@commitlint/cli": "^17.6.6",
82
82
  "@commitlint/config-conventional": "^17.6.6",
83
83
  "@release-it/bumper": "^5.0.0",
84
- "@release-it/keep-a-changelog": "^3.1.0",
84
+ "@release-it/keep-a-changelog": "^4.0.0",
85
85
  "@rollup/plugin-terser": "^0.4.3",
86
86
  "@rollup/plugin-typescript": "^11.1.2",
87
87
  "@types/node": "^20.4.1",
88
- "@typescript-eslint/eslint-plugin": "^5.61.0",
89
- "@typescript-eslint/parser": "^5.61.0",
88
+ "@typescript-eslint/eslint-plugin": "^5.62.0",
89
+ "@typescript-eslint/parser": "^5.62.0",
90
90
  "benny": "^3.7.1",
91
91
  "c8": "^8.0.0",
92
92
  "eslint": "^8.44.0",