poolifier 2.3.5 → 2.3.7
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 +4 -6
- package/lib/index.d.ts +21 -48
- package/lib/index.js +1 -1
- package/package.json +18 -14
package/README.md
CHANGED
|
@@ -3,10 +3,6 @@
|
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
5
|
<h2 align="center">Node Thread Pool and Cluster Pool :arrow_double_up: :on:</h2>
|
|
6
|
-
<h2 align="center">
|
|
7
|
-
<a href="https://ko-fi.com/Q5Q31D6QY">
|
|
8
|
-
<img alt="Ko-fi" src="https://ko-fi.com/img/githubbutton_sm.svg"></a>
|
|
9
|
-
</h2>
|
|
10
6
|
|
|
11
7
|
<p align="center">
|
|
12
8
|
<a href="https://www.npmjs.com/package/poolifier">
|
|
@@ -16,11 +12,13 @@
|
|
|
16
12
|
<a href="https://sonarcloud.io/dashboard?id=pioardi_poolifier">
|
|
17
13
|
<img alt="Quality Gate Status" src="https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=alert_status"></a>
|
|
18
14
|
<a href="https://sonarcloud.io/component_measures/metric/coverage/list?id=pioardi_poolifier">
|
|
19
|
-
<img alt="Code
|
|
15
|
+
<img alt="Code Coverage" src="https://sonarcloud.io/api/project_badges/measure?project=pioardi_poolifier&metric=coverage"></a>
|
|
20
16
|
<a href="https://standardjs.com">
|
|
21
17
|
<img alt="Javascript Standard Style Guide" src="https://img.shields.io/badge/code_style-standard-brightgreen.svg"></a>
|
|
22
18
|
<a href="https://gitter.im/poolifier/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
|
|
23
19
|
<img alt="Gitter chat" src="https://badges.gitter.im/poolifier/community.svg"></a>
|
|
20
|
+
<a href="https://opencollective.com/poolifier">
|
|
21
|
+
<img alt="Open Collective" src="https://opencollective.com/poolifier/tiers/badge.svg"></a>
|
|
24
22
|
<a href="https://badgen.net/badge/Dependabot/enabled/green?icon=dependabot">
|
|
25
23
|
<img alt="Dependabot" src="https://badgen.net/badge/Dependabot/enabled/green?icon=dependabot"></a>
|
|
26
24
|
<a href="http://makeapullrequest.com">
|
|
@@ -99,7 +97,7 @@ You can implement a worker-threads worker in a simple way by extending the class
|
|
|
99
97
|
'use strict'
|
|
100
98
|
const { ThreadWorker } = require('poolifier')
|
|
101
99
|
|
|
102
|
-
function yourFunction
|
|
100
|
+
function yourFunction(data) {
|
|
103
101
|
// this will be executed in the worker thread,
|
|
104
102
|
// the data will be received by using the execute method
|
|
105
103
|
return { ok: 1 }
|
package/lib/index.d.ts
CHANGED
|
@@ -26,40 +26,19 @@ type ExitHandler<Worker> = (this: Worker, code: number) => void;
|
|
|
26
26
|
*/
|
|
27
27
|
interface IPoolWorker {
|
|
28
28
|
/**
|
|
29
|
-
* Register
|
|
29
|
+
* Register an event listener.
|
|
30
30
|
*
|
|
31
|
-
* @param event
|
|
32
|
-
* @param handler The
|
|
31
|
+
* @param event The event.
|
|
32
|
+
* @param handler The event listener.
|
|
33
33
|
*/
|
|
34
|
-
on(event: "message", handler: MessageHandler<this>): void;
|
|
35
|
-
/**
|
|
36
|
-
* Register a listener to the error event.
|
|
37
|
-
*
|
|
38
|
-
* @param event `'error'`.
|
|
39
|
-
* @param handler The error handler.
|
|
40
|
-
*/
|
|
41
|
-
on(event: "error", handler: ErrorHandler<this>): void;
|
|
42
|
-
/**
|
|
43
|
-
* Register a listener to the online event.
|
|
44
|
-
*
|
|
45
|
-
* @param event `'online'`.
|
|
46
|
-
* @param handler The online handler.
|
|
47
|
-
*/
|
|
48
|
-
on(event: "online", handler: OnlineHandler<this>): void;
|
|
49
|
-
/**
|
|
50
|
-
* Register a listener to the exit event.
|
|
51
|
-
*
|
|
52
|
-
* @param event `'exit'`.
|
|
53
|
-
* @param handler The exit handler.
|
|
54
|
-
*/
|
|
55
|
-
on(event: "exit", handler: ExitHandler<this>): void;
|
|
34
|
+
on: ((event: "message", handler: MessageHandler<this>) => void) & ((event: "error", handler: ErrorHandler<this>) => void) & ((event: "online", handler: OnlineHandler<this>) => void) & ((event: "exit", handler: ExitHandler<this>) => void);
|
|
56
35
|
/**
|
|
57
36
|
* Register a listener to the exit event that will only performed once.
|
|
58
37
|
*
|
|
59
38
|
* @param event `'exit'`.
|
|
60
39
|
* @param handler The exit handler.
|
|
61
40
|
*/
|
|
62
|
-
once(event: "exit", handler: ExitHandler<this>)
|
|
41
|
+
once: (event: "exit", handler: ExitHandler<this>) => void;
|
|
63
42
|
}
|
|
64
43
|
/**
|
|
65
44
|
* Enumeration of worker choice strategies.
|
|
@@ -77,9 +56,9 @@ type WorkerChoiceStrategy = keyof typeof WorkerChoiceStrategies;
|
|
|
77
56
|
/**
|
|
78
57
|
* Pool tasks usage statistics requirements.
|
|
79
58
|
*/
|
|
80
|
-
|
|
59
|
+
interface RequiredStatistics {
|
|
81
60
|
runTime: boolean;
|
|
82
|
-
}
|
|
61
|
+
}
|
|
83
62
|
/**
|
|
84
63
|
* Worker choice strategy interface.
|
|
85
64
|
*
|
|
@@ -97,11 +76,11 @@ interface IWorkerChoiceStrategy<Worker extends IPoolWorker> {
|
|
|
97
76
|
/**
|
|
98
77
|
* Resets strategy internals (counters, statistics, etc.).
|
|
99
78
|
*/
|
|
100
|
-
reset()
|
|
79
|
+
reset: () => boolean;
|
|
101
80
|
/**
|
|
102
81
|
* Chooses a worker in the pool.
|
|
103
82
|
*/
|
|
104
|
-
choose()
|
|
83
|
+
choose: () => Worker;
|
|
105
84
|
}
|
|
106
85
|
/**
|
|
107
86
|
* Pool events emitter.
|
|
@@ -160,17 +139,17 @@ interface IPool<Data = unknown, Response = unknown> {
|
|
|
160
139
|
* @param data The input for the specified task. This can only be serializable data.
|
|
161
140
|
* @returns Promise that will be resolved when the task is successfully completed.
|
|
162
141
|
*/
|
|
163
|
-
execute(data: Data)
|
|
142
|
+
execute: (data: Data) => Promise<Response>;
|
|
164
143
|
/**
|
|
165
144
|
* Shutdowns every current worker in this pool.
|
|
166
145
|
*/
|
|
167
|
-
destroy()
|
|
146
|
+
destroy: () => Promise<void>;
|
|
168
147
|
/**
|
|
169
148
|
* Sets the worker choice strategy in this pool.
|
|
170
149
|
*
|
|
171
150
|
* @param workerChoiceStrategy The worker choice strategy.
|
|
172
151
|
*/
|
|
173
|
-
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy)
|
|
152
|
+
setWorkerChoiceStrategy: (workerChoiceStrategy: WorkerChoiceStrategy) => void;
|
|
174
153
|
}
|
|
175
154
|
/**
|
|
176
155
|
* Internal pool types.
|
|
@@ -213,10 +192,6 @@ interface IPoolInternal<Worker extends IPoolWorker, Data = unknown, Response = u
|
|
|
213
192
|
* If it is `'dynamic'`, it provides the `max` property.
|
|
214
193
|
*/
|
|
215
194
|
readonly type: PoolType;
|
|
216
|
-
/**
|
|
217
|
-
* Maximum number of workers that can be created by this pool.
|
|
218
|
-
*/
|
|
219
|
-
readonly max?: number;
|
|
220
195
|
/**
|
|
221
196
|
* Whether the pool is busy or not.
|
|
222
197
|
*
|
|
@@ -236,28 +211,28 @@ interface IPoolInternal<Worker extends IPoolWorker, Data = unknown, Response = u
|
|
|
236
211
|
*
|
|
237
212
|
* @returns A free worker if there is one, otherwise `false`.
|
|
238
213
|
*/
|
|
239
|
-
findFreeWorker()
|
|
214
|
+
findFreeWorker: () => Worker | false;
|
|
240
215
|
/**
|
|
241
216
|
* Gets worker index.
|
|
242
217
|
*
|
|
243
218
|
* @param worker The worker.
|
|
244
219
|
* @returns The worker index.
|
|
245
220
|
*/
|
|
246
|
-
getWorkerIndex(worker: Worker)
|
|
221
|
+
getWorkerIndex: (worker: Worker) => number;
|
|
247
222
|
/**
|
|
248
223
|
* Gets worker running tasks.
|
|
249
224
|
*
|
|
250
225
|
* @param worker The worker.
|
|
251
226
|
* @returns The number of tasks currently running on the worker.
|
|
252
227
|
*/
|
|
253
|
-
getWorkerRunningTasks(worker: Worker)
|
|
228
|
+
getWorkerRunningTasks: (worker: Worker) => number | undefined;
|
|
254
229
|
/**
|
|
255
230
|
* Gets worker average tasks runtime.
|
|
256
231
|
*
|
|
257
232
|
* @param worker The worker.
|
|
258
233
|
* @returns The average tasks runtime on the worker.
|
|
259
234
|
*/
|
|
260
|
-
getWorkerAverageTasksRunTime(worker: Worker)
|
|
235
|
+
getWorkerAverageTasksRunTime: (worker: Worker) => number | undefined;
|
|
261
236
|
}
|
|
262
237
|
/**
|
|
263
238
|
* Enumeration of kill behaviors.
|
|
@@ -371,7 +346,7 @@ interface PromiseWorkerResponseWrapper<Worker extends IPoolWorker, Response = un
|
|
|
371
346
|
*/
|
|
372
347
|
declare class WorkerChoiceStrategyContext<Worker extends IPoolWorker, Data, Response> {
|
|
373
348
|
private readonly pool;
|
|
374
|
-
private createDynamicallyWorkerCallback;
|
|
349
|
+
private readonly createDynamicallyWorkerCallback;
|
|
375
350
|
private workerChoiceStrategy;
|
|
376
351
|
/**
|
|
377
352
|
* Worker choice strategy context constructor.
|
|
@@ -424,8 +399,6 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
424
399
|
readonly workersTasksUsage: Map<Worker, TasksUsage>;
|
|
425
400
|
/** @inheritDoc */
|
|
426
401
|
readonly emitter?: PoolEmitter;
|
|
427
|
-
/** @inheritDoc */
|
|
428
|
-
readonly max?: number;
|
|
429
402
|
/**
|
|
430
403
|
* The promise map.
|
|
431
404
|
*
|
|
@@ -535,7 +508,6 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
535
508
|
* @param listener The message listener callback.
|
|
536
509
|
*/
|
|
537
510
|
protected abstract registerWorkerMessageListener<Message extends Data | Response>(worker: Worker, listener: (message: MessageValue<Message>) => void): void;
|
|
538
|
-
protected internalExecute(worker: Worker, messageId: number): Promise<Response>;
|
|
539
511
|
/**
|
|
540
512
|
* Returns a newly created worker.
|
|
541
513
|
*/
|
|
@@ -560,6 +532,7 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
560
532
|
* @returns The listener function to execute when a message is received from a worker.
|
|
561
533
|
*/
|
|
562
534
|
protected workerListener(): (message: MessageValue<Response>) => void;
|
|
535
|
+
private internalExecute;
|
|
563
536
|
private checkAndEmitBusy;
|
|
564
537
|
/**
|
|
565
538
|
* Increases the number of tasks that the given worker has applied.
|
|
@@ -606,7 +579,7 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
606
579
|
*
|
|
607
580
|
* @param worker The worker.
|
|
608
581
|
*/
|
|
609
|
-
initWorkerTasksUsage
|
|
582
|
+
private initWorkerTasksUsage;
|
|
610
583
|
/**
|
|
611
584
|
* Removes worker tasks usage statistics.
|
|
612
585
|
*
|
|
@@ -691,7 +664,7 @@ declare class FixedClusterPool<Data = unknown, Response = unknown> extends Abstr
|
|
|
691
664
|
* @since 2.0.0
|
|
692
665
|
*/
|
|
693
666
|
declare class DynamicClusterPool<Data = unknown, Response = unknown> extends FixedClusterPool<Data, Response> {
|
|
694
|
-
readonly max: number;
|
|
667
|
+
protected readonly max: number;
|
|
695
668
|
/**
|
|
696
669
|
* Constructs a new poolifier dynamic cluster pool.
|
|
697
670
|
*
|
|
@@ -760,7 +733,7 @@ declare class FixedThreadPool<Data = unknown, Response = unknown> extends Abstra
|
|
|
760
733
|
* @since 0.0.1
|
|
761
734
|
*/
|
|
762
735
|
declare class DynamicThreadPool<Data = unknown, Response = unknown> extends FixedThreadPool<Data, Response> {
|
|
763
|
-
readonly max: number;
|
|
736
|
+
protected readonly max: number;
|
|
764
737
|
/**
|
|
765
738
|
* Constructs a new poolifier dynamic thread pool.
|
|
766
739
|
*
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,r=require("cluster"),t=require("events"),s=require("os"),o=require("worker_threads"),i=require("async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const n=()=>{},a=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class h extends t{}const k=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_RECENTLY_USED:"LESS_RECENTLY_USED",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.requiredStatistics={runTime:!1}}}class u extends c{constructor(){super(...arguments),this.requiredStatistics={runTime:!0},this.workerLastVirtualTaskTimestamp=new Map}reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const t of this.pool.workers){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(Date.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(this.pool.getWorkerAverageTasksRunTime(e)??0)})}}class l extends c{reset(){return!0}choose(){let e,r=1/0;for(const t of this.pool.workers){const s=this.pool.getWorkerRunningTasks(t);if(!1===this.isDynamicPool&&0===s)return t;s<r&&(e=t,r=s)}return e}}class p extends c{constructor(){super(...arguments),this.nextWorkerIndex=0}reset(){return this.nextWorkerIndex=0,!0}choose(){const e=this.pool.workers[this.nextWorkerIndex];return this.nextWorkerIndex=this.nextWorkerIndex===this.pool.workers.length-1?0:this.nextWorkerIndex+1,e}}class g extends c{constructor(e){super(e),this.requiredStatistics={runTime:!0},this.currentWorkerIndex=0,this.workersTaskRunTime=new Map,this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerIndex=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.pool.workers[this.currentWorkerIndex];!0===this.isDynamicPool&&!1===this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerIndex=this.currentWorkerIndex===this.pool.workers.length-1?0:this.currentWorkerIndex+1,this.setWorkerTaskRunTime(this.pool.workers[this.currentWorkerIndex],t,0)),e}initWorkersTaskRunTime(){for(const e of this.pool.workers)this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.pool.getWorkerAverageTasksRunTime(e)}computeWorkerWeight(){let e=0;for(const r of s.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/s.cpus().length)}}class T{static getWorkerChoiceStrategy(e,r=k.ROUND_ROBIN){switch(r){case k.ROUND_ROBIN:return new p(e);case k.LESS_RECENTLY_USED:return new l(e);case k.FAIR_SHARE:return new u(e);case k.WEIGHTED_ROUND_ROBIN:return new g(e);default:throw new Error(`Worker choice strategy '${r}' not found`)}}}class W extends c{constructor(e,r,t=k.ROUND_ROBIN){super(e),this.createDynamicallyWorkerCallback=r,this.workerChoiceStrategy=T.getWorkerChoiceStrategy(this.pool,t),this.requiredStatistics=this.workerChoiceStrategy.requiredStatistics}reset(){return this.workerChoiceStrategy.reset()}choose(){const e=this.pool.findFreeWorker();return e||(!0===this.pool.busy?this.workerChoiceStrategy.choose():this.createDynamicallyWorkerCallback())}}class m{constructor(e,r,t=k.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=r,this.setWorkerChoiceStrategy(t)}getPoolWorkerChoiceStrategy(r=k.ROUND_ROBIN){return this.pool.type===e.DYNAMIC?new W(this.pool,this.createDynamicallyWorkerCallback,r):T.getWorkerChoiceStrategy(this.pool,r)}getWorkerChoiceStrategy(){return this.workerChoiceStrategy}setWorkerChoiceStrategy(e){this.workerChoiceStrategy?.reset(),this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}}class d{constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,this.workers=[],this.workersTasksUsage=new Map,this.promiseMap=new Map,this.nextMessageId=0,!1===this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new h),this.workerChoiceStrategyContext=new m(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=a.HARD,(r.kill===t||0===this.getWorkerRunningTasks(e))&&this.destroyWorker(e)})),e}),this.opts.workerChoiceStrategy)}checkFilePath(e){if(!e)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!1===Number.isSafeInteger(r))throw new Error("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new Error("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??k.ROUND_ROBIN,this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseMap.size}getWorkerIndex(e){return this.workers.indexOf(e)}getWorkerRunningTasks(e){return this.workersTasksUsage.get(e)?.running}getWorkerAverageTasksRunTime(e){return this.workersTasksUsage.get(e)?.avgRunTime}setWorkerChoiceStrategy(e){this.opts.workerChoiceStrategy=e;for(const e of this.workers)this.resetWorkerTasksUsage(e);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeWorker()}findFreeWorker(){for(const e of this.workers)if(0===this.getWorkerRunningTasks(e))return e;return!1}execute(e){const r=this.chooseWorker(),t=this.internalExecute(r,this.nextMessageId);return this.checkAndEmitBusy(),this.sendToWorker(r,{data:e??{},id:this.nextMessageId}),++this.nextMessageId,t}async destroy(){await Promise.all(this.workers.map((e=>this.destroyWorker(e))))}setupHook(){}beforePromiseWorkerResponseHook(e){this.increaseWorkerRunningTasks(e)}afterPromiseWorkerResponseHook(e,r){this.decreaseWorkerRunningTasks(r.worker),this.stepWorkerRunTasks(r.worker,1),this.updateWorkerTasksRunTime(r.worker,e.taskRunTime)}removeWorker(e){this.workers.splice(this.getWorkerIndex(e),1),this.removeWorkerTasksUsage(e)}chooseWorker(){return this.workerChoiceStrategyContext.execute()}internalExecute(e,r){return this.beforePromiseWorkerResponseHook(e),new Promise(((t,s)=>{this.promiseMap.set(r,{resolve:t,reject:s,worker:e})}))}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??n),e.on("error",this.opts.errorHandler??n),e.on("online",this.opts.onlineHandler??n),e.on("exit",this.opts.exitHandler??n),e.once("exit",(()=>this.removeWorker(e))),this.workers.push(e),this.initWorkerTasksUsage(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(void 0!==e.id){const r=this.promiseMap.get(e.id);void 0!==r&&(e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseWorkerResponseHook(e,r),this.promiseMap.delete(e.id))}}}checkAndEmitBusy(){!0===this.opts.enableEvents&&!0===this.busy&&this.emitter?.emit("busy")}increaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,1)}decreaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,-1)}stepWorkerRunningTasks(e,r){if(!0===this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.running=t.running+r,this.workersTasksUsage.set(e,t)}}stepWorkerRunTasks(e,r){if(!0===this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.run=t.run+r,this.workersTasksUsage.set(e,t)}}updateWorkerTasksRunTime(e,r){if(!0===this.workerChoiceStrategyContext.getWorkerChoiceStrategy().requiredStatistics.runTime&&!0===this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.runTime+=r??0,0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workersTasksUsage.set(e,t)}}checkWorkerTasksUsage(e){const r=this.workersTasksUsage.has(e);if(!1===r)throw new Error("Worker could not be found in workers tasks usage map");return r}initWorkerTasksUsage(e){this.workersTasksUsage.set(e,{run:0,running:0,runTime:0,avgRunTime:0})}removeWorkerTasksUsage(e){this.workersTasksUsage.delete(e)}resetWorkerTasksUsage(e){this.removeWorkerTasksUsage(e),this.initWorkerTasksUsage(e)}}class w extends d{constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}class y extends d{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}const R=a.SOFT;class f extends i.AsyncResource{constructor(e,r,t,s,o={killBehavior:R,maxInactiveTime:6e4}){super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.lastTaskTimestamp=Date.now(),!1===r&&(this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??6e4)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){void 0!==e.data&&void 0!==e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):void 0!==e.parent?this.mainWorker=e.parent:void 0!==e.kill&&(this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??6e4,this.opts.async=!!e.async}checkFunctionInput(e){if(!e)throw new Error("fn parameter is mandatory")}getMainWorker(){if(!this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){Date.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??6e4)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),o=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,taskRunTime:o})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{this.lastTaskTimestamp=Date.now()}}runAsync(e,r){const t=Date.now();e(r.data).then((e=>{const s=Date.now()-t;return this.sendToMainWorker({data:e,id:r.id,taskRunTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{this.lastTaskTimestamp=Date.now()})).catch(n)}}exports.ClusterWorker=class extends f{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends w{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends y{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=w,exports.FixedThreadPool=y,exports.KillBehaviors=a,exports.ThreadWorker=class extends f{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=k;
|
|
1
|
+
"use strict";var e,r=require("cluster"),t=require("events"),s=require("os"),o=require("worker_threads"),i=require("async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const n=()=>{},a={},h=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class k extends t{}const c=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_RECENTLY_USED:"LESS_RECENTLY_USED",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class u{constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.requiredStatistics={runTime:!1}}}class l extends u{constructor(){super(...arguments),this.requiredStatistics={runTime:!0},this.workerLastVirtualTaskTimestamp=new Map}reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const t of this.pool.workers){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(Date.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(this.pool.getWorkerAverageTasksRunTime(e)??0)})}}class p extends u{reset(){return!0}choose(){let e,r=1/0;for(const t of this.pool.workers){const s=this.pool.getWorkerRunningTasks(t);if(!this.isDynamicPool&&0===s)return t;s<r&&(e=t,r=s)}return e}}class g extends u{constructor(){super(...arguments),this.nextWorkerIndex=0}reset(){return this.nextWorkerIndex=0,!0}choose(){const e=this.pool.workers[this.nextWorkerIndex];return this.nextWorkerIndex=this.nextWorkerIndex===this.pool.workers.length-1?0:this.nextWorkerIndex+1,e}}class T extends u{constructor(e){super(e),this.requiredStatistics={runTime:!0},this.currentWorkerIndex=0,this.workersTaskRunTime=new Map,this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerIndex=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.pool.workers[this.currentWorkerIndex];this.isDynamicPool&&!this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerIndex=this.currentWorkerIndex===this.pool.workers.length-1?0:this.currentWorkerIndex+1,this.setWorkerTaskRunTime(this.pool.workers[this.currentWorkerIndex],t,0)),e}initWorkersTaskRunTime(){for(const e of this.pool.workers)this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.pool.getWorkerAverageTasksRunTime(e)}computeWorkerWeight(){let e=0;for(const r of s.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/s.cpus().length)}}function m(e,r=c.ROUND_ROBIN){switch(r){case c.ROUND_ROBIN:return new g(e);case c.LESS_RECENTLY_USED:return new p(e);case c.FAIR_SHARE:return new l(e);case c.WEIGHTED_ROUND_ROBIN:return new T(e);default:throw new Error(`Worker choice strategy '${r}' not found`)}}class W extends u{constructor(e,r,t=c.ROUND_ROBIN){super(e),this.createDynamicallyWorkerCallback=r,this.workerChoiceStrategy=m(this.pool,t),this.requiredStatistics=this.workerChoiceStrategy.requiredStatistics}reset(){return this.workerChoiceStrategy.reset()}choose(){const e=this.pool.findFreeWorker();return!1!==e?e:this.pool.busy?this.workerChoiceStrategy.choose():this.createDynamicallyWorkerCallback()}}class d{constructor(e,r,t=c.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=r,this.setWorkerChoiceStrategy(t)}getPoolWorkerChoiceStrategy(r=c.ROUND_ROBIN){return this.pool.type===e.DYNAMIC?new W(this.pool,this.createDynamicallyWorkerCallback,r):m(this.pool,r)}getWorkerChoiceStrategy(){return this.workerChoiceStrategy}setWorkerChoiceStrategy(e){this.workerChoiceStrategy?.reset(),this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}}class w{constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,this.workers=[],this.workersTasksUsage=new Map,this.promiseMap=new Map,this.nextMessageId=0,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new k),this.workerChoiceStrategyContext=new d(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||0===this.getWorkerRunningTasks(e))&&this.destroyWorker(e)})),e}),this.opts.workerChoiceStrategy)}checkFilePath(e){if(null==e||0===e.length)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!Number.isSafeInteger(r))throw new TypeError("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??c.ROUND_ROBIN,this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseMap.size}getWorkerIndex(e){return this.workers.indexOf(e)}getWorkerRunningTasks(e){return this.workersTasksUsage.get(e)?.running}getWorkerAverageTasksRunTime(e){return this.workersTasksUsage.get(e)?.avgRunTime}setWorkerChoiceStrategy(e){this.opts.workerChoiceStrategy=e;for(const e of this.workers)this.resetWorkerTasksUsage(e);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeWorker()}findFreeWorker(){for(const e of this.workers)if(0===this.getWorkerRunningTasks(e))return e;return!1}async execute(e){const r=this.chooseWorker(),t=this.internalExecute(r,this.nextMessageId);return this.checkAndEmitBusy(),this.sendToWorker(r,{data:e??a,id:this.nextMessageId}),++this.nextMessageId,t}async destroy(){await Promise.all(this.workers.map((e=>this.destroyWorker(e))))}setupHook(){}beforePromiseWorkerResponseHook(e){this.increaseWorkerRunningTasks(e)}afterPromiseWorkerResponseHook(e,r){this.decreaseWorkerRunningTasks(r.worker),this.stepWorkerRunTasks(r.worker,1),this.updateWorkerTasksRunTime(r.worker,e.taskRunTime)}removeWorker(e){this.workers.splice(this.getWorkerIndex(e),1),this.removeWorkerTasksUsage(e)}chooseWorker(){return this.workerChoiceStrategyContext.execute()}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??n),e.on("error",this.opts.errorHandler??n),e.on("online",this.opts.onlineHandler??n),e.on("exit",this.opts.exitHandler??n),e.once("exit",(()=>this.removeWorker(e))),this.workers.push(e),this.initWorkerTasksUsage(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(void 0!==e.id){const r=this.promiseMap.get(e.id);void 0!==r&&(null!=e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseWorkerResponseHook(e,r),this.promiseMap.delete(e.id))}}}async internalExecute(e,r){return this.beforePromiseWorkerResponseHook(e),await new Promise(((t,s)=>{this.promiseMap.set(r,{resolve:t,reject:s,worker:e})}))}checkAndEmitBusy(){!0===this.opts.enableEvents&&this.busy&&this.emitter?.emit("busy")}increaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,1)}decreaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,-1)}stepWorkerRunningTasks(e,r){if(this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.running=t.running+r,this.workersTasksUsage.set(e,t)}}stepWorkerRunTasks(e,r){if(this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.run=t.run+r,this.workersTasksUsage.set(e,t)}}updateWorkerTasksRunTime(e,r){if(this.workerChoiceStrategyContext.getWorkerChoiceStrategy().requiredStatistics.runTime&&this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.runTime+=r??0,0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workersTasksUsage.set(e,t)}}checkWorkerTasksUsage(e){const r=this.workersTasksUsage.has(e);if(!r)throw new Error("Worker could not be found in workers tasks usage map");return r}initWorkerTasksUsage(e){this.workersTasksUsage.set(e,{run:0,running:0,runTime:0,avgRunTime:0})}removeWorkerTasksUsage(e){this.workersTasksUsage.delete(e)}resetWorkerTasksUsage(e){this.removeWorkerTasksUsage(e),this.initWorkerTasksUsage(e)}}class y extends w{constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}class R extends w{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}const f=h.SOFT;class x extends i.AsyncResource{constructor(e,r,t,s,o={killBehavior:f,maxInactiveTime:6e4}){super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.lastTaskTimestamp=Date.now(),r||(this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??6e4)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){void 0!==e.data&&void 0!==e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):void 0!==e.parent?this.mainWorker=e.parent:void 0!==e.kill&&(null!=this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??f,this.opts.maxInactiveTime=e.maxInactiveTime??6e4,this.opts.async=e.async??!1}checkFunctionInput(e){if(null==e)throw new Error("fn parameter is mandatory");if("function"!=typeof e)throw new TypeError("fn parameter is not a function")}getMainWorker(){if(null==this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){Date.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??6e4)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),o=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,taskRunTime:o})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{this.lastTaskTimestamp=Date.now()}}runAsync(e,r){const t=Date.now();e(r.data).then((e=>{const s=Date.now()-t;return this.sendToMainWorker({data:e,id:r.id,taskRunTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{this.lastTaskTimestamp=Date.now()})).catch(n)}}exports.ClusterWorker=class extends x{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends y{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends R{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=y,exports.FixedThreadPool=R,exports.KillBehaviors=h,exports.ThreadWorker=class extends x{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=c;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poolifier",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.7",
|
|
4
4
|
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
"benchmark": "npm run build && node -r source-map-support/register benchmarks/internal/bench.js",
|
|
12
12
|
"benchmark:debug": "npm run build && node -r source-map-support/register --inspect benchmarks/internal/bench.js",
|
|
13
13
|
"benchmark:prod": "npm run build:prod && node -r source-map-support/register benchmarks/internal/bench.js",
|
|
14
|
-
"test": "npm run build &&
|
|
14
|
+
"test": "npm run build && c8 mocha 'tests/**/*.test.js'",
|
|
15
15
|
"test:debug": "npm run build && mocha --no-parallel --inspect 'tests/**/*.test.js'",
|
|
16
|
-
"coverage": "
|
|
17
|
-
"coverage:html": "
|
|
18
|
-
"format": "prettier --loglevel silent --write .; prettierx --write .",
|
|
16
|
+
"coverage": "c8 report --reporter=lcov",
|
|
17
|
+
"coverage:html": "c8 report --reporter=html",
|
|
19
18
|
"lint": "eslint . --cache",
|
|
20
19
|
"lint:fix": "eslint . --cache --fix",
|
|
21
20
|
"lint:report": "eslint . --cache --format json --output-file reports/eslint.json",
|
|
@@ -24,6 +23,15 @@
|
|
|
24
23
|
"sonar:properties": "./updateSonarProps.sh",
|
|
25
24
|
"prepublishOnly": "npm run build:prod"
|
|
26
25
|
},
|
|
26
|
+
"ts-standard": {
|
|
27
|
+
"ignore": [
|
|
28
|
+
"tests/**/*.js"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=16.0.0",
|
|
33
|
+
"npm": ">=8.0.0"
|
|
34
|
+
},
|
|
27
35
|
"repository": {
|
|
28
36
|
"type": "git",
|
|
29
37
|
"url": "git+https://github.com/poolifier/poolifier.git"
|
|
@@ -70,14 +78,15 @@
|
|
|
70
78
|
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
|
71
79
|
"@typescript-eslint/parser": "^5.40.1",
|
|
72
80
|
"benchmark": "^2.1.4",
|
|
73
|
-
"
|
|
81
|
+
"c8": "^7.12.0",
|
|
82
|
+
"eslint": "^8.26.0",
|
|
74
83
|
"eslint-config-standard": "^17.0.0",
|
|
84
|
+
"eslint-config-standard-with-typescript": "^23.0.0",
|
|
75
85
|
"eslint-define-config": "^1.7.0",
|
|
76
86
|
"eslint-import-resolver-typescript": "^3.5.2",
|
|
77
87
|
"eslint-plugin-import": "^2.26.0",
|
|
78
|
-
"eslint-plugin-jsdoc": "^39.3.
|
|
88
|
+
"eslint-plugin-jsdoc": "^39.3.19",
|
|
79
89
|
"eslint-plugin-n": "^15.3.0",
|
|
80
|
-
"eslint-plugin-prettierx": "^0.18.0",
|
|
81
90
|
"eslint-plugin-promise": "^6.1.1",
|
|
82
91
|
"eslint-plugin-spellcheck": "^0.0.19",
|
|
83
92
|
"expect": "^29.2.1",
|
|
@@ -86,10 +95,8 @@
|
|
|
86
95
|
"microtime": "^3.1.1",
|
|
87
96
|
"mocha": "^10.1.0",
|
|
88
97
|
"mochawesome": "^7.1.3",
|
|
89
|
-
"nyc": "^15.1.0",
|
|
90
98
|
"prettier": "^2.7.1",
|
|
91
99
|
"prettier-plugin-organize-imports": "^3.1.1",
|
|
92
|
-
"prettierx": "^0.18.3",
|
|
93
100
|
"release-it": "^15.5.0",
|
|
94
101
|
"rollup": "^3.2.3",
|
|
95
102
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
@@ -100,11 +107,8 @@
|
|
|
100
107
|
"rollup-plugin-ts": "^3.0.2",
|
|
101
108
|
"sinon": "^14.0.1",
|
|
102
109
|
"source-map-support": "^0.5.21",
|
|
110
|
+
"ts-standard": "^12.0.1",
|
|
103
111
|
"typedoc": "^0.23.17",
|
|
104
112
|
"typescript": "^4.8.4"
|
|
105
|
-
},
|
|
106
|
-
"engines": {
|
|
107
|
-
"node": ">=16.0.0",
|
|
108
|
-
"npm": ">=8.0.0"
|
|
109
113
|
}
|
|
110
114
|
}
|