@zintrust/workers 0.1.27

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 (178) hide show
  1. package/README.md +861 -0
  2. package/dist/AnomalyDetection.d.ts +102 -0
  3. package/dist/AnomalyDetection.js +321 -0
  4. package/dist/AutoScaler.d.ts +127 -0
  5. package/dist/AutoScaler.js +425 -0
  6. package/dist/BroadcastWorker.d.ts +21 -0
  7. package/dist/BroadcastWorker.js +24 -0
  8. package/dist/CanaryController.d.ts +103 -0
  9. package/dist/CanaryController.js +380 -0
  10. package/dist/ChaosEngineering.d.ts +79 -0
  11. package/dist/ChaosEngineering.js +216 -0
  12. package/dist/CircuitBreaker.d.ts +106 -0
  13. package/dist/CircuitBreaker.js +374 -0
  14. package/dist/ClusterLock.d.ts +90 -0
  15. package/dist/ClusterLock.js +385 -0
  16. package/dist/ComplianceManager.d.ts +177 -0
  17. package/dist/ComplianceManager.js +556 -0
  18. package/dist/DatacenterOrchestrator.d.ts +133 -0
  19. package/dist/DatacenterOrchestrator.js +404 -0
  20. package/dist/DeadLetterQueue.d.ts +122 -0
  21. package/dist/DeadLetterQueue.js +539 -0
  22. package/dist/HealthMonitor.d.ts +42 -0
  23. package/dist/HealthMonitor.js +301 -0
  24. package/dist/MultiQueueWorker.d.ts +89 -0
  25. package/dist/MultiQueueWorker.js +277 -0
  26. package/dist/NotificationWorker.d.ts +21 -0
  27. package/dist/NotificationWorker.js +23 -0
  28. package/dist/Observability.d.ts +153 -0
  29. package/dist/Observability.js +530 -0
  30. package/dist/PluginManager.d.ts +123 -0
  31. package/dist/PluginManager.js +392 -0
  32. package/dist/PriorityQueue.d.ts +117 -0
  33. package/dist/PriorityQueue.js +244 -0
  34. package/dist/ResourceMonitor.d.ts +164 -0
  35. package/dist/ResourceMonitor.js +605 -0
  36. package/dist/SLAMonitor.d.ts +110 -0
  37. package/dist/SLAMonitor.js +274 -0
  38. package/dist/WorkerFactory.d.ts +193 -0
  39. package/dist/WorkerFactory.js +1507 -0
  40. package/dist/WorkerInit.d.ts +85 -0
  41. package/dist/WorkerInit.js +223 -0
  42. package/dist/WorkerMetrics.d.ts +114 -0
  43. package/dist/WorkerMetrics.js +509 -0
  44. package/dist/WorkerRegistry.d.ts +145 -0
  45. package/dist/WorkerRegistry.js +319 -0
  46. package/dist/WorkerShutdown.d.ts +61 -0
  47. package/dist/WorkerShutdown.js +159 -0
  48. package/dist/WorkerVersioning.d.ts +107 -0
  49. package/dist/WorkerVersioning.js +300 -0
  50. package/dist/build-manifest.json +462 -0
  51. package/dist/config/workerConfig.d.ts +3 -0
  52. package/dist/config/workerConfig.js +19 -0
  53. package/dist/createQueueWorker.d.ts +23 -0
  54. package/dist/createQueueWorker.js +113 -0
  55. package/dist/dashboard/index.d.ts +1 -0
  56. package/dist/dashboard/index.js +1 -0
  57. package/dist/dashboard/types.d.ts +117 -0
  58. package/dist/dashboard/types.js +1 -0
  59. package/dist/dashboard/workers-api.d.ts +4 -0
  60. package/dist/dashboard/workers-api.js +638 -0
  61. package/dist/dashboard/workers-dashboard-ui.d.ts +3 -0
  62. package/dist/dashboard/workers-dashboard-ui.js +1026 -0
  63. package/dist/dashboard/workers-dashboard.d.ts +4 -0
  64. package/dist/dashboard/workers-dashboard.js +904 -0
  65. package/dist/helper/index.d.ts +5 -0
  66. package/dist/helper/index.js +10 -0
  67. package/dist/http/WorkerApiController.d.ts +38 -0
  68. package/dist/http/WorkerApiController.js +312 -0
  69. package/dist/http/WorkerController.d.ts +374 -0
  70. package/dist/http/WorkerController.js +1351 -0
  71. package/dist/http/middleware/CustomValidation.d.ts +92 -0
  72. package/dist/http/middleware/CustomValidation.js +270 -0
  73. package/dist/http/middleware/DatacenterValidator.d.ts +3 -0
  74. package/dist/http/middleware/DatacenterValidator.js +94 -0
  75. package/dist/http/middleware/EditWorkerValidation.d.ts +7 -0
  76. package/dist/http/middleware/EditWorkerValidation.js +55 -0
  77. package/dist/http/middleware/FeaturesValidator.d.ts +3 -0
  78. package/dist/http/middleware/FeaturesValidator.js +60 -0
  79. package/dist/http/middleware/InfrastructureValidator.d.ts +31 -0
  80. package/dist/http/middleware/InfrastructureValidator.js +226 -0
  81. package/dist/http/middleware/OptionsValidator.d.ts +3 -0
  82. package/dist/http/middleware/OptionsValidator.js +112 -0
  83. package/dist/http/middleware/PayloadSanitizer.d.ts +7 -0
  84. package/dist/http/middleware/PayloadSanitizer.js +42 -0
  85. package/dist/http/middleware/ProcessorPathSanitizer.d.ts +3 -0
  86. package/dist/http/middleware/ProcessorPathSanitizer.js +74 -0
  87. package/dist/http/middleware/QueueNameSanitizer.d.ts +3 -0
  88. package/dist/http/middleware/QueueNameSanitizer.js +45 -0
  89. package/dist/http/middleware/ValidateDriver.d.ts +7 -0
  90. package/dist/http/middleware/ValidateDriver.js +20 -0
  91. package/dist/http/middleware/VersionSanitizer.d.ts +3 -0
  92. package/dist/http/middleware/VersionSanitizer.js +25 -0
  93. package/dist/http/middleware/WorkerNameSanitizer.d.ts +3 -0
  94. package/dist/http/middleware/WorkerNameSanitizer.js +46 -0
  95. package/dist/http/middleware/WorkerValidationChain.d.ts +27 -0
  96. package/dist/http/middleware/WorkerValidationChain.js +185 -0
  97. package/dist/index.d.ts +46 -0
  98. package/dist/index.js +48 -0
  99. package/dist/routes/workers.d.ts +12 -0
  100. package/dist/routes/workers.js +81 -0
  101. package/dist/storage/WorkerStore.d.ts +45 -0
  102. package/dist/storage/WorkerStore.js +195 -0
  103. package/dist/type.d.ts +76 -0
  104. package/dist/type.js +1 -0
  105. package/dist/ui/router/ui.d.ts +3 -0
  106. package/dist/ui/router/ui.js +83 -0
  107. package/dist/ui/types/worker-ui.d.ts +229 -0
  108. package/dist/ui/types/worker-ui.js +5 -0
  109. package/package.json +53 -0
  110. package/src/AnomalyDetection.ts +434 -0
  111. package/src/AutoScaler.ts +654 -0
  112. package/src/BroadcastWorker.ts +34 -0
  113. package/src/CanaryController.ts +531 -0
  114. package/src/ChaosEngineering.ts +301 -0
  115. package/src/CircuitBreaker.ts +495 -0
  116. package/src/ClusterLock.ts +499 -0
  117. package/src/ComplianceManager.ts +815 -0
  118. package/src/DatacenterOrchestrator.ts +561 -0
  119. package/src/DeadLetterQueue.ts +733 -0
  120. package/src/HealthMonitor.ts +390 -0
  121. package/src/MultiQueueWorker.ts +431 -0
  122. package/src/NotificationWorker.ts +33 -0
  123. package/src/Observability.ts +696 -0
  124. package/src/PluginManager.ts +551 -0
  125. package/src/PriorityQueue.ts +351 -0
  126. package/src/ResourceMonitor.ts +769 -0
  127. package/src/SLAMonitor.ts +408 -0
  128. package/src/WorkerFactory.ts +2108 -0
  129. package/src/WorkerInit.ts +313 -0
  130. package/src/WorkerMetrics.ts +709 -0
  131. package/src/WorkerRegistry.ts +443 -0
  132. package/src/WorkerShutdown.ts +210 -0
  133. package/src/WorkerVersioning.ts +422 -0
  134. package/src/config/workerConfig.ts +25 -0
  135. package/src/createQueueWorker.ts +174 -0
  136. package/src/dashboard/index.ts +6 -0
  137. package/src/dashboard/types.ts +141 -0
  138. package/src/dashboard/workers-api.ts +785 -0
  139. package/src/dashboard/zintrust.svg +30 -0
  140. package/src/helper/index.ts +11 -0
  141. package/src/http/WorkerApiController.ts +369 -0
  142. package/src/http/WorkerController.ts +1512 -0
  143. package/src/http/middleware/CustomValidation.ts +360 -0
  144. package/src/http/middleware/DatacenterValidator.ts +124 -0
  145. package/src/http/middleware/EditWorkerValidation.ts +74 -0
  146. package/src/http/middleware/FeaturesValidator.ts +82 -0
  147. package/src/http/middleware/InfrastructureValidator.ts +295 -0
  148. package/src/http/middleware/OptionsValidator.ts +144 -0
  149. package/src/http/middleware/PayloadSanitizer.ts +52 -0
  150. package/src/http/middleware/ProcessorPathSanitizer.ts +86 -0
  151. package/src/http/middleware/QueueNameSanitizer.ts +55 -0
  152. package/src/http/middleware/ValidateDriver.ts +29 -0
  153. package/src/http/middleware/VersionSanitizer.ts +30 -0
  154. package/src/http/middleware/WorkerNameSanitizer.ts +56 -0
  155. package/src/http/middleware/WorkerValidationChain.ts +230 -0
  156. package/src/index.ts +98 -0
  157. package/src/routes/workers.ts +154 -0
  158. package/src/storage/WorkerStore.ts +240 -0
  159. package/src/type.ts +89 -0
  160. package/src/types/queue-monitor.d.ts +38 -0
  161. package/src/types/queue-redis.d.ts +38 -0
  162. package/src/ui/README.md +13 -0
  163. package/src/ui/components/JsonEditor.js +670 -0
  164. package/src/ui/components/JsonViewer.js +387 -0
  165. package/src/ui/components/WorkerCard.js +178 -0
  166. package/src/ui/components/WorkerExpandPanel.js +257 -0
  167. package/src/ui/components/fetcher.js +42 -0
  168. package/src/ui/components/sla-scorecard.js +32 -0
  169. package/src/ui/components/styles.css +30 -0
  170. package/src/ui/components/table-expander.js +34 -0
  171. package/src/ui/integration/worker-ui-integration.js +565 -0
  172. package/src/ui/router/ui.ts +99 -0
  173. package/src/ui/services/workerApi.js +240 -0
  174. package/src/ui/types/worker-ui.ts +283 -0
  175. package/src/ui/utils/jsonValidator.js +444 -0
  176. package/src/ui/workers/index.html +202 -0
  177. package/src/ui/workers/main.js +1781 -0
  178. package/src/ui/workers/styles.css +1350 -0
