@zintrust/workers 0.1.31 → 0.1.52

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.
Files changed (56) hide show
  1. package/dist/ClusterLock.js +3 -2
  2. package/dist/DeadLetterQueue.js +3 -2
  3. package/dist/HealthMonitor.js +24 -13
  4. package/dist/Observability.js +8 -0
  5. package/dist/WorkerFactory.d.ts +4 -0
  6. package/dist/WorkerFactory.js +409 -42
  7. package/dist/WorkerInit.js +122 -43
  8. package/dist/WorkerMetrics.js +5 -1
  9. package/dist/WorkerRegistry.js +8 -0
  10. package/dist/WorkerShutdown.d.ts +0 -13
  11. package/dist/WorkerShutdown.js +1 -44
  12. package/dist/build-manifest.json +101 -85
  13. package/dist/config/workerConfig.d.ts +1 -0
  14. package/dist/config/workerConfig.js +7 -1
  15. package/dist/createQueueWorker.js +281 -42
  16. package/dist/dashboard/workers-api.js +8 -1
  17. package/dist/http/WorkerController.js +90 -35
  18. package/dist/http/WorkerMonitoringService.js +29 -2
  19. package/dist/http/middleware/FeaturesValidator.js +5 -4
  20. package/dist/index.d.ts +1 -2
  21. package/dist/index.js +0 -1
  22. package/dist/routes/workers.js +10 -7
  23. package/dist/storage/WorkerStore.d.ts +6 -3
  24. package/dist/storage/WorkerStore.js +16 -0
  25. package/dist/telemetry/api/TelemetryMonitoringService.js +29 -2
  26. package/dist/ui/router/ui.js +58 -29
  27. package/dist/ui/workers/index.html +202 -0
  28. package/dist/ui/workers/main.js +1952 -0
  29. package/dist/ui/workers/styles.css +1350 -0
  30. package/dist/ui/workers/zintrust.svg +30 -0
  31. package/package.json +5 -5
  32. package/src/ClusterLock.ts +13 -7
  33. package/src/ComplianceManager.ts +3 -2
  34. package/src/DeadLetterQueue.ts +6 -4
  35. package/src/HealthMonitor.ts +33 -17
  36. package/src/Observability.ts +11 -0
  37. package/src/WorkerFactory.ts +480 -43
  38. package/src/WorkerInit.ts +167 -48
  39. package/src/WorkerMetrics.ts +14 -8
  40. package/src/WorkerRegistry.ts +11 -0
  41. package/src/WorkerShutdown.ts +1 -69
  42. package/src/config/workerConfig.ts +9 -1
  43. package/src/createQueueWorker.ts +428 -43
  44. package/src/dashboard/workers-api.ts +8 -1
  45. package/src/http/WorkerController.ts +111 -36
  46. package/src/http/WorkerMonitoringService.ts +35 -2
  47. package/src/http/middleware/FeaturesValidator.ts +8 -19
  48. package/src/index.ts +2 -3
  49. package/src/routes/workers.ts +10 -8
  50. package/src/storage/WorkerStore.ts +21 -3
  51. package/src/telemetry/api/TelemetryMonitoringService.ts +35 -2
  52. package/src/types/queue-monitor.d.ts +2 -1
  53. package/src/ui/components/WorkerExpandPanel.js +0 -8
  54. package/src/ui/router/EmbeddedAssets.ts +3 -0
  55. package/src/ui/router/ui.ts +57 -39
  56. package/src/WorkerShutdownDurableObject.ts +0 -64
@@ -131,8 +131,9 @@ export const ClusterLock = Object.freeze({
131
131
  Logger.warn('ClusterLock already initialized');
132
132
  return;
133
133
  }
134
- redisClient = createRedisConnection(config);
135
- startHeartbeat(redisClient);
134
+ const client = createRedisConnection(config);
135
+ redisClient = client;
136
+ startHeartbeat(client);
136
137
  Logger.info('ClusterLock initialized', { instanceId: getInstanceId() });
137
138
  },
138
139
  /**
@@ -4,12 +4,13 @@
4
4
  * Sealed namespace for immutability
5
5
  */
6
6
  import { ErrorFactory, Logger, createRedisConnection } from '@zintrust/core';
7
+ import { keyPrefix } from './config/workerConfig';
7
8
  // Redis key prefixes - using workers package prefix system
8
9
  const getDLQPrefix = () => {
9
- return 'worker:dlq:';
10
+ return keyPrefix() + ':dlq:';
10
11
  };
11
12
  const getAuditPrefix = () => {
12
- return 'worker:dlq:audit:';
13
+ return keyPrefix() + ':dlq:audit:';
13
14
  };
