@zintrust/workers 0.1.28 → 0.1.30

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 (111) hide show
  1. package/README.md +16 -1
  2. package/dist/AnomalyDetection.d.ts +4 -0
  3. package/dist/AnomalyDetection.js +8 -0
  4. package/dist/BroadcastWorker.d.ts +2 -0
  5. package/dist/CanaryController.js +49 -5
  6. package/dist/ChaosEngineering.js +13 -0
  7. package/dist/ClusterLock.js +21 -10
  8. package/dist/DeadLetterQueue.js +12 -8
  9. package/dist/MultiQueueWorker.d.ts +1 -1
  10. package/dist/MultiQueueWorker.js +12 -7
  11. package/dist/NotificationWorker.d.ts +2 -0
  12. package/dist/PriorityQueue.d.ts +2 -2
  13. package/dist/PriorityQueue.js +20 -21
  14. package/dist/ResourceMonitor.js +65 -38
  15. package/dist/WorkerFactory.d.ts +23 -3
  16. package/dist/WorkerFactory.js +420 -40
  17. package/dist/WorkerInit.js +8 -3
  18. package/dist/WorkerMetrics.d.ts +2 -1
  19. package/dist/WorkerMetrics.js +152 -93
  20. package/dist/WorkerRegistry.d.ts +6 -0
  21. package/dist/WorkerRegistry.js +70 -1
  22. package/dist/WorkerShutdown.d.ts +21 -0
  23. package/dist/WorkerShutdown.js +82 -9
  24. package/dist/WorkerShutdownDurableObject.d.ts +12 -0
  25. package/dist/WorkerShutdownDurableObject.js +41 -0
  26. package/dist/build-manifest.json +171 -99
  27. package/dist/createQueueWorker.d.ts +2 -0
  28. package/dist/createQueueWorker.js +42 -27
  29. package/dist/dashboard/types.d.ts +5 -0
  30. package/dist/dashboard/workers-api.js +136 -43
  31. package/dist/http/WorkerApiController.js +1 -0
  32. package/dist/http/WorkerController.js +133 -85
  33. package/dist/http/WorkerMonitoringService.d.ts +11 -0
  34. package/dist/http/WorkerMonitoringService.js +62 -0
  35. package/dist/http/middleware/CustomValidation.js +1 -1
  36. package/dist/http/middleware/EditWorkerValidation.d.ts +1 -1
  37. package/dist/http/middleware/EditWorkerValidation.js +7 -6
  38. package/dist/http/middleware/ProcessorPathSanitizer.js +101 -35
  39. package/dist/http/middleware/WorkerValidationChain.js +1 -0
  40. package/dist/index.d.ts +2 -1
  41. package/dist/index.js +1 -0
  42. package/dist/routes/workers.js +48 -6
  43. package/dist/storage/WorkerStore.d.ts +4 -1
  44. package/dist/storage/WorkerStore.js +55 -7
  45. package/dist/telemetry/api/TelemetryAPI.d.ts +46 -0
  46. package/dist/telemetry/api/TelemetryAPI.js +219 -0
  47. package/dist/telemetry/api/TelemetryMonitoringService.d.ts +17 -0
  48. package/dist/telemetry/api/TelemetryMonitoringService.js +113 -0
  49. package/dist/telemetry/components/AlertPanel.d.ts +1 -0
  50. package/dist/telemetry/components/AlertPanel.js +13 -0
  51. package/dist/telemetry/components/CostTracking.d.ts +1 -0
  52. package/dist/telemetry/components/CostTracking.js +14 -0
  53. package/dist/telemetry/components/ResourceUsageChart.d.ts +1 -0
  54. package/dist/telemetry/components/ResourceUsageChart.js +11 -0
  55. package/dist/telemetry/components/WorkerHealthChart.d.ts +1 -0
  56. package/dist/telemetry/components/WorkerHealthChart.js +11 -0
  57. package/dist/telemetry/index.d.ts +15 -0
  58. package/dist/telemetry/index.js +60 -0
  59. package/dist/telemetry/routes/dashboard.d.ts +6 -0
  60. package/dist/telemetry/routes/dashboard.js +608 -0
  61. package/dist/ui/router/EmbeddedAssets.d.ts +4 -0
  62. package/dist/ui/router/EmbeddedAssets.js +13 -0
  63. package/dist/ui/router/ui.js +100 -4
  64. package/package.json +9 -5
  65. package/src/AnomalyDetection.ts +9 -0
  66. package/src/CanaryController.ts +41 -5
  67. package/src/ChaosEngineering.ts +14 -0
  68. package/src/ClusterLock.ts +22 -9
  69. package/src/DeadLetterQueue.ts +13 -8
  70. package/src/MultiQueueWorker.ts +15 -8
  71. package/src/PriorityQueue.ts +21 -22
  72. package/src/ResourceMonitor.ts +72 -40
  73. package/src/WorkerFactory.ts +545 -49
  74. package/src/WorkerInit.ts +8 -3
  75. package/src/WorkerMetrics.ts +183 -105
  76. package/src/WorkerRegistry.ts +80 -1
  77. package/src/WorkerShutdown.ts +115 -9
  78. package/src/WorkerShutdownDurableObject.ts +64 -0
  79. package/src/createQueueWorker.ts +73 -30
  80. package/src/dashboard/types.ts +5 -0
  81. package/src/dashboard/workers-api.ts +165 -52
  82. package/src/http/WorkerApiController.ts +1 -0
  83. package/src/http/WorkerController.ts +167 -90
  84. package/src/http/WorkerMonitoringService.ts +77 -0
  85. package/src/http/middleware/CustomValidation.ts +1 -1
  86. package/src/http/middleware/EditWorkerValidation.ts +7 -6
  87. package/src/http/middleware/ProcessorPathSanitizer.ts +123 -36
  88. package/src/http/middleware/WorkerValidationChain.ts +1 -0
  89. package/src/index.ts +6 -1
  90. package/src/routes/workers.ts +66 -9
  91. package/src/storage/WorkerStore.ts +59 -9
  92. package/src/telemetry/api/TelemetryAPI.ts +292 -0
  93. package/src/telemetry/api/TelemetryMonitoringService.ts +149 -0
  94. package/src/telemetry/components/AlertPanel.ts +13 -0
  95. package/src/telemetry/components/CostTracking.ts +14 -0
  96. package/src/telemetry/components/ResourceUsageChart.ts +11 -0
  97. package/src/telemetry/components/WorkerHealthChart.ts +11 -0
  98. package/src/telemetry/index.ts +121 -0
  99. package/src/telemetry/public/assets/zintrust-logo.svg +15 -0
  100. package/src/telemetry/routes/dashboard.ts +638 -0
  101. package/src/telemetry/styles/tailwind.css +1 -0
  102. package/src/telemetry/styles/zintrust-theme.css +8 -0
  103. package/src/ui/router/EmbeddedAssets.ts +13 -0
  104. package/src/ui/router/ui.ts +112 -5
  105. package/src/ui/workers/index.html +2 -2
  106. package/src/ui/workers/main.js +232 -61
  107. package/src/ui/workers/zintrust.svg +30 -0
  108. package/dist/dashboard/workers-dashboard-ui.d.ts +0 -3
  109. package/dist/dashboard/workers-dashboard-ui.js +0 -1026
  110. package/dist/dashboard/workers-dashboard.d.ts +0 -4
  111. package/dist/dashboard/workers-dashboard.js +0 -904
