@zintrust/workers 0.4.27 → 0.4.36
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/dist/ClusterLock.js +1 -1
- package/dist/ComplianceManager.js +1 -1
- package/dist/DeadLetterQueue.js +1 -1
- package/dist/WorkerFactory.js +41 -17
- package/dist/WorkerMetrics.js +2 -2
- package/dist/build-manifest.json +39 -40
- package/dist/createQueueWorker.js +14 -9
- package/dist/index.d.ts +2 -1
- package/dist/register.js +1 -0
- package/package.json +5 -5
- package/src/ClusterLock.ts +1 -1
- package/src/ComplianceManager.ts +1 -1
- package/src/DeadLetterQueue.ts +1 -1
- package/src/WorkerFactory.ts +56 -18
- package/src/WorkerMetrics.ts +2 -2
- package/src/createQueueWorker.ts +27 -13
- package/src/index.ts +3 -2
- package/src/register.ts +2 -0
package/dist/ClusterLock.js
CHANGED
|
@@ -131,7 +131,7 @@ export const ClusterLock = Object.freeze({
|
|
|
131
131
|
Logger.warn('ClusterLock already initialized');
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
|
-
const client = createRedisConnection(config);
|
|
134
|
+
const client = createRedisConnection(config, 3, { subsystem: 'worker-cluster-lock' });
|
|
135
135
|
redisClient = client;
|
|
136
136
|
startHeartbeat(client);
|
|
137
137
|
Logger.info('ClusterLock initialized', { instanceId: getInstanceId() });
|
|
@@ -191,7 +191,7 @@ export const ComplianceManager = Object.freeze({
|
|
|
191
191
|
Logger.warn('ComplianceManager already initialized');
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
194
|
-
redisClient = createRedisConnection(redisConfig);
|
|
194
|
+
redisClient = createRedisConnection(redisConfig, 3, { subsystem: 'worker-compliance' });
|
|
195
195
|
complianceConfig = {
|
|
196
196
|
gdpr: { ...DEFAULT_CONFIG.gdpr, ...config?.gdpr },
|
|
197
197
|
hipaa: { ...DEFAULT_CONFIG.hipaa, ...config?.hipaa },
|
package/dist/DeadLetterQueue.js
CHANGED
|
@@ -161,7 +161,7 @@ export const DeadLetterQueue = Object.freeze({
|
|
|
161
161
|
Logger.warn('DeadLetterQueue already initialized');
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
|
-
redisClient = createRedisConnection(config);
|
|
164
|
+
redisClient = createRedisConnection(config, 3, { subsystem: 'worker-dlq' });
|
|
165
165
|
retentionPolicy = policy;
|
|
166
166
|
// Start cleanup interval if auto-delete is enabled
|
|
167
167
|
if (policy.enabled && policy.autoDeleteAfterDays !== undefined) {
|
package/dist/WorkerFactory.js
CHANGED
|
@@ -100,7 +100,6 @@ const resolvePackageSpecifierUrl = (specifier) => {
|
|
|
100
100
|
return resolveLocalPackageFallback(specifier);
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
-
const escapeRegExp = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
104
103
|
const rewriteProcessorImports = (code) => {
|
|
105
104
|
const replacements = [];
|
|
106
105
|
const coreUrl = resolvePackageSpecifierUrl('@zintrust/core');
|
|
@@ -113,8 +112,8 @@ const rewriteProcessorImports = (code) => {
|
|
|
113
112
|
return code;
|
|
114
113
|
let updated = code;
|
|
115
114
|
for (const { from, to } of replacements) {
|
|
116
|
-
|
|
117
|
-
updated = updated.
|
|
115
|
+
updated = updated.replaceAll(`'${from}'`, `'${to}'`);
|
|
116
|
+
updated = updated.replaceAll(`"${from}"`, `"${to}"`);
|
|
118
117
|
}
|
|
119
118
|
return updated;
|
|
120
119
|
};
|
|
@@ -330,14 +329,43 @@ const parseCacheControl = (value) => {
|
|
|
330
329
|
};
|
|
331
330
|
const getProcessorSpecConfig = () => workersConfig.processorSpec;
|
|
332
331
|
const toPosixPath = (value) => value.split(path.sep).join('/');
|
|
332
|
+
const isUpperAlpha = (value) => /^[A-Z]$/.test(value);
|
|
333
|
+
const isLowerAlphaOrDigit = (value) => /^[a-z\d]$/.test(value);
|
|
334
|
+
const isAlphaNumeric = (value) => /^[A-Za-z\d]$/.test(value);
|
|
335
|
+
const shouldInsertWorkerNameDash = (previous, current, next) => {
|
|
336
|
+
if (!isAlphaNumeric(previous) || !isAlphaNumeric(current))
|
|
337
|
+
return false;
|
|
338
|
+
if (isLowerAlphaOrDigit(previous) && isUpperAlpha(current)) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
if (isUpperAlpha(previous) && isUpperAlpha(current) && isLowerAlphaOrDigit(next ?? '')) {
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
return false;
|
|
345
|
+
};
|
|
346
|
+
const toKebabWorkerName = (value) => {
|
|
347
|
+
if (!isNonEmptyString(value))
|
|
348
|
+
return value;
|
|
349
|
+
let normalized = '';
|
|
350
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
351
|
+
const current = value[index] ?? '';
|
|
352
|
+
const previous = index > 0 ? (value[index - 1] ?? '') : '';
|
|
353
|
+
const next = value[index + 1];
|
|
354
|
+
if (current === ' ' || current === '_') {
|
|
355
|
+
if (!normalized.endsWith('-'))
|
|
356
|
+
normalized += '-';
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (shouldInsertWorkerNameDash(previous, current, next) && !normalized.endsWith('-')) {
|
|
360
|
+
normalized += '-';
|
|
361
|
+
}
|
|
362
|
+
normalized += current;
|
|
363
|
+
}
|
|
364
|
+
return normalized.replaceAll(/-+/g, '-');
|
|
365
|
+
};
|
|
333
366
|
const normalizeWorkerFileName = (fileName) => {
|
|
334
|
-
const baseName = fileName.
|
|
335
|
-
return baseName
|
|
336
|
-
.replaceAll(/([a-z\d])([A-Z])/g, '$1-$2')
|
|
337
|
-
.replaceAll(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1-$2')
|
|
338
|
-
.replaceAll(/[\s_]+/g, '-')
|
|
339
|
-
.replaceAll(/-+/g, '-')
|
|
340
|
-
.toLowerCase();
|
|
367
|
+
const baseName = fileName.replace(/\.[^.]+$/, '');
|
|
368
|
+
return toKebabWorkerName(baseName).toLowerCase();
|
|
341
369
|
};
|
|
342
370
|
const supportsWorkerFileDiscovery = () => {
|
|
343
371
|
return (isNodeRuntime() &&
|
|
@@ -1364,19 +1392,15 @@ const resolveRedisConfigFromEnv = (config, context) => {
|
|
|
1364
1392
|
};
|
|
1365
1393
|
const resolveRedisConfigFromDirect = (config, context) => {
|
|
1366
1394
|
const fallbackDb = Env.getInt('REDIS_QUEUE_DB', ZintrustLang.REDIS_DEFAULT_DB);
|
|
1367
|
-
const redisConfigWithDatabase = config;
|
|
1368
1395
|
let normalizedDb = fallbackDb;
|
|
1369
1396
|
if (typeof config.db === 'number') {
|
|
1370
1397
|
normalizedDb = config.db;
|
|
1371
1398
|
}
|
|
1372
|
-
else if (typeof redisConfigWithDatabase.database === 'number') {
|
|
1373
|
-
normalizedDb = redisConfigWithDatabase.database;
|
|
1374
|
-
}
|
|
1375
1399
|
return {
|
|
1376
1400
|
host: requireRedisHost(config.host, context),
|
|
1377
1401
|
port: config.port,
|
|
1378
1402
|
db: normalizedDb,
|
|
1379
|
-
password: config.password ?? Env.get('REDIS_PASSWORD'
|
|
1403
|
+
password: config.password ?? Env.get('REDIS_PASSWORD'),
|
|
1380
1404
|
};
|
|
1381
1405
|
};
|
|
1382
1406
|
const resolveRedisConfig = (config, context) => isRedisEnvConfig(config)
|
|
@@ -1498,7 +1522,7 @@ const resolveWorkerStore = async (config) => {
|
|
|
1498
1522
|
const redisConfig = resolveRedisConfigWithFallback(persistence.redis, config.infrastructure?.redis, 'Worker persistence requires redis config (persistence.redis or infrastructure.redis)', 'infrastructure.persistence.redis');
|
|
1499
1523
|
const key_prefix = persistence.keyPrefix ?? keyPrefix();
|
|
1500
1524
|
logRedisPersistenceConfig(redisConfig, key_prefix, 'resolveWorkerStore');
|
|
1501
|
-
const client = createRedisConnection(redisConfig);
|
|
1525
|
+
const client = createRedisConnection(redisConfig, 3, { subsystem: 'worker-persistence' });
|
|
1502
1526
|
next = RedisWorkerStore.create(client, key_prefix);
|
|
1503
1527
|
}
|
|
1504
1528
|
else if (persistence.driver === 'database') {
|
|
@@ -1542,7 +1566,7 @@ const createWorkerStore = async (persistence) => {
|
|
|
1542
1566
|
const redisConfig = resolveRedisConfigWithFallback(persistence.redis ?? { env: true }, undefined, 'Worker persistence requires redis config (persistence.redis or REDIS_* env values)', 'persistence.redis');
|
|
1543
1567
|
const key_prefix = persistence.keyPrefix ?? keyPrefix();
|
|
1544
1568
|
logRedisPersistenceConfig(redisConfig, key_prefix, 'createWorkerStore');
|
|
1545
|
-
const client = createRedisConnection(redisConfig);
|
|
1569
|
+
const client = createRedisConnection(redisConfig, 3, { subsystem: 'worker-persistence' });
|
|
1546
1570
|
return RedisWorkerStore.create(client, key_prefix);
|
|
1547
1571
|
}
|
|
1548
1572
|
// Database driver
|
package/dist/WorkerMetrics.js
CHANGED
|
@@ -31,7 +31,7 @@ const getValidClient = async () => {
|
|
|
31
31
|
}
|
|
32
32
|
// If no client, create one
|
|
33
33
|
if (!redisClient) {
|
|
34
|
-
redisClient = createRedisConnection(cachedConfig);
|
|
34
|
+
redisClient = createRedisConnection(cachedConfig, 3, { subsystem: 'worker-metrics' });
|
|
35
35
|
}
|
|
36
36
|
const client = redisClient;
|
|
37
37
|
if (!client) {
|
|
@@ -220,7 +220,7 @@ const WorkerMetrics = Object.freeze({
|
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
222
222
|
cachedConfig = config;
|
|
223
|
-
redisClient = createRedisConnection(config);
|
|
223
|
+
redisClient = createRedisConnection(config, 3, { subsystem: 'worker-metrics' });
|
|
224
224
|
Logger.info('WorkerMetrics initialized');
|
|
225
225
|
},
|
|
226
226
|
/**
|
package/dist/build-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/workers",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"buildDate": "2026-03-
|
|
3
|
+
"version": "0.4.36",
|
|
4
|
+
"buildDate": "2026-03-30T17:20:12.328Z",
|
|
5
5
|
"buildEnvironment": {
|
|
6
|
-
"node": "
|
|
6
|
+
"node": "v22.22.1",
|
|
7
7
|
"platform": "darwin",
|
|
8
8
|
"arch": "arm64"
|
|
9
9
|
},
|
|
10
10
|
"git": {
|
|
11
|
-
"commit": "
|
|
12
|
-
"branch": "
|
|
11
|
+
"commit": "a45b4628",
|
|
12
|
+
"branch": "release"
|
|
13
13
|
},
|
|
14
14
|
"package": {
|
|
15
15
|
"engines": {
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"@opentelemetry/api",
|
|
20
20
|
"hot-shots",
|
|
21
21
|
"ioredis",
|
|
22
|
-
"ml.js",
|
|
23
22
|
"prom-client",
|
|
24
23
|
"simple-statistics"
|
|
25
24
|
],
|
|
@@ -47,8 +46,8 @@
|
|
|
47
46
|
"sha256": "1a6d9ea39ba9e17fc16eb52294c10c3912024cb3009a8f9c07ffa576e754c7f4"
|
|
48
47
|
},
|
|
49
48
|
"BroadcastWorker.d.ts": {
|
|
50
|
-
"size":
|
|
51
|
-
"sha256": "
|
|
49
|
+
"size": 934,
|
|
50
|
+
"sha256": "ea5b05820fdd30d80f4483f476e400c78fa842ebbb29a442b2f1261dc0b2de8a"
|
|
52
51
|
},
|
|
53
52
|
"BroadcastWorker.js": {
|
|
54
53
|
"size": 821,
|
|
@@ -83,16 +82,16 @@
|
|
|
83
82
|
"sha256": "77439b1e80a344ff2de380082886addda0e8ca0419e57e7039e9f6ad4c596462"
|
|
84
83
|
},
|
|
85
84
|
"ClusterLock.js": {
|
|
86
|
-
"size":
|
|
87
|
-
"sha256": "
|
|
85
|
+
"size": 13488,
|
|
86
|
+
"sha256": "d216923cb5829a14155f2acfd7691075e9ee1d262fd25711afe877a7920642ca"
|
|
88
87
|
},
|
|
89
88
|
"ComplianceManager.d.ts": {
|
|
90
89
|
"size": 5142,
|
|
91
90
|
"sha256": "19a2695093bc102680cd8f28775d2b2b5efea5ad52b682c89fedbf4424b4b711"
|
|
92
91
|
},
|
|
93
92
|
"ComplianceManager.js": {
|
|
94
|
-
"size":
|
|
95
|
-
"sha256": "
|
|
93
|
+
"size": 19653,
|
|
94
|
+
"sha256": "79fd228c4bae3d13fa64a75809fb112c65624560558fa57d6b7544a5ad1cf4f5"
|
|
96
95
|
},
|
|
97
96
|
"DatacenterOrchestrator.d.ts": {
|
|
98
97
|
"size": 3532,
|
|
@@ -107,8 +106,8 @@
|
|
|
107
106
|
"sha256": "dd82e7e2c23a2fcaefc0cb87a1a7931594d6a5f76b758f608c77063f5e8533cd"
|
|
108
107
|
},
|
|
109
108
|
"DeadLetterQueue.js": {
|
|
110
|
-
"size":
|
|
111
|
-
"sha256": "
|
|
109
|
+
"size": 19476,
|
|
110
|
+
"sha256": "de0ac7ee53a71e375536041a3ce65be22bc29f1901af8564f1e60307007c061e"
|
|
112
111
|
},
|
|
113
112
|
"HealthMonitor.d.ts": {
|
|
114
113
|
"size": 1485,
|
|
@@ -127,8 +126,8 @@
|
|
|
127
126
|
"sha256": "23837171b31c5242e4b43226cb0acae815dea0ce7b1628aa1428a86b24689230"
|
|
128
127
|
},
|
|
129
128
|
"NotificationWorker.d.ts": {
|
|
130
|
-
"size":
|
|
131
|
-
"sha256": "
|
|
129
|
+
"size": 952,
|
|
130
|
+
"sha256": "facdae5d7e82364ad9aab4398f8a3f8b5be8d8e9cb3aaa18fde76a88cd05b959"
|
|
132
131
|
},
|
|
133
132
|
"NotificationWorker.js": {
|
|
134
133
|
"size": 828,
|
|
@@ -175,28 +174,28 @@
|
|
|
175
174
|
"sha256": "555277c91f87899415cebf35eddc08d7c0b80cc55bfac7cb7fe96529cc927257"
|
|
176
175
|
},
|
|
177
176
|
"WorkerFactory.d.ts": {
|
|
178
|
-
"size":
|
|
179
|
-
"sha256": "
|
|
177
|
+
"size": 7716,
|
|
178
|
+
"sha256": "3869f960c87260588e40941ff91bffcfa0757be7a04815fd28b57dd4840c51df"
|
|
180
179
|
},
|
|
181
180
|
"WorkerFactory.js": {
|
|
182
|
-
"size":
|
|
183
|
-
"sha256": "
|
|
181
|
+
"size": 102863,
|
|
182
|
+
"sha256": "1024756e603ca67461a955723dc004de9395267e50c7f59c9f8f0c13b2f0f7d8"
|
|
184
183
|
},
|
|
185
184
|
"WorkerInit.d.ts": {
|
|
186
|
-
"size":
|
|
187
|
-
"sha256": "
|
|
185
|
+
"size": 3284,
|
|
186
|
+
"sha256": "f902550cda7fca36f2db6297d39a4339ed6b5ff1c738da867233309cf0ca8fe1"
|
|
188
187
|
},
|
|
189
188
|
"WorkerInit.js": {
|
|
190
|
-
"size":
|
|
191
|
-
"sha256": "
|
|
189
|
+
"size": 14031,
|
|
190
|
+
"sha256": "51f44ca085a1682130aa36fed3acfd575108e974bf4c21eaa772001719c18853"
|
|
192
191
|
},
|
|
193
192
|
"WorkerMetrics.d.ts": {
|
|
194
193
|
"size": 3304,
|
|
195
194
|
"sha256": "5b684f87668bba86825625c20dda162915674e8c0fe3c3a32e05a2d9951c985f"
|
|
196
195
|
},
|
|
197
196
|
"WorkerMetrics.js": {
|
|
198
|
-
"size":
|
|
199
|
-
"sha256": "
|
|
197
|
+
"size": 20656,
|
|
198
|
+
"sha256": "a3888a82cdc8311887e1320a320d11630cf9358d80681be21f6fc02c90fee2fa"
|
|
200
199
|
},
|
|
201
200
|
"WorkerRegistry.d.ts": {
|
|
202
201
|
"size": 3941,
|
|
@@ -231,8 +230,8 @@
|
|
|
231
230
|
"sha256": "8af20d462270e7044c6ea983821f5b6e6ce8a5caf39b6e8fefff07c9a0bf071e"
|
|
232
231
|
},
|
|
233
232
|
"build-manifest.json": {
|
|
234
|
-
"size":
|
|
235
|
-
"sha256": "
|
|
233
|
+
"size": 19591,
|
|
234
|
+
"sha256": "09f2e951c3a969cd97fcfc9ed8133e14da9e4b4250453c84d69ef973dd7bedec"
|
|
236
235
|
},
|
|
237
236
|
"config/workerConfig.d.ts": {
|
|
238
237
|
"size": 132,
|
|
@@ -243,12 +242,12 @@
|
|
|
243
242
|
"sha256": "189c3b1d8a31de1c04206fcfacc1fed974a74684d7d70895cd20a03b35860ac6"
|
|
244
243
|
},
|
|
245
244
|
"createQueueWorker.d.ts": {
|
|
246
|
-
"size":
|
|
247
|
-
"sha256": "
|
|
245
|
+
"size": 1031,
|
|
246
|
+
"sha256": "dacd49f6c112eba439bdd9bb457eea90daedbf32efc381cd3189ce562fa5b0a8"
|
|
248
247
|
},
|
|
249
248
|
"createQueueWorker.js": {
|
|
250
|
-
"size":
|
|
251
|
-
"sha256": "
|
|
249
|
+
"size": 14103,
|
|
250
|
+
"sha256": "8e619da00200c0c1270674a6a2941ae050c8ade3e38925b5446916cddc2a8b65"
|
|
252
251
|
},
|
|
253
252
|
"dashboard/index.d.ts": {
|
|
254
253
|
"size": 109,
|
|
@@ -271,8 +270,8 @@
|
|
|
271
270
|
"sha256": "8e0e04329e1119d8ae835dd4458efead084293bcc2c263c09dd5a19d467e5ca4"
|
|
272
271
|
},
|
|
273
272
|
"dashboard/workers-api.js": {
|
|
274
|
-
"size":
|
|
275
|
-
"sha256": "
|
|
273
|
+
"size": 28207,
|
|
274
|
+
"sha256": "ae1ff0e962b1f64e6d200a35c3b8b1de968341d8df6283625a3efce50a23826c"
|
|
276
275
|
},
|
|
277
276
|
"helper/index.d.ts": {
|
|
278
277
|
"size": 159,
|
|
@@ -411,20 +410,20 @@
|
|
|
411
410
|
"sha256": "e90a171f2d8f6a62518099f825775b80299b934a7d48421bc1a3ebd6b065d617"
|
|
412
411
|
},
|
|
413
412
|
"index.d.ts": {
|
|
414
|
-
"size":
|
|
415
|
-
"sha256": "
|
|
413
|
+
"size": 2721,
|
|
414
|
+
"sha256": "0dcb7ca21683ab210eeb23d067e41b55ae996fa9e52c688371dc177f070ec059"
|
|
416
415
|
},
|
|
417
416
|
"index.js": {
|
|
418
|
-
"size":
|
|
419
|
-
"sha256": "
|
|
417
|
+
"size": 2337,
|
|
418
|
+
"sha256": "d6f01d1d1f313d649fd5072e70617e3e28061e9c55d25ce24c3ea2d06e2cb8eb"
|
|
420
419
|
},
|
|
421
420
|
"register.d.ts": {
|
|
422
421
|
"size": 256,
|
|
423
422
|
"sha256": "07753654e043fd8ac0d42c4d74a26406929fc076c8c86b3d0513898c7da0d0aa"
|
|
424
423
|
},
|
|
425
424
|
"register.js": {
|
|
426
|
-
"size":
|
|
427
|
-
"sha256": "
|
|
425
|
+
"size": 1626,
|
|
426
|
+
"sha256": "4c404dfb2df0ac42da904bf30cece14ddbe127b8408d1ded951bf60ca86d07a3"
|
|
428
427
|
},
|
|
429
428
|
"routes/workers.d.ts": {
|
|
430
429
|
"size": 498,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Core from '@zintrust/core';
|
|
2
2
|
import { Env, Logger, Queue } from '@zintrust/core';
|
|
3
|
+
const TypedQueue = Queue;
|
|
3
4
|
const RETRY_BASE_DELAY_MS = 1000;
|
|
4
5
|
const RETRY_MAX_DELAY_MS = 30000;
|
|
5
6
|
const getJobStateTracker = () => {
|
|
@@ -92,6 +93,12 @@ const buildBaseLogFields = (message, getLogFields) => {
|
|
|
92
93
|
...getLogFields(message.payload),
|
|
93
94
|
};
|
|
94
95
|
};
|
|
96
|
+
const toBullMQPayload = (payload) => {
|
|
97
|
+
if (typeof payload === 'object' && payload !== null) {
|
|
98
|
+
return { ...payload };
|
|
99
|
+
}
|
|
100
|
+
return { payload };
|
|
101
|
+
};
|
|
95
102
|
const getWorkerInstanceId = () => {
|
|
96
103
|
return typeof Env['WORKER_INSTANCE_ID'] === 'string'
|
|
97
104
|
? String(Env['WORKER_INSTANCE_ID'])
|
|
@@ -139,12 +146,12 @@ const checkAndRequeueIfNotDue = async (options, queueName, driverName, message,
|
|
|
139
146
|
...baseLogFields,
|
|
140
147
|
dueAt: new Date(timestamp).toISOString(),
|
|
141
148
|
});
|
|
142
|
-
await
|
|
143
|
-
await
|
|
149
|
+
await TypedQueue.enqueue(queueName, toBullMQPayload(message.payload), driverName);
|
|
150
|
+
await TypedQueue.ack(queueName, message.id, driverName);
|
|
144
151
|
return true;
|
|
145
152
|
};
|
|
146
153
|
const onProcessSuccess = async (input) => {
|
|
147
|
-
await
|
|
154
|
+
await TypedQueue.ack(input.queueName, input.message.id, input.driverName);
|
|
148
155
|
if (typeof input.trackerApi.completed === 'function') {
|
|
149
156
|
await input.trackerApi.completed({
|
|
150
157
|
queueName: input.queueName,
|
|
@@ -177,22 +184,20 @@ const onProcessFailure = async (input) => {
|
|
|
177
184
|
if (nextAttempts < input.options.maxAttempts) {
|
|
178
185
|
const retryDelayMs = getRetryDelayMs(nextAttempts);
|
|
179
186
|
retryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
180
|
-
const currentPayload =
|
|
181
|
-
? input.message.payload
|
|
182
|
-
: { payload: input.message.payload };
|
|
187
|
+
const currentPayload = toBullMQPayload(input.message.payload);
|
|
183
188
|
const payloadForRetry = {
|
|
184
189
|
...currentPayload,
|
|
185
190
|
attempts: nextAttempts,
|
|
186
191
|
timestamp: Date.now() + retryDelayMs,
|
|
187
192
|
};
|
|
188
|
-
await
|
|
193
|
+
await TypedQueue.enqueue(input.queueName, payloadForRetry, input.driverName);
|
|
189
194
|
Logger.info(`${input.options.kindLabel} re-queued for retry`, {
|
|
190
195
|
...input.baseLogFields,
|
|
191
196
|
attempts: nextAttempts,
|
|
192
197
|
retryDelayMs,
|
|
193
198
|
});
|
|
194
199
|
}
|
|
195
|
-
await
|
|
200
|
+
await TypedQueue.ack(input.queueName, input.message.id, input.driverName);
|
|
196
201
|
await removeHeartbeatIfSupported(input.queueName, input.message.id);
|
|
197
202
|
if (typeof input.trackerApi.failed === 'function') {
|
|
198
203
|
await input.trackerApi.failed({
|
|
@@ -235,7 +240,7 @@ const startTrackingAndHeartbeat = async (input) => {
|
|
|
235
240
|
return { startedAtMs, heartbeatTimer };
|
|
236
241
|
};
|
|
237
242
|
const processQueueMessage = async (options, queueName, driverName) => {
|
|
238
|
-
const message =
|
|
243
|
+
const message = await TypedQueue.dequeue(queueName, driverName);
|
|
239
244
|
if (!message)
|
|
240
245
|
return false;
|
|
241
246
|
const baseLogFields = buildBaseLogFields(message, options.getLogFields);
|
package/dist/index.d.ts
CHANGED
|
@@ -32,11 +32,12 @@ export { BroadcastWorker } from './BroadcastWorker';
|
|
|
32
32
|
export { createQueueWorker } from './createQueueWorker';
|
|
33
33
|
export type { CreateQueueWorkerOptions } from './createQueueWorker';
|
|
34
34
|
export { NotificationWorker } from './NotificationWorker';
|
|
35
|
-
export type { RedisConfig, WorkerAutoScalingConfig, WorkerComplianceConfig, WorkerConfig, WorkerCostConfig, WorkerObservabilityConfig,
|
|
35
|
+
export type { RedisConfig, WorkerAutoScalingConfig, WorkerComplianceConfig, WorkerConfig, WorkerCostConfig, WorkerObservabilityConfig, WorkersConfigOverrides, WorkersGlobalConfig, WorkerStatus, WorkerVersioningConfig, } from '@zintrust/core';
|
|
36
36
|
export type { Job, Worker, WorkerOptions } from 'bullmq';
|
|
37
37
|
export type { IAnomaly, IAnomalyConfig, IForecast, IMetric, IPrediction, IRecommendation, IRootCauseAnalysis, } from './AnomalyDetection';
|
|
38
38
|
export type { IChaosComparison, IChaosExperiment, IChaosReport, IChaosStatus, } from './ChaosEngineering';
|
|
39
39
|
export type { ISLAConfig, ISLAReport, ISLAStatus, ISLAViolation, ITimeRange } from './SLAMonitor';
|
|
40
|
+
export type * from './config/workerConfig';
|
|
40
41
|
export type * from './type';
|
|
41
42
|
/**
|
|
42
43
|
* Package version and build metadata
|
package/dist/register.js
CHANGED
|
@@ -16,6 +16,7 @@ const getWorkerProviders = () => {
|
|
|
16
16
|
['worker:start-all', WorkerCommands.createWorkerStartAllCommand()],
|
|
17
17
|
['worker:stop', WorkerCommands.createWorkerStopCommand()],
|
|
18
18
|
['worker:restart', WorkerCommands.createWorkerRestartCommand()],
|
|
19
|
+
['worker:doctor', WorkerCommands.createWorkerDoctorCommand()],
|
|
19
20
|
['worker:summary', WorkerCommands.createWorkerSummaryCommand()],
|
|
20
21
|
];
|
|
21
22
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/workers",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.36",
|
|
4
4
|
"description": "Worker orchestration and background job management for ZinTrust.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@zintrust/core": "^0.4.
|
|
43
|
+
"@zintrust/core": "^0.4.36",
|
|
44
44
|
"@zintrust/queue-monitor": "*",
|
|
45
45
|
"@zintrust/queue-redis": "*"
|
|
46
46
|
},
|
|
@@ -67,10 +67,10 @@
|
|
|
67
67
|
"prepublishOnly": "npm run build"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@opentelemetry/api": "^1.9.
|
|
70
|
+
"@opentelemetry/api": "^1.9.1",
|
|
71
71
|
"hot-shots": "^14.2.0",
|
|
72
|
-
"ioredis": "^5.10.
|
|
72
|
+
"ioredis": "^5.10.1",
|
|
73
73
|
"prom-client": "^15.1.3",
|
|
74
74
|
"simple-statistics": "^7.8.9"
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|
package/src/ClusterLock.ts
CHANGED
|
@@ -193,7 +193,7 @@ export const ClusterLock = Object.freeze({
|
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
const client = createRedisConnection(config);
|
|
196
|
+
const client = createRedisConnection(config, 3, { subsystem: 'worker-cluster-lock' });
|
|
197
197
|
redisClient = client;
|
|
198
198
|
startHeartbeat(client);
|
|
199
199
|
|
package/src/ComplianceManager.ts
CHANGED
|
@@ -344,7 +344,7 @@ export const ComplianceManager = Object.freeze({
|
|
|
344
344
|
return;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
redisClient = createRedisConnection(redisConfig);
|
|
347
|
+
redisClient = createRedisConnection(redisConfig, 3, { subsystem: 'worker-compliance' });
|
|
348
348
|
complianceConfig = {
|
|
349
349
|
gdpr: { ...DEFAULT_CONFIG.gdpr, ...config?.gdpr },
|
|
350
350
|
hipaa: { ...DEFAULT_CONFIG.hipaa, ...config?.hipaa },
|
package/src/DeadLetterQueue.ts
CHANGED
|
@@ -260,7 +260,7 @@ export const DeadLetterQueue = Object.freeze({
|
|
|
260
260
|
return;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
redisClient = createRedisConnection(config);
|
|
263
|
+
redisClient = createRedisConnection(config, 3, { subsystem: 'worker-dlq' });
|
|
264
264
|
retentionPolicy = policy;
|
|
265
265
|
|
|
266
266
|
// Start cleanup interval if auto-delete is enabled
|
package/src/WorkerFactory.ts
CHANGED
|
@@ -141,9 +141,6 @@ const resolvePackageSpecifierUrl = (specifier: string): string | null => {
|
|
|
141
141
|
}
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
-
const escapeRegExp = (value: string): string =>
|
|
145
|
-
value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
146
|
-
|
|
147
144
|
const rewriteProcessorImports = (code: string): string => {
|
|
148
145
|
const replacements: Array<{ from: string; to: string }> = [];
|
|
149
146
|
const coreUrl = resolvePackageSpecifierUrl('@zintrust/core');
|
|
@@ -155,8 +152,8 @@ const rewriteProcessorImports = (code: string): string => {
|
|
|
155
152
|
|
|
156
153
|
let updated = code;
|
|
157
154
|
for (const { from, to } of replacements) {
|
|
158
|
-
|
|
159
|
-
updated = updated.
|
|
155
|
+
updated = updated.replaceAll(`'${from}'`, `'${to}'`);
|
|
156
|
+
updated = updated.replaceAll(`"${from}"`, `"${to}"`);
|
|
160
157
|
}
|
|
161
158
|
|
|
162
159
|
return updated;
|
|
@@ -548,14 +545,58 @@ const getProcessorSpecConfig = (): typeof workersConfig.processorSpec =>
|
|
|
548
545
|
|
|
549
546
|
const toPosixPath = (value: string): string => value.split(path.sep).join('/');
|
|
550
547
|
|
|
548
|
+
const isUpperAlpha = (value: string): boolean => /^[A-Z]$/.test(value);
|
|
549
|
+
|
|
550
|
+
const isLowerAlphaOrDigit = (value: string): boolean => /^[a-z\d]$/.test(value);
|
|
551
|
+
|
|
552
|
+
const isAlphaNumeric = (value: string): boolean => /^[A-Za-z\d]$/.test(value);
|
|
553
|
+
|
|
554
|
+
const shouldInsertWorkerNameDash = (
|
|
555
|
+
previous: string,
|
|
556
|
+
current: string,
|
|
557
|
+
next: string | undefined
|
|
558
|
+
): boolean => {
|
|
559
|
+
if (!isAlphaNumeric(previous) || !isAlphaNumeric(current)) return false;
|
|
560
|
+
|
|
561
|
+
if (isLowerAlphaOrDigit(previous) && isUpperAlpha(current)) {
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (isUpperAlpha(previous) && isUpperAlpha(current) && isLowerAlphaOrDigit(next ?? '')) {
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return false;
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
const toKebabWorkerName = (value: string): string => {
|
|
573
|
+
if (!isNonEmptyString(value)) return value;
|
|
574
|
+
|
|
575
|
+
let normalized = '';
|
|
576
|
+
|
|
577
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
578
|
+
const current = value[index] ?? '';
|
|
579
|
+
const previous = index > 0 ? (value[index - 1] ?? '') : '';
|
|
580
|
+
const next = value[index + 1];
|
|
581
|
+
|
|
582
|
+
if (current === ' ' || current === '_') {
|
|
583
|
+
if (!normalized.endsWith('-')) normalized += '-';
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (shouldInsertWorkerNameDash(previous, current, next) && !normalized.endsWith('-')) {
|
|
588
|
+
normalized += '-';
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
normalized += current;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return normalized.replaceAll(/-+/g, '-');
|
|
595
|
+
};
|
|
596
|
+
|
|
551
597
|
const normalizeWorkerFileName = (fileName: string): string => {
|
|
552
|
-
const baseName = fileName.
|
|
553
|
-
return baseName
|
|
554
|
-
.replaceAll(/([a-z\d])([A-Z])/g, '$1-$2')
|
|
555
|
-
.replaceAll(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1-$2')
|
|
556
|
-
.replaceAll(/[\s_]+/g, '-')
|
|
557
|
-
.replaceAll(/-+/g, '-')
|
|
558
|
-
.toLowerCase();
|
|
598
|
+
const baseName = fileName.replace(/\.[^.]+$/, '');
|
|
599
|
+
return toKebabWorkerName(baseName).toLowerCase();
|
|
559
600
|
};
|
|
560
601
|
|
|
561
602
|
const supportsWorkerFileDiscovery = (): boolean => {
|
|
@@ -1984,20 +2025,17 @@ const resolveRedisConfigFromEnv = (config: RedisEnvConfig, context: string): Red
|
|
|
1984
2025
|
|
|
1985
2026
|
const resolveRedisConfigFromDirect = (config: RedisConfig, context: string): RedisConfig => {
|
|
1986
2027
|
const fallbackDb = Env.getInt('REDIS_QUEUE_DB', ZintrustLang.REDIS_DEFAULT_DB);
|
|
1987
|
-
const redisConfigWithDatabase = config as RedisConfig & { database?: number };
|
|
1988
2028
|
|
|
1989
2029
|
let normalizedDb = fallbackDb;
|
|
1990
2030
|
if (typeof config.db === 'number') {
|
|
1991
2031
|
normalizedDb = config.db;
|
|
1992
|
-
} else if (typeof redisConfigWithDatabase.database === 'number') {
|
|
1993
|
-
normalizedDb = redisConfigWithDatabase.database;
|
|
1994
2032
|
}
|
|
1995
2033
|
|
|
1996
2034
|
return {
|
|
1997
2035
|
host: requireRedisHost(config.host, context),
|
|
1998
2036
|
port: config.port,
|
|
1999
2037
|
db: normalizedDb,
|
|
2000
|
-
password: config.password ?? Env.get('REDIS_PASSWORD'
|
|
2038
|
+
password: config.password ?? Env.get('REDIS_PASSWORD'),
|
|
2001
2039
|
};
|
|
2002
2040
|
};
|
|
2003
2041
|
|
|
@@ -2163,7 +2201,7 @@ const resolveWorkerStore = async (config: WorkerFactoryConfig): Promise<WorkerSt
|
|
|
2163
2201
|
);
|
|
2164
2202
|
const key_prefix = persistence.keyPrefix ?? keyPrefix();
|
|
2165
2203
|
logRedisPersistenceConfig(redisConfig, key_prefix, 'resolveWorkerStore');
|
|
2166
|
-
const client = createRedisConnection(redisConfig);
|
|
2204
|
+
const client = createRedisConnection(redisConfig, 3, { subsystem: 'worker-persistence' });
|
|
2167
2205
|
next = RedisWorkerStore.create(client, key_prefix);
|
|
2168
2206
|
} else if (persistence.driver === 'database') {
|
|
2169
2207
|
const explicitConnection =
|
|
@@ -2217,7 +2255,7 @@ const createWorkerStore = async (persistence: WorkerPersistenceConfig): Promise<
|
|
|
2217
2255
|
);
|
|
2218
2256
|
const key_prefix = persistence.keyPrefix ?? keyPrefix();
|
|
2219
2257
|
logRedisPersistenceConfig(redisConfig, key_prefix, 'createWorkerStore');
|
|
2220
|
-
const client = createRedisConnection(redisConfig);
|
|
2258
|
+
const client = createRedisConnection(redisConfig, 3, { subsystem: 'worker-persistence' });
|
|
2221
2259
|
return RedisWorkerStore.create(client, key_prefix);
|
|
2222
2260
|
}
|
|
2223
2261
|
|
package/src/WorkerMetrics.ts
CHANGED
|
@@ -111,7 +111,7 @@ const getValidClient = async (): Promise<RedisConnection> => {
|
|
|
111
111
|
|
|
112
112
|
// If no client, create one
|
|
113
113
|
if (!redisClient) {
|
|
114
|
-
redisClient = createRedisConnection(cachedConfig);
|
|
114
|
+
redisClient = createRedisConnection(cachedConfig, 3, { subsystem: 'worker-metrics' });
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
const client = redisClient;
|
|
@@ -358,7 +358,7 @@ const WorkerMetrics = Object.freeze({
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
cachedConfig = config;
|
|
361
|
-
redisClient = createRedisConnection(config);
|
|
361
|
+
redisClient = createRedisConnection(config, 3, { subsystem: 'worker-metrics' });
|
|
362
362
|
Logger.info('WorkerMetrics initialized');
|
|
363
363
|
},
|
|
364
364
|
|
package/src/createQueueWorker.ts
CHANGED
|
@@ -2,6 +2,17 @@ import type { BullMQPayload, QueueMessage } from '@zintrust/core';
|
|
|
2
2
|
import * as Core from '@zintrust/core';
|
|
3
3
|
import { Env, Logger, Queue } from '@zintrust/core';
|
|
4
4
|
|
|
5
|
+
type QueueApi = Readonly<{
|
|
6
|
+
enqueue: (queue: string, payload: BullMQPayload, driverName?: string) => Promise<string>;
|
|
7
|
+
dequeue: <TPayload>(
|
|
8
|
+
queue: string,
|
|
9
|
+
driverName?: string
|
|
10
|
+
) => Promise<QueueMessage<TPayload> | undefined>;
|
|
11
|
+
ack: (queue: string, id: string, driverName?: string) => Promise<void>;
|
|
12
|
+
}>;
|
|
13
|
+
|
|
14
|
+
const TypedQueue = Queue as QueueApi;
|
|
15
|
+
|
|
5
16
|
const RETRY_BASE_DELAY_MS = 1000;
|
|
6
17
|
const RETRY_MAX_DELAY_MS = 30000;
|
|
7
18
|
|
|
@@ -97,9 +108,7 @@ const getAttemptsFromMessage = <TPayload>(message: QueueMessage<TPayload>): numb
|
|
|
97
108
|
typeof message.payload === 'object' && message.payload !== null
|
|
98
109
|
? normalizeAttempts((message.payload as Record<string, unknown>)['attempts'])
|
|
99
110
|
: 0;
|
|
100
|
-
const messageAttempts = normalizeAttempts(
|
|
101
|
-
(message as QueueMessage<TPayload> & { attempts?: number }).attempts
|
|
102
|
-
);
|
|
111
|
+
const messageAttempts = normalizeAttempts(message.attempts);
|
|
103
112
|
return Math.max(payloadAttempts, messageAttempts);
|
|
104
113
|
};
|
|
105
114
|
|
|
@@ -152,6 +161,14 @@ const buildBaseLogFields = <TPayload>(
|
|
|
152
161
|
};
|
|
153
162
|
};
|
|
154
163
|
|
|
164
|
+
const toBullMQPayload = <TPayload>(payload: TPayload): BullMQPayload => {
|
|
165
|
+
if (typeof payload === 'object' && payload !== null) {
|
|
166
|
+
return { ...(payload as Record<string, unknown>) };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { payload };
|
|
170
|
+
};
|
|
171
|
+
|
|
155
172
|
type TrackerApi = {
|
|
156
173
|
started?: (input: {
|
|
157
174
|
queueName: string;
|
|
@@ -263,8 +280,8 @@ const checkAndRequeueIfNotDue = async <TPayload>(
|
|
|
263
280
|
...baseLogFields,
|
|
264
281
|
dueAt: new Date(timestamp).toISOString(),
|
|
265
282
|
});
|
|
266
|
-
await
|
|
267
|
-
await
|
|
283
|
+
await TypedQueue.enqueue(queueName, toBullMQPayload(message.payload), driverName);
|
|
284
|
+
await TypedQueue.ack(queueName, message.id, driverName);
|
|
268
285
|
return true;
|
|
269
286
|
};
|
|
270
287
|
|
|
@@ -277,7 +294,7 @@ const onProcessSuccess = async <TPayload>(input: {
|
|
|
277
294
|
startedAtMs: number;
|
|
278
295
|
baseLogFields: Record<string, unknown>;
|
|
279
296
|
}): Promise<boolean> => {
|
|
280
|
-
await
|
|
297
|
+
await TypedQueue.ack(input.queueName, input.message.id, input.driverName);
|
|
281
298
|
|
|
282
299
|
if (typeof input.trackerApi.completed === 'function') {
|
|
283
300
|
await input.trackerApi.completed({
|
|
@@ -324,10 +341,7 @@ const onProcessFailure = async <TPayload>(input: {
|
|
|
324
341
|
if (nextAttempts < input.options.maxAttempts) {
|
|
325
342
|
const retryDelayMs = getRetryDelayMs(nextAttempts);
|
|
326
343
|
retryAt = new Date(Date.now() + retryDelayMs).toISOString();
|
|
327
|
-
const currentPayload =
|
|
328
|
-
typeof input.message.payload === 'object' && input.message.payload !== null
|
|
329
|
-
? (input.message.payload as Record<string, unknown>)
|
|
330
|
-
: ({ payload: input.message.payload } as Record<string, unknown>);
|
|
344
|
+
const currentPayload = toBullMQPayload(input.message.payload);
|
|
331
345
|
|
|
332
346
|
const payloadForRetry: BullMQPayload = {
|
|
333
347
|
...currentPayload,
|
|
@@ -335,7 +349,7 @@ const onProcessFailure = async <TPayload>(input: {
|
|
|
335
349
|
timestamp: Date.now() + retryDelayMs,
|
|
336
350
|
};
|
|
337
351
|
|
|
338
|
-
await
|
|
352
|
+
await TypedQueue.enqueue(input.queueName, payloadForRetry, input.driverName);
|
|
339
353
|
Logger.info(`${input.options.kindLabel} re-queued for retry`, {
|
|
340
354
|
...input.baseLogFields,
|
|
341
355
|
attempts: nextAttempts,
|
|
@@ -343,7 +357,7 @@ const onProcessFailure = async <TPayload>(input: {
|
|
|
343
357
|
});
|
|
344
358
|
}
|
|
345
359
|
|
|
346
|
-
await
|
|
360
|
+
await TypedQueue.ack(input.queueName, input.message.id, input.driverName);
|
|
347
361
|
await removeHeartbeatIfSupported(input.queueName, input.message.id);
|
|
348
362
|
|
|
349
363
|
if (typeof input.trackerApi.failed === 'function') {
|
|
@@ -409,7 +423,7 @@ const processQueueMessage = async <TPayload>(
|
|
|
409
423
|
queueName: string,
|
|
410
424
|
driverName?: string
|
|
411
425
|
): Promise<boolean> => {
|
|
412
|
-
const message =
|
|
426
|
+
const message = await TypedQueue.dequeue<TPayload>(queueName, driverName);
|
|
413
427
|
if (!message) return false;
|
|
414
428
|
|
|
415
429
|
const baseLogFields = buildBaseLogFields(message, options.getLogFields);
|
package/src/index.ts
CHANGED
|
@@ -71,10 +71,10 @@ export type {
|
|
|
71
71
|
WorkerConfig,
|
|
72
72
|
WorkerCostConfig,
|
|
73
73
|
WorkerObservabilityConfig,
|
|
74
|
-
WorkerStatus,
|
|
75
|
-
WorkerVersioningConfig,
|
|
76
74
|
WorkersConfigOverrides,
|
|
77
75
|
WorkersGlobalConfig,
|
|
76
|
+
WorkerStatus,
|
|
77
|
+
WorkerVersioningConfig,
|
|
78
78
|
} from '@zintrust/core';
|
|
79
79
|
|
|
80
80
|
// Re-export bullmq types for type compatibility
|
|
@@ -97,6 +97,7 @@ export type {
|
|
|
97
97
|
} from './ChaosEngineering';
|
|
98
98
|
export type { ISLAConfig, ISLAReport, ISLAStatus, ISLAViolation, ITimeRange } from './SLAMonitor';
|
|
99
99
|
|
|
100
|
+
export type * from './config/workerConfig';
|
|
100
101
|
export type * from './type';
|
|
101
102
|
|
|
102
103
|
/**
|
package/src/register.ts
CHANGED
|
@@ -15,6 +15,7 @@ type WorkerCommandsModule = {
|
|
|
15
15
|
createWorkerStartAllCommand: () => CliCommandProvider;
|
|
16
16
|
createWorkerStopCommand: () => CliCommandProvider;
|
|
17
17
|
createWorkerRestartCommand: () => CliCommandProvider;
|
|
18
|
+
createWorkerDoctorCommand: () => CliCommandProvider;
|
|
18
19
|
createWorkerSummaryCommand: () => CliCommandProvider;
|
|
19
20
|
};
|
|
20
21
|
};
|
|
@@ -38,6 +39,7 @@ const getWorkerProviders = (): Array<[string, CliCommandProvider]> => {
|
|
|
38
39
|
['worker:start-all', WorkerCommands.createWorkerStartAllCommand()],
|
|
39
40
|
['worker:stop', WorkerCommands.createWorkerStopCommand()],
|
|
40
41
|
['worker:restart', WorkerCommands.createWorkerRestartCommand()],
|
|
42
|
+
['worker:doctor', WorkerCommands.createWorkerDoctorCommand()],
|
|
41
43
|
['worker:summary', WorkerCommands.createWorkerSummaryCommand()],
|
|
42
44
|
];
|
|
43
45
|
};
|