@@ -0,0 +1,444 @@
1
+ /* eslint-disable @typescript-eslint/explicit-function-return-type */
2
+ /**
3
+ * JSON Validation Utilities
4
+ * Client-side validation using backend schemas
5
+ */
6
+
7
+ /**
8
+ * Validation result interface
9
+ */
10
+ const createValidationResult = (isValid, errors = [], warnings = []) => ({
11
+ isValid,
12
+ errors: [...errors],
13
+ warnings: [...warnings],
14
+ });
15
+
16
+ /**
17
+ * Worker validation schemas (mirroring backend)
18
+ */
19
+ const WorkerValidationSchemas = {
20
+ create: {
21
+ required: ['name', 'queueName', 'processor', 'version'],
22
+ fields: {
23
+ name: {
24
+ type: 'string',
25
+ minLength: 3,
26
+ maxLength: 50,
27
+ pattern: /^[a-zA-Z0-9_-]+$/,
28
+ },
29
+ queueName: {
30
+ type: 'string',
31
+ minLength: 3,
32
+ maxLength: 50,
33
+ pattern: /^[a-zA-Z0-9_-]+$/,
34
+ },
35
+ processor: {
36
+ type: 'string',
37
+ minLength: 1,
38
+ maxLength: 255,
39
+ },
40
+ version: {
41
+ type: 'string',
42
+ pattern: /^\d+\.\d+\.\d+$/,
43
+ },
44
+ driver: {
45
+ type: 'string',
46
+ allowedValues: ['database', 'redis', 'memory'],
47
+ optional: true,
48
+ },
49
+ concurrency: {
50
+ type: 'number',
51
+ min: 1,
52
+ max: 1000,
53
+ optional: true,
54
+ },
55
+ autoStart: {
56
+ type: 'boolean',
57
+ optional: true,
58
+ },
59
+ infrastructure: {
60
+ type: 'object',
61
+ optional: true,
62
+ fields: {
63
+ driver: {
64
+ type: 'string',
65
+ allowedValues: ['database', 'redis', 'memory'],
66
+ },
67
+ persistence: {
68
+ type: 'object',
69
+ optional: true,
70
+ fields: {
71
+ driver: {
72
+ type: 'string',
73
+ allowedValues: ['memory', 'redis', 'database'],
74
+ },
75
+ },
76
+ },
77
+ deadLetterQueue: {
78
+ type: 'object',
79
+ optional: true,
80
+ fields: {
81
+ enabled: {
82
+ type: 'boolean',
83
+ },
84
+ maxRetries: {
85
+ type: 'number',
86
+ min: 0,
87
+ max: 10,
88
+ },
89
+ },
90
+ },
91
+ autoScaler: {
92
+ type: 'object',
93
+ optional: true,
94
+ fields: {
95
+ enabled: {
96
+ type: 'boolean',
97
+ },
98
+ minWorkers: {
99
+ type: 'number',
100
+ min: 0,
101
+ integer: true,
102
+ },
103
+ maxWorkers: {
104
+ type: 'number',
105
+ min: 1,
106
+ integer: true,
107
+ },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ features: {
113
+ type: 'object',
114
+ optional: true,
115
+ fields: {
116
+ clustering: { type: 'boolean' },
117
+ metrics: { type: 'boolean' },
118
+ autoScaling: { type: 'boolean' },
119
+ circuitBreaker: { type: 'boolean' },
120
+ deadLetterQueue: { type: 'boolean' },
121
+ resourceMonitoring: { type: 'boolean' },
122
+ compliance: { type: 'boolean' },
123
+ observability: { type: 'boolean' },
124
+ plugins: { type: 'boolean' },
125
+ versioning: { type: 'boolean' },
126
+ datacenterOrchestration: { type: 'boolean' },
127
+ },
128
+ },
129
+ datacenter: {
130
+ type: 'object',
131
+ optional: true,
132
+ fields: {
133
+ primaryRegion: {
134
+ type: 'string',
135
+ minLength: 3,
136
+ maxLength: 20,
137
+ pattern: /^[a-z0-9-]+$/,
138
+ },
139
+ secondaryRegions: {
140
+ type: 'array',
141
+ optional: true,
142
+ itemType: 'string',
143
+ },
144
+ affinityRules: {
145
+ type: 'object',
146
+ optional: true,
147
+ fields: {
148
+ preferLocal: { type: 'boolean' },
149
+ maxLatency: { type: 'number', min: 0 },
150
+ avoidRegions: {
151
+ type: 'array',
152
+ optional: true,
153
+ itemType: 'string',
154
+ },
155
+ },
156
+ },
157
+ },
158
+ },
159
+ },
160
+ },
161
+ };
162
+
163
+ /**
164
+ * Validate string field against schema constraints
165
+ */
166
+ const validateStringField = (value, fieldSchema, fieldName, path, errors) => {
167
+ if (fieldSchema.minLength && value.length < fieldSchema.minLength) {
168
+ errors.push({
169
+ path: path || fieldName,
170
+ message: `${fieldName} must be at least ${fieldSchema.minLength} characters long`,
171
+ code: 'STRING_TOO_SHORT',
172
+ });
173
+ }
174
+
175
+ if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) {
176
+ errors.push({
177
+ path: path || fieldName,
178
+ message: `${fieldName} must be at most ${fieldSchema.maxLength} characters long`,
179
+ code: 'STRING_TOO_LONG',
180
+ });
181
+ }
182
+
183
+ if (fieldSchema.pattern && !fieldSchema.pattern.test(value)) {
184
+ errors.push({
185
+ path: path || fieldName,
186
+ message: `${fieldName} format is invalid`,
187
+ code: 'INVALID_FORMAT',
188
+ });
189
+ }
190
+ };
191
+
192
+ /**
193
+ * Validate number field against schema constraints
194
+ */
195
+ const validateNumberField = (value, fieldSchema, fieldName, path, errors) => {
196
+ if (fieldSchema.min !== undefined && value < fieldSchema.min) {
197
+ errors.push({
198
+ path: path || fieldName,
199
+ message: `${fieldName} must be at least ${fieldSchema.min}`,
200
+ code: 'NUMBER_TOO_SMALL',
201
+ });
202
+ }
203
+
204
+ if (fieldSchema.max !== undefined && value > fieldSchema.max) {
205
+ errors.push({
206
+ path: path || fieldName,
207
+ message: `${fieldName} must be at most ${fieldSchema.max}`,
208
+ code: 'NUMBER_TOO_LARGE',
209
+ });
210
+ }
211
+
212
+ if (fieldSchema.integer && !Number.isInteger(value)) {
213
+ errors.push({
214
+ path: path || fieldName,
215
+ message: `${fieldName} must be a whole number (integer)`,
216
+ code: 'NOT_INTEGER',
217
+ });
218
+ }
219
+ };
220
+
221
+ /**
222
+ * Validate array field against schema constraints
223
+ */
224
+ const validateArrayField = (value, fieldSchema, fieldName, path, errors, warnings) => {
225
+ if (fieldSchema.itemType) {
226
+ value.forEach((item, index) => {
227
+ const itemResult = validateField(
228
+ item,
229
+ { type: fieldSchema.itemType },
230
+ `${fieldName}[${index}]`,
231
+ `${path || fieldName}[${index}]`
232
+ );
233
+ if (!itemResult.isValid) {
234
+ errors.push(...itemResult.errors);
235
+ }
236
+ warnings.push(...itemResult.warnings);
237
+ });
238
+ }
239
+ };
240
+
241
+ /**
242
+ * Validate object field against schema constraints
243
+ */
244
+ const validateObjectField = (value, fieldSchema, fieldName, path, errors, warnings) => {
245
+ if (fieldSchema.fields) {
246
+ Object.keys(fieldSchema.fields).forEach((subFieldName) => {
247
+ const subFieldSchema = fieldSchema.fields[subFieldName];
248
+ const subPath = path ? `${path}.${subFieldName}` : subFieldName;
249
+ const subResult = validateField(value[subFieldName], subFieldSchema, subFieldName, subPath);
250
+ if (!subResult.isValid) {
251
+ errors.push(...subResult.errors);
252
+ }
253
+ warnings.push(...subResult.warnings);
254
+ });
255
+ }
256
+ };
257
+
258
+ /**
259
+ * Check if field meets basic requirements (required/optional)
260
+ */
261
+ const validateBasicRequirements = (value, fieldSchema, fieldName, path) => {
262
+ const errors = [];
263
+ const warnings = [];
264
+
265
+ // Check if field is required and missing
266
+ if (fieldSchema.required && (value === undefined || value === null)) {
267
+ errors.push({
268
+ path: path || fieldName,
269
+ message: `${fieldName} is required`,
270
+ code: 'REQUIRED_FIELD',
271
+ });
272
+ return { shouldStop: true, result: createValidationResult(false, errors, warnings) };
273
+ }
274
+
275
+ // Skip validation if field is optional and not provided
276
+ if (fieldSchema.optional && (value === undefined || value === null)) {
277
+ return { shouldStop: true, result: createValidationResult(true, errors, warnings) };
278
+ }
279
+
280
+ return { shouldStop: false, result: null };
281
+ };
282
+
283
+ /**
284
+ * Validate field type
285
+ */
286
+ const validateFieldType = (value, fieldSchema, fieldName, path) => {
287
+ const errors = [];
288
+ const warnings = [];
289
+
290
+ if (fieldSchema.type) {
291
+ const expectedType = fieldSchema.type;
292
+ const actualType = Array.isArray(value) ? 'array' : typeof value;
293
+
294
+ if (actualType !== expectedType) {
295
+ errors.push({
296
+ path: path || fieldName,
297
+ message: `${fieldName} must be of type ${expectedType}, got ${actualType}`,
298
+ code: 'INVALID_TYPE',
299
+ });
300
+ return { isValid: false, errors, warnings };
301
+ }
302
+ }
303
+
304
+ return { isValid: true, errors, warnings };
305
+ };
306
+
307
+ /**
308
+ * Perform type-specific validations
309
+ */
310
+ const performTypeSpecificValidations = (value, fieldSchema, fieldName, path, errors, warnings) => {
311
+ if (typeof value === 'string') {
312
+ validateStringField(value, fieldSchema, fieldName, path, errors);
313
+ } else if (typeof value === 'number') {
314
+ validateNumberField(value, fieldSchema, fieldName, path, errors);
315
+ }
316
+
317
+ // Allowed values validation
318
+ if (fieldSchema.allowedValues && !fieldSchema.allowedValues.includes(value)) {
319
+ errors.push({
320
+ path: path || fieldName,
321
+ message: `${fieldName} must be one of: ${fieldSchema.allowedValues.join(', ')}`,
322
+ code: 'INVALID_VALUE',
323
+ });
324
+ }
325
+
326
+ // Array validation
327
+ if (Array.isArray(value)) {
328
+ validateArrayField(value, fieldSchema, fieldName, path, errors, warnings);
329
+ }
330
+
331
+ // Object validation
332
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
333
+ validateObjectField(value, fieldSchema, fieldName, path, errors, warnings);
334
+ }
335
+ };
336
+
337
+ /**
338
+ * Validate a single field against schema
339
+ */
340
+ const validateField = (value, fieldSchema, fieldName, path = '') => {
341
+ const errors = [];
342
+ const warnings = [];
343
+
344
+ // Check basic requirements (required/optional)
345
+ const basicCheck = validateBasicRequirements(value, fieldSchema, fieldName, path);
346
+ if (basicCheck.shouldStop) {
347
+ return basicCheck.result;
348
+ }
349
+
350
+ // Validate field type
351
+ const typeCheck = validateFieldType(value, fieldSchema, fieldName, path);
352
+ if (!typeCheck.isValid) {
353
+ return createValidationResult(false, typeCheck.errors, typeCheck.warnings);
354
+ }
355
+
356
+ // Perform type-specific validations
357
+ performTypeSpecificValidations(value, fieldSchema, fieldName, path, errors, warnings);
358
+
359
+ return createValidationResult(errors.length === 0, errors, warnings);
360
+ };
361
+
362
+ /**
363
+ * Validate worker data against schema
364
+ */
365
+ const validateWorkerData = (data, schemaName = 'create') => {
366
+ const schema = WorkerValidationSchemas[schemaName];
367
+ if (!schema) {
368
+ return createValidationResult(
369
+ false,
370
+ [],
371
+ [
372
+ {
373
+ path: 'schema',
374
+ message: `Unknown validation schema: ${schemaName}`,
375
+ code: 'UNKNOWN_SCHEMA',
376
+ },
377
+ ]
378
+ );
379
+ }
380
+
381
+ const errors = [];
382
+ const warnings = [];
383
+
384
+ // Check required fields
385
+ schema.required.forEach((fieldName) => {
386
+ if (!(fieldName in data)) {
387
+ errors.push({
388
+ path: fieldName,
389
+ message: `${fieldName} is required`,
390
+ code: 'REQUIRED_FIELD',
391
+ });
392
+ }
393
+ });
394
+
395
+ // Validate all fields
396
+ Object.keys(data).forEach((fieldName) => {
397
+ const fieldSchema = schema.fields[fieldName];
398
+ if (fieldSchema) {
399
+ const result = validateField(data[fieldName], fieldSchema, fieldName);
400
+ if (!result.isValid) {
401
+ errors.push(...result.errors);
402
+ }
403
+ warnings.push(...result.warnings);
404
+ } else {
405
+ warnings.push({
406
+ path: fieldName,
407
+ message: `Unknown field: ${fieldName}`,
408
+ code: 'UNKNOWN_FIELD',
409
+ });
410
+ }
411
+ });
412
+
413
+ return createValidationResult(errors.length === 0, errors, warnings);
414
+ };
415
+
416
+ /**
417
+ * Validate JSON string
418
+ */
419
+ const validateJsonString = (jsonString) => {
420
+ try {
421
+ const data = JSON.parse(jsonString);
422
+ return {
423
+ isValid: true,
424
+ data,
425
+ error: null,
426
+ };
427
+ } catch (error) {
428
+ return {
429
+ isValid: false,
430
+ data: null,
431
+ error: error.message,
432
+ };
433
+ }
434
+ };
435
+
436
+ /**
437
+ * Sealed namespace for JSON validation utilities
438
+ */
439
+ export const JsonValidator = Object.freeze({
440
+ validateWorkerData,
441
+ validateJsonString,
442
+ schemas: WorkerValidationSchemas,
443
+ createValidationResult,
444
+ });
@@ -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="/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">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>