@zintrust/workers 0.1.29 → 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 +10 -6
  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
@@ -16,6 +16,9 @@ let totalWorkers = 0;
16
16
  let autoRefreshEnabled = true;
17
17
  let refreshTimer = null;
18
18
  let currentTheme = null;
19
+ let eventSource = null;
20
+ let sseActive = false;
21
+ let lastSseRefresh = 0;
19
22
  const _bulkAutoStartEnabled = false;
20
23
  const _lastWorkers = [];
21
24
  const detailsCache = new Map();
@@ -88,7 +91,7 @@ function validateWorkerData(data) {
88
91
  return true;
89
92
  }
90
93
 
91
- // Data fetching
94
+ // Data fetching - only for search and pagination, SSE handles regular updates
92
95
  async function fetchData() {
93
96
  const elements = getDomElements();
94
97
 
@@ -105,7 +108,7 @@ async function fetchData() {
105
108
  limit: limit,
106
109
  status: document.getElementById('status-filter')?.value || '',
107
110
  driver: document.getElementById('driver-filter')?.value || '',
108
- sortBy: document.getElementById('sort-select')?.value || 'name',
111
+ sortBy: document.getElementById('sort-select')?.value || 'status',
109
112
  sortOrder: 'asc',
110
113
  search: query,
111
114
  });
@@ -138,7 +141,7 @@ async function fetchData() {
138
141
  function changeLimit(_newLimit) {
139
142
  localStorage.setItem(PAGE_SIZE_KEY, _newLimit);
140
143
  currentPage = 1;
141
- fetchData();
144
+ fetchData(); // Enable for pagination
142
145
  }
143
146
 
144
147
  // Make functions globally available for HTML onclick/onchange handlers
@@ -308,7 +311,9 @@ function updateDetailViews(detailRow, details) {
308
311
  if (!details) return;
309
312
 
310
313
  // Update all data-key elements
311
- detailRow.querySelectorAll('[data-key]').forEach(updateDetailElement);
314
+ detailRow.querySelectorAll('[data-key]').forEach((element) => {
315
+ updateDetailElement(element);
316
+ });
312
317
 
313
318
  // Delegate to specialized functions
314
319
  updateLogsContainer(detailRow, details);
@@ -1236,41 +1241,79 @@ function loadPage(direction) {
1236
1241
  } else if (direction === 'next' && currentPage < totalPages) {
1237
1242
  currentPage++;
1238
1243
  }
1239
- fetchData();
1244
+ fetchData(); // Enable for pagination
1240
1245
  }
1241
1246
 
1242
1247
  function goToPage(page) {
1243
1248
  currentPage = page;
1244
- fetchData();
1249
+ fetchData(); // Enable for pagination
1245
1250
  }
1246
1251
 
1247
1252
  // Worker actions
1248
1253
  async function startWorker(name, driver) {
1254
+ // Find and disable the start button to prevent multiple clicks
1255
+ const startBtn = document.querySelector(`button[onclick="startWorker('${name}', '${driver}')"]`);
1256
+ if (startBtn) {
1257
+ startBtn.disabled = true;
1258
+ startBtn.textContent = 'Starting...';
1259
+ }
1260
+
1249
1261
  try {
1250
1262
  await fetch(`${API_BASE}/api/workers/${name}/start?driver=${driver}`, { method: 'POST' });
1251
- fetchData();
1263
+ fetchData(); // Refresh data after action
1252
1264
  } catch (err) {
1253
1265
  console.error('Failed to start worker:', err);
1266
+ // Re-enable button on error
1267
+ if (startBtn) {
1268
+ startBtn.disabled = false;
1269
+ startBtn.textContent = 'Start';
1270
+ }
1254
1271
  }
1255
1272
  }
1256
1273
 
1257
1274
  async function stopWorker(name, driver) {
1275
+ // Find and disable the stop button to prevent multiple clicks
1276
+ const stopBtn = document.querySelector(`button[onclick="stopWorker('${name}', '${driver}')"]`);
1277
+ if (stopBtn) {
1278
+ stopBtn.disabled = true;
1279
+ stopBtn.textContent = 'Stopping...';
1280
+ }
1281
+
1258
1282
  try {
1259
1283
  await fetch(`${API_BASE}/api/workers/${name}/stop?driver=${driver}`, { method: 'POST' });
1260
- fetchData();
1284
+ fetchData(); // Refresh data after action
1261
1285
  } catch (err) {
1262
1286
  console.error('Failed to stop worker:', err);
1287
+ // Re-enable button on error
1288
+ if (stopBtn) {
1289
+ stopBtn.disabled = false;
1290
+ stopBtn.textContent = 'Stop';
1291
+ }
1263
1292
  }
1264
1293
  }
1265
1294
 
1266
1295
  async function restartWorker(name, driver) {
1296
+ // Find and disable the restart button to prevent multiple clicks
1297
+ const restartBtn = document.querySelector(
1298
+ `button[onclick="restartWorker('${name}', '${driver}')"]`
1299
+ );
1300
+ if (restartBtn) {
1301
+ restartBtn.disabled = true;
1302
+ restartBtn.textContent = 'Restarting...';
1303
+ }
1304
+
1267
1305
  try {
1268
1306
  await fetch(`${API_BASE}/api/workers/${name}/restart?driver=${driver}`, {
1269
1307
  method: 'POST',
1270
1308
  });
1271
- fetchData();
1309
+ fetchData(); // Refresh data after action
1272
1310
  } catch (err) {
1273
1311
  console.error('Failed to restart worker:', err);
1312
+ // Re-enable button on error
1313
+ if (restartBtn) {
1314
+ restartBtn.disabled = false;
1315
+ restartBtn.textContent = 'Restart';
1316
+ }
1274
1317
  }
1275
1318
  }
1276
1319
 
@@ -1283,7 +1326,7 @@ async function deleteWorker(name, driver) {
1283
1326
  method: 'DELETE',
1284
1327
  });
