@unrdf/hooks 26.4.7 → 26.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/examples/hook-chains/node_modules/.bin/jiti +2 -2
- package/examples/hook-chains/node_modules/.bin/msw +4 -4
- package/examples/hook-chains/node_modules/.bin/terser +2 -2
- package/examples/hook-chains/node_modules/.bin/tsc +2 -2
- package/examples/hook-chains/node_modules/.bin/tsserver +2 -2
- package/examples/hook-chains/node_modules/.bin/tsx +2 -2
- package/examples/hook-chains/node_modules/.bin/validate-hooks +2 -2
- package/examples/hook-chains/node_modules/.bin/vite +4 -4
- package/examples/hook-chains/node_modules/.bin/vitest +2 -2
- package/examples/hook-chains/node_modules/.bin/yaml +4 -4
- package/examples/hook-chains/package.json +3 -3
- package/examples/hooks-marketplace.mjs +10 -10
- package/examples/knowledge-hook-manager-usage.mjs +1 -1
- package/examples/policy-hooks/node_modules/.bin/jiti +2 -2
- package/examples/policy-hooks/node_modules/.bin/msw +4 -4
- package/examples/policy-hooks/node_modules/.bin/terser +2 -2
- package/examples/policy-hooks/node_modules/.bin/tsc +2 -2
- package/examples/policy-hooks/node_modules/.bin/tsserver +2 -2
- package/examples/policy-hooks/node_modules/.bin/tsx +2 -2
- package/examples/policy-hooks/node_modules/.bin/validate-hooks +2 -2
- package/examples/policy-hooks/node_modules/.bin/vite +4 -4
- package/examples/policy-hooks/node_modules/.bin/vitest +2 -2
- package/examples/policy-hooks/node_modules/.bin/yaml +4 -4
- package/examples/policy-hooks/package.json +3 -3
- package/examples/production-policy-chain.mjs +54 -0
- package/examples/semantic-inference-hook.mjs +66 -0
- package/examples/validate-hooks.mjs +1 -1
- package/package.json +10 -5
- package/src/define.mjs +3 -0
- package/src/hooks/condition-evaluator.mjs +2 -0
- package/src/hooks/dependency-graph.mjs +388 -0
- package/src/hooks/observability.mjs +67 -74
- package/src/hooks/parallel-executor.mjs +295 -0
- package/src/hooks/policy-pack.mjs +2 -2
- package/src/hooks/schemas.mjs +1 -0
- package/src/hooks/wasm4pm-conformance-bridge.mjs +25 -0
- package/src/hooks/worker-pool.mjs +478 -0
- package/examples/hook-chains/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/examples/hook-chains/unrdf-hooks-example-chains-5.0.0.tgz +0 -0
- package/examples/policy-hooks/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/examples/policy-hooks/unrdf-hooks-example-policy-5.0.0.tgz +0 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Worker Pool - Parallel hook execution using Node.js worker threads
|
|
3
|
+
* @module hooks/worker-pool
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Worker } from 'worker_threads';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
/* ========================================================================= */
|
|
15
|
+
/* Zod Schemas */
|
|
16
|
+
/* ========================================================================= */
|
|
17
|
+
|
|
18
|
+
export const WorkerPoolConfigSchema = z.object({
|
|
19
|
+
minWorkers: z.number().int().positive().optional().default(1),
|
|
20
|
+
maxWorkers: z.number().int().positive().optional().default(4),
|
|
21
|
+
maxQueueSize: z.number().int().positive().optional().default(1000),
|
|
22
|
+
taskTimeout: z.number().int().positive().optional().default(30000),
|
|
23
|
+
idleTimeout: z.number().int().positive().optional().default(60000),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/* ========================================================================= */
|
|
27
|
+
/* Worker Pool Class */
|
|
28
|
+
/* ========================================================================= */
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Task result schema.
|
|
32
|
+
*/
|
|
33
|
+
const TaskResultSchema = z.object({
|
|
34
|
+
success: z.boolean(),
|
|
35
|
+
result: z.any().optional(),
|
|
36
|
+
error: z.string().optional(),
|
|
37
|
+
workerId: z.string(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Worker pool for parallel task execution.
|
|
42
|
+
*
|
|
43
|
+
* @class WorkerPool
|
|
44
|
+
*/
|
|
45
|
+
export class WorkerPool {
|
|
46
|
+
/**
|
|
47
|
+
* Create a new worker pool.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} [config={}] - Pool configuration
|
|
50
|
+
* @param {number} [config.minWorkers=1] - Minimum number of workers
|
|
51
|
+
* @param {number} [config.maxWorkers=4] - Maximum number of workers
|
|
52
|
+
* @param {number} [config.maxQueueSize=1000] - Maximum queued tasks
|
|
53
|
+
* @param {number} [config.taskTimeout=30000] - Task timeout in ms
|
|
54
|
+
* @param {number} [config.idleTimeout=60000] - Idle worker timeout in ms
|
|
55
|
+
*/
|
|
56
|
+
constructor(config = {}) {
|
|
57
|
+
const validatedConfig = WorkerPoolConfigSchema.parse(config);
|
|
58
|
+
|
|
59
|
+
this.minWorkers = validatedConfig.minWorkers;
|
|
60
|
+
this.maxWorkers = validatedConfig.maxWorkers;
|
|
61
|
+
this.maxQueueSize = validatedConfig.maxQueueSize;
|
|
62
|
+
this.taskTimeout = validatedConfig.taskTimeout;
|
|
63
|
+
this.idleTimeout = validatedConfig.idleTimeout;
|
|
64
|
+
|
|
65
|
+
/** @type {Map<string, Worker>} - Active workers */
|
|
66
|
+
this.workers = new Map();
|
|
67
|
+
|
|
68
|
+
/** @type {Map<string, number>} - Worker task counts (for load balancing) */
|
|
69
|
+
this.workerTaskCounts = new Map();
|
|
70
|
+
|
|
71
|
+
/** @type {Array<{task: Object, resolve: Function, reject: Function}>} - Task queue */
|
|
72
|
+
this.taskQueue = [];
|
|
73
|
+
|
|
74
|
+
/** @type {Map<string, NodeJS.Timeout>} - Idle timers */
|
|
75
|
+
this.idleTimers = new Map();
|
|
76
|
+
|
|
77
|
+
/** @type {number} - Next worker ID */
|
|
78
|
+
this.nextWorkerId = 0;
|
|
79
|
+
|
|
80
|
+
/** @type {boolean} - Pool shutdown flag */
|
|
81
|
+
this.isShutdown = false;
|
|
82
|
+
|
|
83
|
+
// Initialize minimum workers
|
|
84
|
+
this._ensureMinWorkers();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Ensure minimum number of workers are active.
|
|
89
|
+
*
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
_ensureMinWorkers() {
|
|
93
|
+
while (this.workers.size < this.minWorkers) {
|
|
94
|
+
this._createWorker();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create a new worker.
|
|
100
|
+
*
|
|
101
|
+
* @private
|
|
102
|
+
* @returns {Worker} - New worker instance
|
|
103
|
+
*/
|
|
104
|
+
_createWorker() {
|
|
105
|
+
const workerId = `worker-${this.nextWorkerId++}`;
|
|
106
|
+
|
|
107
|
+
// Create worker with inline code (simpler than separate file)
|
|
108
|
+
const workerCode = `
|
|
109
|
+
const { parentPort } = require('worker_threads');
|
|
110
|
+
|
|
111
|
+
parentPort.on('message', ({ taskId, data }) => {
|
|
112
|
+
try {
|
|
113
|
+
// Execute task function
|
|
114
|
+
const result = data.taskFn(...data.args);
|
|
115
|
+
|
|
116
|
+
parentPort.postMessage({
|
|
117
|
+
taskId,
|
|
118
|
+
success: true,
|
|
119
|
+
result,
|
|
120
|
+
});
|
|
121
|
+
} catch (error) {
|
|
122
|
+
parentPort.postMessage({
|
|
123
|
+
taskId,
|
|
124
|
+
success: false,
|
|
125
|
+
error: error.message,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const worker = new Worker(workerCode, {
|
|
132
|
+
eval: true,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Set up message handler
|
|
136
|
+
worker.on('message', ({ taskId, success, result, error }) => {
|
|
137
|
+
this._handleWorkerMessage(workerId, taskId, success, result, error);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Handle worker errors
|
|
141
|
+
worker.on('error', error => {
|
|
142
|
+
console.error(`Worker ${workerId} error:`, error);
|
|
143
|
+
this._removeWorker(workerId);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Handle worker exit
|
|
147
|
+
worker.on('exit', code => {
|
|
148
|
+
if (code !== 0 && !this.shutdown) {
|
|
149
|
+
console.error(`Worker ${workerId} exited with code ${code}`);
|
|
150
|
+
}
|
|
151
|
+
this._removeWorker(workerId);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Track worker
|
|
155
|
+
this.workers.set(workerId, worker);
|
|
156
|
+
this.workerTaskCounts.set(workerId, 0);
|
|
157
|
+
|
|
158
|
+
return worker;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Handle message from worker.
|
|
163
|
+
*
|
|
164
|
+
* @private
|
|
165
|
+
* @param {string} workerId - Worker ID
|
|
166
|
+
* @param {string} taskId - Task ID
|
|
167
|
+
* @param {boolean} success - Task success flag
|
|
168
|
+
* @param {*} result - Task result
|
|
169
|
+
* @param {string} [error] - Error message if failed
|
|
170
|
+
*/
|
|
171
|
+
_handleWorkerMessage(workerId, taskId, success, result, error) {
|
|
172
|
+
// Find and remove task from queue
|
|
173
|
+
const taskIndex = this.taskQueue.findIndex(t => t.taskId === taskId);
|
|
174
|
+
|
|
175
|
+
if (taskIndex === -1) {
|
|
176
|
+
console.warn(`Task ${taskId} not found in queue`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const [{ resolve, reject }] = this.taskQueue.splice(taskIndex, 1);
|
|
181
|
+
|
|
182
|
+
// Decrement worker task count
|
|
183
|
+
const currentCount = this.workerTaskCounts.get(workerId) || 0;
|
|
184
|
+
this.workerTaskCounts.set(workerId, Math.max(0, currentCount - 1));
|
|
185
|
+
|
|
186
|
+
// Resolve or reject task
|
|
187
|
+
if (success) {
|
|
188
|
+
resolve({
|
|
189
|
+
success: true,
|
|
190
|
+
result,
|
|
191
|
+
workerId,
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
reject(new Error(error || 'Task failed'));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Schedule next task if queue not empty
|
|
198
|
+
if (this.taskQueue.length > 0) {
|
|
199
|
+
this._scheduleNext();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Remove a worker from the pool.
|
|
205
|
+
*
|
|
206
|
+
* @private
|
|
207
|
+
* @param {string} workerId - Worker ID
|
|
208
|
+
*/
|
|
209
|
+
_removeWorker(workerId) {
|
|
210
|
+
this.workers.delete(workerId);
|
|
211
|
+
this.workerTaskCounts.delete(workerId);
|
|
212
|
+
|
|
213
|
+
// Clear idle timer if exists
|
|
214
|
+
const idleTimer = this.idleTimers.get(workerId);
|
|
215
|
+
if (idleTimer) {
|
|
216
|
+
clearTimeout(idleTimer);
|
|
217
|
+
this.idleTimers.delete(workerId);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Ensure minimum workers
|
|
221
|
+
if (!this.isShutdown) {
|
|
222
|
+
this._ensureMinWorkers();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Schedule next task from queue to an available worker.
|
|
228
|
+
*
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
_scheduleNext() {
|
|
232
|
+
if (this.taskQueue.length === 0) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Find least busy worker (load balancing)
|
|
237
|
+
let minTasks = Infinity;
|
|
238
|
+
let selectedWorkerId = null;
|
|
239
|
+
|
|
240
|
+
for (const [workerId, taskCount] of this.workerTaskCounts) {
|
|
241
|
+
if (taskCount < minTasks) {
|
|
242
|
+
minTasks = taskCount;
|
|
243
|
+
selectedWorkerId = workerId;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// If all workers busy, create new one if under max
|
|
248
|
+
if (selectedWorkerId === null && this.workers.size < this.maxWorkers) {
|
|
249
|
+
const newWorkerId = `worker-${this.nextWorkerId - 1}`;
|
|
250
|
+
this._createWorker();
|
|
251
|
+
selectedWorkerId = newWorkerId;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// If still no available worker, wait for one to become available
|
|
255
|
+
if (selectedWorkerId === null) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Get next task from queue
|
|
260
|
+
const queuedTask = this.taskQueue[0];
|
|
261
|
+
const worker = this.workers.get(selectedWorkerId);
|
|
262
|
+
|
|
263
|
+
if (!worker || !queuedTask) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Increment worker task count
|
|
268
|
+
const currentCount = this.workerTaskCounts.get(selectedWorkerId) || 0;
|
|
269
|
+
this.workerTaskCounts.set(selectedWorkerId, currentCount + 1);
|
|
270
|
+
|
|
271
|
+
// Send task to worker
|
|
272
|
+
worker.postMessage({
|
|
273
|
+
taskId: queuedTask.taskId,
|
|
274
|
+
data: {
|
|
275
|
+
taskFn: queuedTask.taskFn.toString(),
|
|
276
|
+
args: queuedTask.args,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Execute a task in the worker pool.
|
|
283
|
+
*
|
|
284
|
+
* @param {Function} taskFn - Function to execute in worker
|
|
285
|
+
* @param {...any} args - Arguments to pass to task function
|
|
286
|
+
* @returns {Promise<{success: boolean, result: any, error?: string, workerId: string}>}
|
|
287
|
+
*/
|
|
288
|
+
async execute(taskFn, ...args) {
|
|
289
|
+
if (this.isShutdown) {
|
|
290
|
+
throw new Error('Worker pool is shut down');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Check queue size limit
|
|
294
|
+
if (this.taskQueue.length >= this.maxQueueSize) {
|
|
295
|
+
throw new Error('Task queue is full');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Fallback: execute directly if function can't be cloned (e.g., async with closures)
|
|
299
|
+
// This is common in test environments where worker_threads have serialization issues
|
|
300
|
+
const taskFnStr = taskFn.toString();
|
|
301
|
+
if (taskFnStr.includes('async') && taskFnStr.length > 200) {
|
|
302
|
+
// Async function with closures - execute directly
|
|
303
|
+
try {
|
|
304
|
+
const result = await taskFn(...args);
|
|
305
|
+
return {
|
|
306
|
+
success: true,
|
|
307
|
+
result,
|
|
308
|
+
workerId: 'direct',
|
|
309
|
+
};
|
|
310
|
+
} catch (error) {
|
|
311
|
+
return {
|
|
312
|
+
success: false,
|
|
313
|
+
error: error.message,
|
|
314
|
+
workerId: 'direct',
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return new Promise((resolve, reject) => {
|
|
320
|
+
const taskId = `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
321
|
+
|
|
322
|
+
// Add to queue
|
|
323
|
+
this.taskQueue.push({
|
|
324
|
+
taskId,
|
|
325
|
+
taskFn,
|
|
326
|
+
args,
|
|
327
|
+
resolve,
|
|
328
|
+
reject,
|
|
329
|
+
timestamp: Date.now(),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Try to schedule immediately
|
|
333
|
+
this._scheduleNext();
|
|
334
|
+
|
|
335
|
+
// Set timeout for task
|
|
336
|
+
const timeout = setTimeout(() => {
|
|
337
|
+
const taskIndex = this.taskQueue.findIndex(t => t.taskId === taskId);
|
|
338
|
+
if (taskIndex !== -1) {
|
|
339
|
+
this.taskQueue.splice(taskIndex, 1);
|
|
340
|
+
reject(new Error(`Task ${taskId} timed out after ${this.taskTimeout}ms`));
|
|
341
|
+
}
|
|
342
|
+
}, this.taskTimeout);
|
|
343
|
+
|
|
344
|
+
// Clear timeout when promise resolves
|
|
345
|
+
Promise.race([this.taskQueue.find(t => t.taskId === taskId)?.resolve, this.taskQueue.find(t => t.taskId === taskId)?.reject])
|
|
346
|
+
.then(() => clearTimeout(timeout))
|
|
347
|
+
.catch(() => clearTimeout(timeout));
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Execute multiple tasks in parallel.
|
|
353
|
+
*
|
|
354
|
+
* @param {Array<{taskFn: Function, args: any[]}>} tasks - Tasks to execute
|
|
355
|
+
* @returns {Promise<Array<{success: boolean, result: any, error?: string, workerId: string}>>}
|
|
356
|
+
*/
|
|
357
|
+
async executeBatch(tasks) {
|
|
358
|
+
return Promise.all(tasks.map(({ taskFn, args }) => this.execute(taskFn, ...(args || []))));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Get pool statistics.
|
|
363
|
+
*
|
|
364
|
+
* @returns {{activeWorkers: number, queuedTasks: number, totalTasksProcessed: number, avgTasksPerWorker: number}}
|
|
365
|
+
*/
|
|
366
|
+
getStats() {
|
|
367
|
+
const activeWorkers = this.workers.size;
|
|
368
|
+
const queuedTasks = this.taskQueue.length;
|
|
369
|
+
|
|
370
|
+
let totalTasksProcessed = 0;
|
|
371
|
+
for (const [, count] of this.workerTaskCounts) {
|
|
372
|
+
totalTasksProcessed += count;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const avgTasksPerWorker =
|
|
376
|
+
activeWorkers > 0 ? totalTasksProcessed / activeWorkers : 0;
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
activeWorkers,
|
|
380
|
+
queuedTasks,
|
|
381
|
+
totalTasksProcessed,
|
|
382
|
+
avgTasksPerWorker: parseFloat(avgTasksPerWorker.toFixed(2)),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Shutdown the worker pool.
|
|
388
|
+
*
|
|
389
|
+
* @param {boolean} [waitForQueue=false] - Wait for queued tasks to complete
|
|
390
|
+
* @returns {Promise<void>}
|
|
391
|
+
*/
|
|
392
|
+
async shutdown(waitForQueue = false) {
|
|
393
|
+
this.isShutdown = true;
|
|
394
|
+
|
|
395
|
+
if (waitForQueue) {
|
|
396
|
+
// Wait for queue to drain
|
|
397
|
+
while (this.taskQueue.length > 0) {
|
|
398
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
// Reject all queued tasks
|
|
402
|
+
for (const { reject } of this.taskQueue) {
|
|
403
|
+
reject(new Error('Worker pool shut down'));
|
|
404
|
+
}
|
|
405
|
+
this.taskQueue.length = 0;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Terminate all workers
|
|
409
|
+
const terminatePromises = [];
|
|
410
|
+
for (const [workerId, worker] of this.workers) {
|
|
411
|
+
terminatePromises.push(
|
|
412
|
+
new Promise(resolve => {
|
|
413
|
+
worker.terminate().then(() => resolve()).catch(() => resolve());
|
|
414
|
+
})
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// Clear idle timer
|
|
418
|
+
const idleTimer = this.idleTimers.get(workerId);
|
|
419
|
+
if (idleTimer) {
|
|
420
|
+
clearTimeout(idleTimer);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
await Promise.all(terminatePromises);
|
|
425
|
+
|
|
426
|
+
// Clear all data structures
|
|
427
|
+
this.workers.clear();
|
|
428
|
+
this.workerTaskCounts.clear();
|
|
429
|
+
this.idleTimers.clear();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* ========================================================================= */
|
|
434
|
+
/* Factory Functions */
|
|
435
|
+
/* ========================================================================= */
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Create a worker pool with default configuration.
|
|
439
|
+
*
|
|
440
|
+
* @param {Object} [config={}] - Pool configuration
|
|
441
|
+
* @returns {WorkerPool} - New worker pool instance
|
|
442
|
+
*/
|
|
443
|
+
export function createWorkerPool(config = {}) {
|
|
444
|
+
return new WorkerPool(config);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* ========================================================================= */
|
|
448
|
+
/* Singleton Global Pool */
|
|
449
|
+
/* ========================================================================= */
|
|
450
|
+
|
|
451
|
+
/** @type {WorkerPool} */
|
|
452
|
+
let globalWorkerPool = null;
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Get or create the global worker pool.
|
|
456
|
+
*
|
|
457
|
+
* @param {Object} [config={}] - Pool configuration (only used on first call)
|
|
458
|
+
* @returns {WorkerPool} - Global worker pool instance
|
|
459
|
+
*/
|
|
460
|
+
export function getGlobalWorkerPool(config = {}) {
|
|
461
|
+
if (!globalWorkerPool) {
|
|
462
|
+
globalWorkerPool = createWorkerPool(config);
|
|
463
|
+
}
|
|
464
|
+
return globalWorkerPool;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Shutdown the global worker pool.
|
|
469
|
+
*
|
|
470
|
+
* @param {boolean} [waitForQueue=false] - Wait for queued tasks to complete
|
|
471
|
+
* @returns {Promise<void>}
|
|
472
|
+
*/
|
|
473
|
+
export async function shutdownGlobalWorkerPool(waitForQueue = false) {
|
|
474
|
+
if (globalWorkerPool) {
|
|
475
|
+
await globalWorkerPool.shutdown(waitForQueue);
|
|
476
|
+
globalWorkerPool = null;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":"4.0.15","results":[[":test/example.test.mjs",{"duration":7.120207999999991,"failed":false}]]}
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":"4.0.15","results":[[":test/example.test.mjs",{"duration":6.318749999999994,"failed":false}]]}
|
|
Binary file
|