14
15
  // Internal state
15
16
  let redisClient = null;
@@ -24,7 +24,7 @@ const persistStatusChange = async (name, status, lastError) => {
24
24
  Logger.error(`Failed to persist status change for ${name}`, err);
25
25
  }
26
26
  };
27
- const verifyWorkerHealth = async (worker, _name, _queueName) => {
27
+ const verifyWorkerHealth = async (worker) => {
28
28
  // Check if isClosing exists (isClosing check safe for mocks)
29
29
  const workerAny = worker;
30
30
  const isClosingFn = workerAny['isClosing'];
@@ -40,9 +40,30 @@ const verifyWorkerHealth = async (worker, _name, _queueName) => {
40
40
  if (pingResult !== 'PONG') {
41
41
  throw ErrorFactory.createWorkerError(`Redis ping failed: ${pingResult}`);
42
42
  }
43
- Logger.debug(`Worker health verification passed for ${_name} ${_queueName}`);
44
43
  return true;
45
44
  };
45
+ const withTimeout = async (promise, timeoutMs, onTimeout) => {
46
+ let timeoutId = null;
47
+ return await new Promise((resolve, reject) => {
48
+ timeoutId = globalThis.setTimeout(() => {
49
+ if (timeoutId) {
50
+ clearTimeout(timeoutId);
51
+ timeoutId = null;
52
+ }
53
+ reject(onTimeout());
54
+ }, timeoutMs);
55
+ timeoutId.unref();
56
+ promise
57
+ .then(resolve)
58
+ .catch(reject)
59
+ .finally(() => {
60
+ if (timeoutId) {
61
+ clearTimeout(timeoutId);
62
+ timeoutId = null;
63
+ }
64
+ });
65
+ });
66
+ };
46
67
  const updateState = (state, isHealthy, errorMsg, latency) => {
47
68
  const now = new Date();
48
69
  state.lastCheck = now;
@@ -108,17 +129,7 @@ const performCheck = async (state) => {
108
129
  if (!state.worker) {
109
130
  throw ErrorFactory.createWorkerError('Worker instance not available');
110
131
  }
111
- isHealthy = await Promise.race([
112
- verifyWorkerHealth(state.worker, state.name, state.queueName || 'unknown'),
113
- new Promise((_, reject) => {
114
- // eslint-disable-next-line
115
- const id = setTimeout(() => {
116
- reject(ErrorFactory.createWorkerError('Health check timeout'));
117
- }, config.checkTimeoutMs);
118
- // Unref to prevent holding event loop if everything else finishes
119
- id.unref();
120
- }),
121
- ]);
132
+ isHealthy = await withTimeout(verifyWorkerHealth(state.worker), config.checkTimeoutMs, () => ErrorFactory.createWorkerError('Health check timeout'));
122
133
  }
123
134
  catch (err) {
124
135
  isHealthy = false;
@@ -16,6 +16,11 @@ const activeSpans = new Map();
16
16
  let spanSweepInterval = null;
17
17
  const MAX_ACTIVE_SPANS = 1000;
18
18
  const SPAN_TTL_MS = 5 * 60 * 1000;
19
+ const isUnrefableTimer = (value) => {
20
+ if (typeof value !== 'object' || value === null)
21
+ return false;
22
+ return 'unref' in value && typeof value.unref === 'function';
23
+ };
19
24
  const cleanupStaleSpans = () => {
20
25
  const now = Date.now();
21
26
  for (const [spanId, entry] of activeSpans.entries()) {
@@ -155,6 +160,9 @@ export const Observability = Object.freeze({
155
160
  spanSweepInterval = setInterval(() => {
156
161
  cleanupStaleSpans();
157
162
  }, SPAN_TTL_MS);
163
+ if (isUnrefableTimer(spanSweepInterval)) {
164
+ spanSweepInterval.unref();
165
+ }
158
166
  }
159
167
  Logger.info('Observability initialized', {
160
168
  prometheus: config.prometheus.enabled,
@@ -111,6 +111,10 @@ export declare const WorkerFactory: Readonly<{
111
111
  registerProcessorSpec: (spec: string, processor: WorkerFactoryConfig["processor"]) => void;
112
112
  resolveProcessorPath: (modulePath: string) => Promise<WorkerFactoryConfig["processor"] | undefined>;
113
113
  resolveProcessorSpec: (spec: string) => Promise<WorkerFactoryConfig["processor"] | undefined>;
114
+ /**
115
+ * Register a new worker configuration without starting it.
116
+ */
117
+ register(config: WorkerFactoryConfig): Promise<void>;
114
118
  /**
115
119
  * Create new worker with full setup
116
120
  */