@zintrust/workers 0.4.4 → 0.4.34

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.
@@ -191,6 +191,9 @@ async function getWorkersFromMixedPersistence(offset, limit, query) {
191
191
  ...transformToWorkerData(dbRecords, 'database'),
192
192
  ...transformToWorkerData(redisRecords, 'redis'),
193
193
  ];
194
+ if (workers.length === 0) {
195
+ return getWorkersFromFileFallback(limit, query.includeInactive === true);
196
+ }
194
197
  return {
195
198
  workers,
196
199
  total: dbRecords.length + redisRecords.length >= limit
@@ -216,6 +219,9 @@ async function getWorkersFromSinglePersistence(persistenceDriver, offset, limit,
216
219
  try {
217
220
  const normalizedDriver = normalizeDriver(persistenceDriver);
218
221
  const driverRecords = await WorkerFactory.listPersistedRecords({ driver: normalizedDriver }, { offset, limit, includeInactive: query.includeInactive });
222
+ if (driverRecords.length === 0) {
223
+ return getWorkersFromFileFallback(limit, query.includeInactive === true);
224
+ }
219
225
  const workers = transformToWorkerData(driverRecords, normalizedDriver);
220
226
  return {
221
227
  workers,
@@ -236,6 +242,31 @@ async function getWorkersFromSinglePersistence(persistenceDriver, offset, limit,
236
242
  };
237
243
  }
238
244
  }
245
+ async function getWorkersFromFileFallback(limit, includeInactive) {
246
+ try {
247
+ const discovered = await WorkerFactory.listFileBackedRecords();
248
+ const filtered = includeInactive
249
+ ? discovered
250
+ : discovered.filter((record) => record.activeStatus !== false);
251
+ return {
252
+ workers: transformToWorkerData(filtered, 'memory'),
253
+ total: filtered.length,
254
+ drivers: getAvailableDriversFromDrivers(['memory']),
255
+ effectiveLimit: limit,
256
+ prePaginated: false,
257
+ };
258
+ }
259
+ catch (error) {
260
+ Logger.debug('File-backed worker fallback failed', error);
261
+ return {
262
+ workers: [],
263
+ total: 0,
264
+ drivers: getAvailableDriversFromDrivers(['memory']),
265
+ effectiveLimit: limit,
266
+ prePaginated: false,
267
+ };
268
+ }
269
+ }
239
270
  const normalizeDriver = (driver) => {
240
271
  if (driver === 'db' || driver === 'database')
241
272
  return 'database';
@@ -402,18 +433,18 @@ async function getQueueData() {
402
433
  // Get queue statistics based on QUEUE_DRIVER
403
434
  switch (queueDriver) {
404
435
  case 'redis':
405
- return getRedisQueueData();
436
+ return await getRedisQueueData();
406
437
  case 'database':
407
- return getDatabaseQueueData();
438
+ return await getDatabaseQueueData();
408
439
  case 'db':
409
- return getDatabaseQueueData();
440
+ return await getDatabaseQueueData();
410
441
  default:
411
- return getMemoryQueueData();
442
+ return await getMemoryQueueData();
412
443
  }
413
444
  }
414
445
  catch (error) {
415
446
  Logger.error('Error fetching queue data:', error);
416
- return getMemoryQueueData();
447
+ return await getMemoryQueueData();
417
448
  }
418
449
  }
419
450
  async function getRedisQueueData() {
@@ -472,13 +503,13 @@ async function getDatabaseQueueData() {
472
503
  const { useEnsureDbConnected } = await import('@zintrust/core');
473
504
  const db = await useEnsureDbConnected();
474
505
  // Get queue statistics from actual database tables using proper query builder
475
- const queueStats = (await db
506
+ const queueStats = await db
476
507
  .table('queue_jobs')
477
508
  .select('COUNT(DISTINCT queue) as totalQueues')
478
509
  .selectAs('COUNT(*)', 'totalJobs')
479
510
  .selectAs('SUM(CASE WHEN reserved_at IS NOT NULL AND failed_at IS NULL THEN 1 ELSE 0 END)', 'processingJobs')
480
511
  .selectAs('SUM(CASE WHEN failed_at IS NOT NULL THEN 1 ELSE 0 END)', 'failedJobs')
481
- .first());
512
+ .first();
482
513
  const stats = queueStats || {
483
514
  totalQueues: 0,
484
515
  totalJobs: 0,
@@ -582,7 +613,8 @@ async function enrichWithDetails(workers) {
582
613
  async function buildWorkerDetails(worker) {
583
614
  try {
584
615
  const persistenceOverride = resolvePersistenceOverride(worker.driver);
585
- const persisted = await WorkerFactory.getPersisted(worker.name, persistenceOverride);
616
+ const persisted = (await WorkerFactory.getPersisted(worker.name, persistenceOverride)) ??
617
+ (await WorkerFactory.getFileBackedRecord(worker.name));
586
618
  const health = await getWorkerHealthSnapshot(worker.name, worker.health);
587
619
  const metrics = await getWorkerMetricsSnapshot(worker.name, worker);
588
620
  const configuration = buildWorkerConfiguration(worker, persisted);
@@ -730,6 +762,12 @@ export async function getWorkerDetails(name, driver) {
730
762
  worker = buildWorkerFromRecord(record, normalizedDriver);
731
763
  }
732
764
  }
765
+ if (!worker) {
766
+ const fileBacked = await WorkerFactory.getFileBackedRecord(name);
767
+ if (fileBacked) {
768
+ worker = buildWorkerFromRecord(fileBacked, 'memory');
769
+ }
770
+ }
733
771
  if (!worker) {
734
772
  throw ErrorFactory.createWorkerError(`Worker ${name} not found`);
735
773
  }
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export { MultiQueueWorker } from './MultiQueueWorker';
24
24
  export { WorkerVersioning } from './WorkerVersioning';
25
25
  export { WorkerFactory } from './WorkerFactory';
26
26
  export type { ProcessorResolver, WorkerFactoryConfig, WorkerPersistenceConfig, } from './WorkerFactory';
27
- export { WorkerInit } from './WorkerInit';
27
+ export { buildFileBackedAutoStartTasks, selectAutoStartNames, selectAutoStartTasks, WorkerInit, } from './WorkerInit';
28
28
  export { WorkerShutdown } from './WorkerShutdown';
29
29
  export { WorkerController } from './http/WorkerController';
30
30
  export { registerWorkerRoutes } from './routes/workers';
@@ -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, WorkerStatus, WorkerVersioningConfig, WorkersConfigOverrides, WorkersGlobalConfig, } from '@zintrust/core';
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/index.js CHANGED
@@ -31,7 +31,7 @@ export { MultiQueueWorker } from './MultiQueueWorker.js';
31
31
  export { WorkerVersioning } from './WorkerVersioning.js';
32
32
  // Factory & Lifecycle
33
33
  export { WorkerFactory } from './WorkerFactory.js';
34
- export { WorkerInit } from './WorkerInit.js';
34
+ export { buildFileBackedAutoStartTasks, selectAutoStartNames, selectAutoStartTasks, WorkerInit, } from './WorkerInit.js';
35
35
  export { WorkerShutdown } from './WorkerShutdown.js';
36
36
  // HTTP Controllers & Routes
37
37
  export { WorkerController } from './http/WorkerController.js';
@@ -1,9 +1,9 @@
1
+ type Registry = {
2
+ register: (id: string, provider: CliCommandProvider) => void;
3
+ };
1
4
  type CliCommandProvider = {
2
5
  getCommand: () => unknown;
3
6
  name?: string;
4
7
  };
5
- type Registry = {
6
- register: (id: string, provider: CliCommandProvider) => void;
7
- };
8
8
  export declare function registerWorkerCliCommands(registry: Registry): void;
9
9
  export {};
package/dist/register.js CHANGED
@@ -1,5 +1,11 @@
1
1
  const commandModule = (await (async () => {
2
- return (await import('@zintrust/core/cli'));
2
+ const workerCommandsSpecifier = '@zintrust/core/worker-commands';
3
+ try {
4
+ return (await import(workerCommandsSpecifier));
5
+ }
6
+ catch {
7
+ return (await import('@zintrust/core/cli'));
8
+ }
3
9
  })());
4
10
  const getWorkerProviders = () => {
5
11
  const { WorkerCommands } = commandModule;
@@ -27,9 +33,9 @@ registerWorkerCliCommands({
27
33
  },
28
34
  });
29
35
  try {
30
- const core = (await import('@zintrust/core/cli'));
31
- if (core.OptionalCliCommandRegistry !== undefined) {
32
- registerWorkerCliCommands(core.OptionalCliCommandRegistry);
36
+ const coreCli = (await import('@zintrust/core/cli'));
37
+ if (coreCli.OptionalCliCommandRegistry !== undefined) {
38
+ registerWorkerCliCommands(coreCli.OptionalCliCommandRegistry);
33
39
  }
34
40
  }
35
41
  catch {
@@ -0,0 +1,202 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>ZinTrust Workers Dashboard</title>
7
+ <link rel="stylesheet" href="workers/styles.css" />
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <div class="header">
12
+ <div class="header-top">
13
+ <div style="display: flex; align-items: center; gap: 16px">
14
+ <div class="logo-frame">
15
+ <img src="workers/zintrust.svg" alt="ZinTrust" class="logo-img" />
16
+ </div>
17
+ <h1>ZinTrust Workers</h1>
18
+ </div>
19
+ <div class="header-actions">
20
+ <button id="theme-toggle" class="theme-toggle">
21
+ <svg class="icon" viewBox="0 0 24 24">
22
+ <circle cx="12" cy="12" r="5" />
23
+ <path
24
+ d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"
25
+ />
26
+ </svg>
27
+ Theme
28
+ </button>
29
+ <button id="auto-refresh-toggle" class="btn" onclick="toggleAutoRefresh()">
30
+ <svg id="auto-refresh-icon" class="icon" viewBox="0 0 24 24">
31
+ <polygon points="5 3 19 12 5 21 5 3" />
32
+ </svg>
33
+ <span id="auto-refresh-label">Auto Refresh</span>
34
+ </button>
35
+ <button class="btn" onclick="fetchData()">
36
+ <svg class="icon" viewBox="0 0 24 24">
37
+ <path d="M23 4v6h-6" />
38
+ <path d="M1 20v-6h6" />
39
+ <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
40
+ </svg>
41
+ Refresh
42
+ </button>
43
+ <button class="btn btn-primary" onclick="showAddWorkerModal()">
44
+ <svg class="icon" viewBox="0 0 24 24">
45
+ <line x1="12" y1="5" x2="12" y2="19" />
46
+ <line x1="5" y1="12" x2="19" y2="12" />
47
+ </svg>
48
+ Add Worker
49
+ </button>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="nav-bar">
54
+ <nav class="nav-links">
55
+ <a href="/queue-monitor" class="nav-link">Queue Monitor</a>
56
+ <a href="/telemetry" class="nav-link">Telemetry</a>
57
+ <a href="/metrics" class="nav-link">Metrics</a>
58
+ </nav>
59
+ </div>
60
+
61
+ <div class="filters-bar">
62
+ <div class="filter-group">
63
+ <span>Status:</span>
64
+ <select id="status-filter">
65
+ <option value="">All Status</option>
66
+ <option value="running">Running</option>
67
+ <option value="stopped">Stopped</option>
68
+ <option value="error">Error</option>
69
+ <option value="paused">Paused</option>
70
+ </select>
71
+ </div>
72
+ <div class="filter-group">
73
+ <span>Driver:</span>
74
+ <select id="driver-filter">
75
+ <option value="">All Drivers</option>
76
+ </select>
77
+ </div>
78
+ <div class="filter-group">
79
+ <span>Sort:</span>
80
+ <select id="sort-select">
81
+ <option value="name">Sort by Name</option>
82
+ <option value="status" selected>Sort by Status</option>
83
+ <option value="driver">Sort by Driver</option>
84
+ <option value="health">Sort by Health</option>
85
+ <option value="version">Sort by Version</option>
86
+ <option value="processed">Sort by Performance</option>
87
+ </select>
88
+ </div>
89
+ <div style="flex-grow: 1"></div>
90
+ <div class="search-box">
91
+ <svg class="search-icon" viewBox="0 0 24 24">
92
+ <circle cx="11" cy="11" r="8"></circle>
93
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
94
+ </svg>
95
+ <input type="text" id="search-input" placeholder="Search workers..." />
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <div id="loading" style="text-align: center; padding: 40px; color: var(--muted)">
101
+ <div>Loading workers...</div>
102
+ </div>
103
+
104
+ <div
105
+ id="error"
106
+ style="display: none; text-align: center; padding: 40px; color: var(--danger)"
107
+ >
108
+ <div>Failed to load workers data</div>
109
+ <button class="btn" onclick="fetchData()" style="margin-top: 16px">Retry</button>
110
+ </div>
111
+
112
+ <div id="workers-content" style="display: none">
113
+ <div class="summary-bar" id="queue-summary">
114
+ <div class="summary-item">
115
+ <span class="summary-label">Queue Driver</span>
116
+ <span class="summary-value" id="queue-driver">-</span>
117
+ </div>
118
+ <div class="summary-item">
119
+ <span class="summary-label">Queues</span>
120
+ <span class="summary-value" id="queue-total">0</span>
121
+ </div>
122
+ <div class="summary-item">
123
+ <span class="summary-label">Jobs</span>
124
+ <span class="summary-value" id="queue-jobs">0</span>
125
+ </div>
126
+ <div class="summary-item">
127
+ <span class="summary-label">Processing</span>
128
+ <span class="summary-value" id="queue-processing">0</span>
129
+ </div>
130
+ <div class="summary-item">
131
+ <span class="summary-label">Failed</span>
132
+ <span class="summary-value" id="queue-failed">0</span>
133
+ </div>
134
+ <div class="summary-item">
135
+ <span class="summary-label">Drivers</span>
136
+ <div class="drivers-list" id="drivers-list"></div>
137
+ </div>
138
+ </div>
139
+ <div class="table-container">
140
+ <div class="table-wrapper">
141
+ <table>
142
+ <thead>
143
+ <tr>
144
+ <th style="width: 250px">Worker</th>
145
+ <th style="width: 120px">Status</th>
146
+ <th style="width: 120px">Health</th>
147
+ <th style="width: 100px">Driver</th>
148
+ <th style="width: 100px">Version</th>
149
+ <th style="width: 320px">Performance</th>
150
+ <th style="width: 180px">Actions</th>
151
+ </tr>
152
+ </thead>
153
+ <tbody id="workers-tbody">
154
+ <!-- Workers will be populated here -->
155
+ </tbody>
156
+ </table>
157
+ </div>
158
+
159
+ <div class="pagination">
160
+ <div class="pagination-info" id="pagination-info">Showing 0-0 of 0 workers</div>
161
+ <div class="pagination-controls">
162
+ <button class="page-btn" id="prev-btn" onclick="loadPage('prev')" disabled>
163
+ <svg
164
+ viewBox="0 0 24 24"
165
+ fill="none"
166
+ stroke="currentColor"
167
+ stroke-linecap="round"
168
+ stroke-linejoin="round"
169
+ >
170
+ <polyline points="15 18 9 12 15 6"></polyline>
171
+ </svg>
172
+ </button>
173
+ <div id="page-numbers" style="display: flex; gap: 8px"></div>
174
+ <button class="page-btn" id="next-btn" onclick="loadPage('next')" disabled>
175
+ <svg
176
+ viewBox="0 0 24 24"
177
+ fill="none"
178
+ stroke="currentColor"
179
+ stroke-linecap="round"
180
+ stroke-linejoin="round"
181
+ >
182
+ <polyline points="9 18 15 12 9 6"></polyline>
183
+ </svg>
184
+ </button>
185
+
186
+ <div class="page-size-selector">
187
+ <span>Show:</span>
188
+ <select id="limit-select" onchange="changeLimit(this.value)">
189
+ <option value="10">10</option>
190
+ <option value="25">25</option>
191
+ <option value="50">50</option>
192
+ <option value="100">100</option>
193
+ </select>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+ <script src="workers/main.js"></script>
201
+ </body>
202
+ </html>