1285
1328
  if (!response.ok) throw new Error('Failed to delete worker');
1286
- fetchData();
1329
+ fetchData(); // Refresh data after action
1287
1330
  } catch (err) {
1288
1331
  console.error('Failed to delete worker:', err);
1289
1332
  alert('Failed to delete worker: ' + err.message);
@@ -1608,7 +1651,7 @@ function createEditButtons(modal, textarea, name, driver) {
1608
1651
 
1609
1652
  alert('Worker updated successfully!');
1610
1653
  modal.remove();
1611
- fetchData(); // Refresh the data
1654
+ fetchData(); // Refresh data after action
1612
1655
  } catch (error) {
1613
1656
  alert('Invalid JSON: ' + error.message);
1614
1657
  }
@@ -1683,62 +1726,171 @@ function toggleAutoRefresh() {
1683
1726
  setAutoRefresh(!autoRefreshEnabled);
1684
1727
  }
1685
1728
 
1686
- // Auto-refresh
1687
- function setAutoRefresh(enabled) {
1688
- autoRefreshEnabled = enabled;
1689
- localStorage.setItem(AUTO_REFRESH_KEY, enabled.toString());
1729
+ function setupEventStream() {
1730
+ if (!globalThis.window.EventSource) return;
1731
+
1732
+ if (eventSource) {
1733
+ eventSource.close();
1734
+ eventSource = null;
1735
+ }
1736
+
1737
+ // Get current sort parameters to match fetchData
1738
+ const sortBy = document.getElementById('sort-select')?.value || 'status';
1739
+ const sortOrder = 'asc';
1740
+ const status = document.getElementById('status-filter')?.value || '';
1741
+ const driver = document.getElementById('driver-filter')?.value || '';
1742
+ const search = document.getElementById('search-input')?.value || '';
1743
+
1744
+ const params = new URLSearchParams({
1745
+ sortBy,
1746
+ sortOrder,
1747
+ status,
1748
+ driver,
1749
+ search,
1750
+ });
1751
+
1752
+ eventSource = new globalThis.window.EventSource(
1753
+ API_BASE + '/api/workers/events?' + params.toString()
1754
+ );
1690
1755
 
1756
+ eventSource.onopen = () => {
1757
+ sseActive = true;
1758
+ if (refreshTimer) {
1759
+ clearInterval(refreshTimer);
1760
+ refreshTimer = null;
1761
+ }
1762
+ };
1763
+
1764
+ eventSource.onmessage = (evt) => {
1765
+ const elements = getDomElements();
1766
+
1767
+ try {
1768
+ const payload = JSON.parse(evt.data);
1769
+ if (payload && payload.type === 'snapshot') {
1770
+ const now = Date.now();
1771
+ if (now - lastSseRefresh < 4000) return;
1772
+ lastSseRefresh = now;
1773
+ // Use SSE data to update the page
1774
+ if (payload.workers) {
1775
+ renderWorkers(payload.workers);
1776
+ // Hide loading state if it's showing
1777
+ } else if (payload.snapshot) {
1778
+ // Handle queue snapshot events (different format)
1779
+ // console.log('Queue snapshot received, not updating workers UI');
1780
+ }
1781
+ if (payload.monitoring) {
1782
+ // Handle queue monitoring events (different format)
1783
+ }
1784
+ }
1785
+ hideLoadingState(elements);
1786
+ } catch (err) {
1787
+ hideLoadingState(elements);
1788
+ console.error('Failed to parse SSE payload', err);
1789
+ }
1790
+ };
1791
+
1792
+ eventSource.onerror = () => {
1793
+ if (eventSource) {
1794
+ eventSource.close();
1795
+ eventSource = null;
1796
+ }
1797
+ sseActive = false;
1798
+ };
1799
+ }
1800
+
1801
+ // Helper functions to reduce complexity
1802
+ function clearRefreshTimer() {
1691
1803
  if (refreshTimer) {
1692
1804
  clearInterval(refreshTimer);
1693
1805
  refreshTimer = null;
1694
1806
  }
1807
+ }
1808
+
1809
+ function disableEventStream() {
1810
+ if (eventSource) {
1811
+ eventSource.close();
1812
+ eventSource = null;
1813
+ }
1814
+ sseActive = false;
1815
+ }
1695
1816
 
1696
- if (enabled) {
1697
- refreshTimer = setInterval(fetchData, 30000);
1817
+ function enableEventStreamOrPolling() {
1818
+ if (globalThis.window.EventSource) {
1819
+ setupEventStream();
1820
+ } else if (!sseActive && autoRefreshEnabled) {
1821
+ refreshTimer = setInterval(fetchData, 30000); // Commented out - SSE should be primary
1822
+ }
1823
+ }
1824
+
1825
+ function createPauseIcon(icon) {
1826
+ // Clear existing content
1827
+ while (icon.firstChild) {
1828
+ icon.firstChild.remove();
1829
+ }
1830
+ // Create pause icon
1831
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1832
+ svg.setAttribute('viewBox', '0 0 24 24');
1833
+ const rect1 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1834
+ rect1.setAttribute('x', '6');
1835
+ rect1.setAttribute('y', '4');
1836
+ rect1.setAttribute('width', '4');
1837
+ rect1.setAttribute('height', '16');
1838
+ const rect2 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1839
+ rect2.setAttribute('x', '14');
1840
+ rect2.setAttribute('y', '4');
1841
+ rect2.setAttribute('width', '4');
1842
+ rect2.setAttribute('height', '16');
1843
+ svg.appendChild(rect1);
1844
+ svg.appendChild(rect2);
1845
+ icon.appendChild(svg);
1846
+ }
1847
+
1848
+ function createPlayIcon(icon) {
1849
+ // Clear existing content
1850
+ while (icon.firstChild) {
1851
+ icon.firstChild.remove();
1698
1852
  }
1853
+ // Create play icon
1854
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1855
+ svg.setAttribute('viewBox', '0 0 24 24');
1856
+ const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
1857
+ polygon.setAttribute('points', '5 3 19 12 5 21 5 3');
1858
+ svg.appendChild(polygon);
1859
+ icon.appendChild(svg);
1860
+ }
1699
1861
 
1862
+ function updateRefreshButton(enabled) {
1700
1863
  const btn = document.getElementById('auto-refresh-toggle');
1701
1864
  const icon = document.getElementById('auto-refresh-icon');
1702
1865
  const label = document.getElementById('auto-refresh-label');
1703
1866
 
1704
- if (btn && icon && label) {
1705
- if (enabled) {
1706
- label.textContent = 'Pause Refresh';
1707
- // Clear existing content
1708
- while (icon.firstChild) {
1709
- icon.firstChild.remove();
1710
- }
1711
- // Create pause icon
1712
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1713
- svg.setAttribute('viewBox', '0 0 24 24');
1714
- const rect1 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1715
- rect1.setAttribute('x', '6');
1716
- rect1.setAttribute('y', '4');
1717
- rect1.setAttribute('width', '4');
1718
- rect1.setAttribute('height', '16');
1719
- const rect2 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1720
- rect2.setAttribute('x', '14');
1721
- rect2.setAttribute('y', '4');
1722
- rect2.setAttribute('width', '4');
1723
- rect2.setAttribute('height', '16');
1724
- svg.appendChild(rect1);
1725
- svg.appendChild(rect2);
1726
- icon.appendChild(svg);
1727
- } else {
1728
- label.textContent = 'Auto Refresh';
1729
- // Clear existing content
1730
- while (icon.firstChild) {
1731
- icon.firstChild.remove();
1732
- }
1733
- // Create play icon
1734
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1735
- svg.setAttribute('viewBox', '0 0 24 24');
1736
- const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
1737
- polygon.setAttribute('points', '5 3 19 12 5 21 5 3');
1738
- svg.appendChild(polygon);
1739
- icon.appendChild(svg);
1740
- }
1867
+ if (!btn || !icon || !label) return;
1868
+
1869
+ if (enabled) {
1870
+ label.textContent = 'Pause Refresh';
1871
+ createPauseIcon(icon);
1872
+ } else {
1873
+ label.textContent = 'Auto Refresh';
1874
+ createPlayIcon(icon);
1875
+ }
1876
+ }
1877
+
1878
+ // Auto-refresh - Refactored to reduce complexity
1879
+ function setAutoRefresh(enabled) {
1880
+ autoRefreshEnabled = enabled;
1881
+ localStorage.setItem(AUTO_REFRESH_KEY, enabled.toString());
1882
+
1883
+ clearRefreshTimer();
1884
+
1885
+ if (!enabled) {
1886
+ disableEventStream();
1741
1887
  }
1888
+
1889
+ if (enabled) {
1890
+ enableEventStreamOrPolling();
1891
+ }
1892
+
1893
+ updateRefreshButton(enabled);
1742
1894
  }
1743
1895
 
1744
1896
  // Event listeners
@@ -1749,21 +1901,35 @@ document.addEventListener('DOMContentLoaded', () => {
1749
1901
  document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
1750
1902
 
1751
1903
  // Set up event listeners
1752
- document.getElementById('status-filter').addEventListener('change', fetchData);
1753
- document.getElementById('driver-filter').addEventListener('change', fetchData);
1754
- document.getElementById('sort-select').addEventListener('change', fetchData);
1904
+ document.getElementById('status-filter').addEventListener('change', () => {
1905
+ currentPage = 1;
1906
+ setupEventStream(); // Reconnect SSE with new filters
1907
+ fetchData(); // Enable for search/filter
1908
+ });
1909
+ document.getElementById('driver-filter').addEventListener('change', () => {
1910
+ currentPage = 1;
1911
+ setupEventStream(); // Reconnect SSE with new filters
1912
+ fetchData(); // Enable for search/filter
1913
+ });
1914
+ document.getElementById('sort-select').addEventListener('change', () => {
1915
+ currentPage = 1;
1916
+ setupEventStream(); // Reconnect SSE with new sort
1917
+ fetchData(); // Enable for sorting
1918
+ });
1755
1919
 
1756
1920
  const searchBtn = document.getElementById('search-btn');
1757
1921
  if (searchBtn) {
1758
1922
  searchBtn.addEventListener('click', () => {
1759
1923
  currentPage = 1;
1760
- fetchData();
1924
+ setupEventStream(); // Reconnect SSE with new search
1925
+ fetchData(); // Enable for search
1761
1926
  });
1762
1927
  }
1763
1928
  document.getElementById('search-input').addEventListener('keypress', (e) => {
1764
1929
  if (e.key === 'Enter') {
1765
1930
  currentPage = 1;
1766
- fetchData();
1931
+ setupEventStream(); // Reconnect SSE with new search
1932
+ fetchData(); // Enable for search
1767
1933
  }
1768
1934
  });
1769
1935
 
@@ -1776,6 +1942,11 @@ document.addEventListener('DOMContentLoaded', () => {
1776
1942
  // Use stored value
1777
1943
  setAutoRefresh(storedAutoRefresh === 'true');
1778
1944
  }
1779
- // Load initial data
1780
- fetchData();
1945
+ setupEventStream();
1946
+ // SSE should handle initial data loading
1947
+ globalThis.window.addEventListener('beforeunload', () => {
1948
+ if (eventSource) {
1949
+ eventSource.close();
1950
+ }
1951
+ });
1781
1952
  });
@@ -0,0 +1,30 @@
1
+ <svg width="120" height="120" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="zt-g2d" x1="10" y1="50" x2="90" y2="50" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#22c55e" />
5
+ <stop offset="1" stop-color="#38bdf8" />
6
+ </linearGradient>
7
+ </defs>
8
+ <circle cx="50" cy="50" r="34" stroke="rgba(255,255,255,0.16)" stroke-width="4" />
9
+ <ellipse cx="50" cy="50" rx="40" ry="18" stroke="url(#zt-g2d)" stroke-width="4" />
10
+ <ellipse cx="50" cy="50" rx="18" ry="40" stroke="url(#zt-g2d)" stroke-width="4" opacity="0.75" />
11
+ <circle cx="50" cy="50" r="6" fill="url(#zt-g2d)" />
12
+ <path
13
+ d="M40 52C35 52 32 49 32 44C32 39 35 36 40 36H48"
14
+ stroke="white"
15
+ stroke-width="6"
16
+ stroke-linecap="round"
17
+ />
18
+ <path
19
+ d="M60 48C65 48 68 51 68 56C68 61 65 64 60 64H52"
20
+ stroke="white"
21
+ stroke-width="6"
22
+ stroke-linecap="round"
23
+ />
24
+ <path
25
+ d="M44 50H56"
26
+ stroke="rgba(255,255,255,0.22)"
27
+ stroke-width="6"
28
+ stroke-linecap="round"
29
+ />
30
+ </svg>
@@ -1,3 +0,0 @@
1
- export type { WorkersDashboardUiOptions } from './types';
2
- declare const getWorkersDashboardStyles: () => string;
3
- export { getWorkersDashboardStyles };