@@ -0,0 +1,219 @@
1
+ import { Logger } from '@zintrust/core';
2
+ import { getWorkers } from '../../dashboard/workers-api';
3
+ import { HealthMonitor } from '../../HealthMonitor';
4
+ import { ResourceMonitor } from '../../ResourceMonitor';
5
+ const isOkWithUsage = (value) => value.ok === true && 'usage' in value;
6
+ const isOkWithSummary = (value) => value.ok === true && 'summary' in value;
7
+ // Helper function to create stopped worker alert
8
+ const createStoppedWorkerAlert = (worker) => ({
9
+ type: 'worker-stopped',
10
+ severity: 'warning',
11
+ message: `Worker ${worker.name} is stopped`,
12
+ timestamp: worker.health?.lastCheck || new Date().toISOString(),
13
+ });
14
+ // Helper function to create health check alert
15
+ const createHealthCheckAlert = (worker) => {
16
+ const check = worker.health?.checks?.[0];
17
+ if (!check)
18
+ return null;
19
+ return {
20
+ type: 'health-check-failed',
21
+ severity: check.status === 'fail' ? 'critical' : 'warning',
22
+ message: check.message || `Health check failed: ${check.name}`,
23
+ timestamp: worker.health?.lastCheck || new Date().toISOString(),
24
+ };
25
+ };
26
+ // Helper function to generate worker alerts
27
+ const generateWorkerAlerts = (workers) => {
28
+ return workers
29
+ .filter((w) => {
30
+ return (w.status !== 'running' ||
31
+ w.health?.status !== 'healthy' ||
32
+ (w.health?.checks && w.health.checks.length > 0));
33
+ })
34
+ .map((w) => {
35
+ const workerData = {
36
+ workerName: w.name,
37
+ status: w.status,
38
+ healthStatus: w.health?.status || 'unknown',
39
+ lastCheck: w.health?.lastCheck || new Date().toISOString(),
40
+ checks: w.health?.checks || [],
41
+ };
42
+ let alert = null;
43
+ if (w.status === 'stopped') {
44
+ alert = createStoppedWorkerAlert(w);
45
+ }
46
+ else if (w.health?.checks && w.health.checks.length > 0) {
47
+ alert = createHealthCheckAlert(w);
48
+ }
49
+ return {
50
+ ...workerData,
51
+ alert,
52
+ };
53
+ })
54
+ .filter((w) => w.alert !== null);
55
+ };
56
+ // Helper function to generate resource alerts
57
+ const generateResourceAlerts = (resourceUsage) => {
58
+ const resourceAlerts = [];
59
+ const cpuUsage = resourceUsage.resourceSnapshot?.cpu?.usage || 0;
60
+ const memoryUsage = resourceUsage.resourceSnapshot?.memory?.usage || 0;
61
+ if (cpuUsage > 90) {
62
+ resourceAlerts.push({
63
+ type: 'cpu-high',
64
+ severity: 'critical',
65
+ message: `Critical CPU usage: ${cpuUsage.toFixed(1)}%`,
66
+ timestamp: new Date().toISOString(),
67
+ recommendation: 'Consider scaling up or optimizing worker code',
68
+ });
69
+ }
70
+ else if (cpuUsage > 80) {
71
+ resourceAlerts.push({
72
+ type: 'cpu-high',
73
+ severity: 'warning',
74
+ message: `High CPU usage: ${cpuUsage.toFixed(1)}%`,
75
+ timestamp: new Date().toISOString(),
76
+ recommendation: 'Monitor closely and consider scaling',
77
+ });
78
+ }
79
+ if (memoryUsage > 95) {
80
+ resourceAlerts.push({
81
+ type: 'memory-high',
82
+ severity: 'critical',
83
+ message: `Critical memory usage: ${memoryUsage.toFixed(1)}%`,
84
+ timestamp: new Date().toISOString(),
85
+ recommendation: 'Increase memory allocation or optimize memory usage',
86
+ });
87
+ }
88
+ else if (memoryUsage > 85) {
89
+ resourceAlerts.push({
90
+ type: 'memory-high',
91
+ severity: 'warning',
92
+ message: `High memory usage: ${memoryUsage.toFixed(1)}%`,
93
+ timestamp: new Date().toISOString(),
94
+ recommendation: 'Monitor memory usage closely',
95
+ });
96
+ }
97
+ return resourceAlerts;
98
+ };
99
+ // Helper function to calculate monitoring summary
100
+ const calculateMonitoringSummary = (runningWorkers) => {
101
+ const healthyCount = runningWorkers.filter((w) => w.health?.status === 'healthy').length;
102
+ const degradedCount = runningWorkers.filter((w) => w.health?.status === 'warning').length;
103
+ const criticalCount = runningWorkers.filter((w) => w.health?.status === 'unhealthy').length;
104
+ return {
105
+ total: runningWorkers.length,
106
+ healthy: healthyCount,
107
+ degraded: degradedCount,
108
+ critical: criticalCount,
109
+ details: runningWorkers.map((w) => ({
110
+ workerName: w.name,
111
+ status: w.health?.status || 'unknown',
112
+ lastCheck: w.health?.lastCheck || new Date().toISOString(),
113
+ checks: w.health?.checks || [],
114
+ })),
115
+ };
116
+ };
117
+ export const TelemetryAPI = Object.freeze({
118
+ async getSystemSummary() {
119
+ try {
120
+ // Get all workers (both running and stopped) for complete alert visibility
121
+ const workersResult = await getWorkers({});
122
+ const runningWorkers = workersResult.workers.filter((w) => w.status === 'running');
123
+ // Calculate monitoring summary from running workers only
124
+ const monitoringSummary = calculateMonitoringSummary(runningWorkers);
125
+ // Generate alerts from workers and resources
126
+ const workerAlerts = generateWorkerAlerts(workersResult.workers);
127
+ const resourceUsage = ResourceMonitor.getCurrentUsage('system');
128
+ const resourceAlerts = generateResourceAlerts(resourceUsage);
129
+ // Combine all alerts
130
+ const allAlerts = [...workerAlerts, ...resourceAlerts];
131
+ return {
132
+ ok: true,
133
+ summary: {
134
+ workers: runningWorkers.length,
135
+ monitoring: {
136
+ ...monitoringSummary,
137
+ alerts: allAlerts, // Include all alerts (workers + resources)
138
+ },
139
+ resources: resourceUsage,
140
+ alerts: allAlerts, // Top-level alerts for easy access
141
+ },
142
+ };
143
+ }
144
+ catch (error) {
145
+ Logger.error('Failed to get system summary', error);
146
+ return {
147
+ ok: false,
148
+ error: error instanceof Error ? error.message : 'Unknown error',
149
+ summary: {},
150
+ };
151
+ }
152
+ },
153
+ async getMonitoringSummary() {
154
+ try {
155
+ const summary = await HealthMonitor.getSummary();
156
+ return { ok: true, summary };
157
+ }
158
+ catch (error) {
159
+ Logger.error('Failed to get monitoring summary', error);
160
+ return {
161
+ ok: false,
162
+ error: error instanceof Error ? error.message : 'Unknown error',
163
+ summary: {},
164
+ };
165
+ }
166
+ },
167
+ async getResourceCurrent() {
168
+ try {
169
+ const usage = ResourceMonitor.getCurrentUsage('system');
170
+ return { ok: true, usage };
171
+ }
172
+ catch (error) {
173
+ Logger.error('Failed to get resource usage', error);
174
+ return {
175
+ ok: false,
176
+ error: error instanceof Error ? error.message : 'Unknown error',
177
+ usage: null,
178
+ };
179
+ }
180
+ },
181
+ async getResourceTrends() {
182
+ try {
183
+ const trends = ResourceMonitor.getAllTrends('system', 'day');
184
+ return { ok: true, trends };
185
+ }
186
+ catch (error) {
187
+ Logger.error('Failed to get resource trends', error);
188
+ return {
189
+ ok: false,
190
+ error: error instanceof Error ? error.message : 'Unknown error',
191
+ trends: null,
192
+ };
193
+ }
194
+ },
195
+ });
196
+ export function createSnapshotBuilder() {
197
+ return async () => {
198
+ const [systemSummaryResult, resourceCurrentResult] = await Promise.allSettled([
199
+ TelemetryAPI.getSystemSummary(),
200
+ TelemetryAPI.getResourceCurrent(),
201
+ ]);
202
+ if (systemSummaryResult.status === 'rejected') {
203
+ Logger.error('Telemetry dashboard summary failed', systemSummaryResult.reason);
204
+ }
205
+ if (resourceCurrentResult.status === 'rejected') {
206
+ Logger.error('Telemetry resource summary failed', resourceCurrentResult.reason);
207
+ }
208
+ const systemSummary = systemSummaryResult.status === 'fulfilled' ? systemSummaryResult.value : { ok: false };
209
+ const resourceCurrent = resourceCurrentResult.status === 'fulfilled'
210
+ ? resourceCurrentResult.value
211
+ : { ok: false };
212
+ return {
213
+ ok: systemSummary.ok ?? false,
214
+ summary: isOkWithSummary(systemSummary) ? systemSummary.summary : {},
215
+ resources: isOkWithUsage(resourceCurrent) ? resourceCurrent.usage : null,
216
+ cost: null,
217
+ };
218
+ };
219
+ }
@@ -0,0 +1,17 @@
1
+ import type { IResponse } from '@zintrust/core';
2
+ import type { createSnapshotBuilder, TelemetrySettings } from './TelemetryAPI';
3
+ export type TelemetrySnapshotData = {
4
+ type: string;
5
+ ts: string;
6
+ ok: boolean;
7
+ summary: unknown;
8
+ resources: unknown;
9
+ cost: unknown;
10
+ };
11
+ export declare const TelemetryMonitoringService: Readonly<{
12
+ subscribe(callback: (data: TelemetrySnapshotData) => void): void;
13
+ unsubscribe(callback: (data: TelemetrySnapshotData) => void): void;
14
+ startMonitoring(settings: TelemetrySettings, buildSnapshot: ReturnType<typeof createSnapshotBuilder>): void;
15
+ stopMonitoring(): void;
16
+ }>;
17
+ export declare const teleStream: (res: IResponse, settings: TelemetrySettings, buildSnapshot: ReturnType<typeof createSnapshotBuilder>) => Promise<void>;
@@ -0,0 +1,113 @@
1
+ import { Logger, NodeSingletons } from '@zintrust/core';
2
+ // Internal state for singleton service
3
+ const emitter = new NodeSingletons.EventEmitter();
4
+ emitter.setMaxListeners(Infinity);
5
+ let interval = null;
6
+ let subscribers = 0;
7
+ let currentSettings = null;
8
+ let currentBuildSnapshot = null;
9
+ const broadcastTelemetrySnapshot = async () => {
10
+ try {
11
+ if (subscribers <= 0 || !currentBuildSnapshot || !currentSettings)
12
+ return;
13
+ const snapshot = await currentBuildSnapshot();
14
+ const payload = {
15
+ type: 'snapshot',
16
+ ts: new Date().toISOString(),
17
+ ...snapshot,
18
+ };
19
+ emitter.emit('snapshot', payload);
20
+ }
21
+ catch (err) {
22
+ Logger.error('TelemetryMonitoringService.broadcastSnapshot failed', err);
23
+ emitter.emit('error', err);
24
+ }
25
+ };
26
+ const startPolling = () => {
27
+ if (interval || !currentSettings)
28
+ return;
29
+ Logger.debug('Starting TelemetryMonitoringService polling');
30
+ // Initial fetch
31
+ void broadcastTelemetrySnapshot();
32
+ interval = setInterval(() => {
33
+ void broadcastTelemetrySnapshot();
34
+ }, currentSettings.refreshIntervalMs);
35
+ };
36
+ const stopPolling = () => {
37
+ if (interval) {
38
+ Logger.debug('Stopping TelemetryMonitoringService polling');
39
+ clearInterval(interval);
40
+ interval = null;
41
+ }
42
+ };
43
+ export const TelemetryMonitoringService = Object.freeze({
44
+ subscribe(callback) {
45
+ emitter.on('snapshot', callback);
46
+ subscribers++;
47
+ if (subscribers === 1) {
48
+ startPolling();
49
+ }
50
+ },
51
+ unsubscribe(callback) {
52
+ emitter.off('snapshot', callback);
53
+ subscribers--;
54
+ if (subscribers <= 0) {
55
+ stopPolling();
56
+ currentSettings = null;
57
+ currentBuildSnapshot = null;
58
+ }
59
+ },
60
+ startMonitoring(settings, buildSnapshot) {
61
+ if (subscribers === 0) {
62
+ currentSettings = settings;
63
+ currentBuildSnapshot = buildSnapshot;
64
+ }
65
+ },
66
+ stopMonitoring() {
67
+ if (subscribers <= 0) {
68
+ stopPolling();
69
+ currentSettings = null;
70
+ currentBuildSnapshot = null;
71
+ }
72
+ },
73
+ });
74
+ export const teleStream = async (res, settings, buildSnapshot) => {
75
+ const raw = res.getRaw();
76
+ raw.writeHead(200, {
77
+ 'Content-Type': 'text/event-stream',
78
+ 'Cache-Control': 'no-cache, no-transform',
79
+ Connection: 'keep-alive',
80
+ 'X-Accel-Buffering': 'no',
81
+ });
82
+ let closed = false;
83
+ const send = async (payload) => {
84
+ try {
85
+ raw.write(`data: ${JSON.stringify(payload)}\n\n`);
86
+ }
87
+ catch (err) {
88
+ Logger.error('Telemetry SSE send failed', err);
89
+ }
90
+ };
91
+ // Send hello immediately
92
+ await send({ type: 'hello', ts: new Date().toISOString() });
93
+ // Start monitoring with the singleton service
94
+ TelemetryMonitoringService.startMonitoring(settings, buildSnapshot);
95
+ // Subscribe to telemetry snapshots
96
+ const onSnapshot = (data) => {
97
+ if (!closed) {
98
+ void send(data);
99
+ }
100
+ };
101
+ TelemetryMonitoringService.subscribe(onSnapshot);
102
+ // Heartbeat to keep connection alive
103
+ const hb = setInterval(() => {
104
+ if (!closed)
105
+ raw.write(': ping\n\n');
106
+ }, 15000);
107
+ raw.on('close', () => {
108
+ closed = true;
109
+ clearInterval(hb);
110
+ TelemetryMonitoringService.unsubscribe(onSnapshot);
111
+ TelemetryMonitoringService.stopMonitoring();
112
+ });
113
+ };
@@ -0,0 +1 @@
1
+ export declare const renderAlertPanel: () => string;
@@ -0,0 +1,13 @@
1
+ export const renderAlertPanel = () => {
2
+ return `
3
+ <div class="zt-card">
4
+ <div class="zt-card-header">
5
+ <h3 class="zt-card-title">Alert History</h3>
6
+ <span class="zt-card-meta">Latest events</span>
7
+ </div>
8
+ <ul id="alertList" class="zt-alert-list">
9
+ <li class="zt-alert-item">No alerts yet.</li>
10
+ </ul>
11
+ </div>
12
+ `;
13
+ };
@@ -0,0 +1 @@
1
+ export declare const renderCostTracking: () => string;
@@ -0,0 +1,14 @@
1
+ export const renderCostTracking = () => {
2
+ return `
3
+ <div class="zt-card">
4
+ <div class="zt-card-header">
5
+ <h3 class="zt-card-title">Cost Tracking</h3>
6
+ <span class="zt-card-meta">Daily estimate</span>
7
+ </div>
8
+ <div class="zt-card-body">
9
+ <p class="zt-cost-value" id="costTotal">$0.00</p>
10
+ <p class="zt-card-meta">Auto-updated from resource metrics</p>
11
+ </div>
12
+ </div>
13
+ `;
14
+ };
@@ -0,0 +1 @@
1
+ export declare const renderResourceUsageChart: () => string;
@@ -0,0 +1,11 @@
1
+ export const renderResourceUsageChart = () => {
2
+ return `
3
+ <div class="zt-card">
4
+ <div class="zt-card-header">
5
+ <h3 class="zt-card-title">Resource Usage</h3>
6
+ <span class="zt-card-meta">Current snapshot</span>
7
+ </div>
8
+ <canvas id="resourceUsageChart" class="zt-chart"></canvas>
9
+ </div>
10
+ `;
11
+ };
@@ -0,0 +1 @@
1
+ export declare const renderWorkerHealthChart: () => string;
@@ -0,0 +1,11 @@
1
+ export const renderWorkerHealthChart = () => {
2
+ return `
3
+ <div class="zt-card">
4
+ <div class="zt-card-header">
5
+ <h3 class="zt-card-title">Worker Health</h3>
6
+ <span class="zt-card-meta">Last 24h</span>
7
+ </div>
8
+ <canvas id="workerHealthChart" class="zt-chart"></canvas>
9
+ </div>
10
+ `;
11
+ };
@@ -0,0 +1,15 @@
1
+ import type { IRouter } from '@zintrust/core';
2
+ export type TelemetryDashboardConfig = {
3
+ enabled?: boolean;
4
+ basePath?: string;
5
+ middleware?: ReadonlyArray<string>;
6
+ autoRefresh?: boolean;
7
+ refreshIntervalMs?: number;
8
+ };
9
+ export type TelemetryDashboardApi = {
10
+ registerRoutes: (router: IRouter) => void;
11
+ };
12
+ export declare const TelemetryDashboard: Readonly<{
13
+ create(config: TelemetryDashboardConfig): TelemetryDashboardApi;
14
+ }>;
15
+ export default TelemetryDashboard;
@@ -0,0 +1,60 @@
1
+ import { Router } from '@zintrust/core';
2
+ import { createSnapshotBuilder } from './api/TelemetryAPI';
3
+ import { teleStream } from './api/TelemetryMonitoringService';
4
+ import { getDashboardHtml } from './routes/dashboard';
5
+ const DEFAULTS = {
6
+ enabled: true,
7
+ basePath: '/telemetry',
8
+ autoRefresh: true,
9
+ refreshIntervalMs: 10000,
10
+ };
11
+ export const TelemetryDashboard = Object.freeze({
12
+ create(config) {
13
+ const settings = buildSettings(config);
14
+ const buildSnapshot = createSnapshotBuilder();
15
+ const registerRoutes = createRouteRegistrar(settings, buildSnapshot);
16
+ return Object.freeze({ registerRoutes });
17
+ },
18
+ });
19
+ function buildSettings(config) {
20
+ return {
21
+ enabled: config.enabled ?? DEFAULTS.enabled,
22
+ basePath: config.basePath ?? DEFAULTS.basePath,
23
+ middleware: config.middleware ?? [],
24
+ autoRefresh: config.autoRefresh ?? DEFAULTS.autoRefresh,
25
+ refreshIntervalMs: typeof config.refreshIntervalMs === 'number' && Number.isFinite(config.refreshIntervalMs)
26
+ ? Math.max(1000, Math.floor(config.refreshIntervalMs))
27
+ : DEFAULTS.refreshIntervalMs,
28
+ };
29
+ }
30
+ function createRouteRegistrar(settings, buildSnapshot) {
31
+ return (router) => {
32
+ if (!settings.enabled)
33
+ return;
34
+ const routeOptions = (settings.middleware.length > 0 ? { middleware: settings.middleware } : undefined);
35
+ registerDashboardRoute(router, settings, routeOptions);
36
+ registerSummaryApi(router, settings, routeOptions, buildSnapshot);
37
+ registerEventsApi(router, settings, routeOptions, buildSnapshot);
38
+ };
39
+ }
40
+ function registerDashboardRoute(router, settings, routeOptions) {
41
+ Router.get(router, settings.basePath, (_req, res) => {
42
+ res.html(getDashboardHtml({
43
+ basePath: settings.basePath,
44
+ autoRefresh: settings.autoRefresh,
45
+ refreshIntervalMs: settings.refreshIntervalMs,
46
+ }));
47
+ }, routeOptions);
48
+ }
49
+ function registerSummaryApi(router, settings, routeOptions, buildSnapshot) {
50
+ Router.get(router, `${settings.basePath}/api/summary`, async (_req, res) => {
51
+ const snapshot = await buildSnapshot();
52
+ res.json(snapshot);
53
+ }, routeOptions);
54
+ }
55
+ function registerEventsApi(router, settings, routeOptions, buildSnapshot) {
56
+ Router.get(router, `${settings.basePath}/api/events`, async (_req, res) => {
57
+ teleStream(res, settings, buildSnapshot);
58
+ }, routeOptions);
59
+ }
60
+ export default TelemetryDashboard;
@@ -0,0 +1,6 @@
1
+ export type DashboardUiOptions = {
2
+ basePath: string;
3
+ autoRefresh: boolean;
4
+ refreshIntervalMs: number;
5
+ };
6
+ export declare const getDashboardHtml: (options: DashboardUiOptions) => string;