@oneuptime/common 14.0.8 → 14.0.10

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 (976) hide show
  1. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +60 -0
  2. package/Models/DatabaseModels/CephCluster.ts +213 -0
  3. package/Models/DatabaseModels/DatabaseServer.ts +213 -0
  4. package/Models/DatabaseModels/DockerHost.ts +213 -0
  5. package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
  6. package/Models/DatabaseModels/GlobalConfig.ts +1 -1
  7. package/Models/DatabaseModels/Host.ts +213 -0
  8. package/Models/DatabaseModels/Index.ts +4 -0
  9. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  10. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  11. package/Models/DatabaseModels/PodmanHost.ts +213 -0
  12. package/Models/DatabaseModels/Project.ts +55 -5
  13. package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
  14. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  15. package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
  16. package/Models/DatabaseModels/RunnerJob.ts +219 -0
  17. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  18. package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
  19. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  20. package/Server/API/AIChatAPI.ts +6 -3
  21. package/Server/API/AIInvestigationAPI.ts +17 -10
  22. package/Server/API/AutoRemediationAPI.ts +598 -25
  23. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  24. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  25. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  26. package/Server/API/ResourceAiAccessAPI.ts +1669 -0
  27. package/Server/Infrastructure/GlobalCache.ts +32 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  33. package/Server/Infrastructure/Semaphore.ts +22 -0
  34. package/Server/Middleware/TelemetryIngest.ts +51 -18
  35. package/Server/Services/AIService.ts +70 -1
  36. package/Server/Services/AlertService.ts +352 -11
  37. package/Server/Services/AlertSeverityService.ts +65 -15
  38. package/Server/Services/AnalyticsDatabaseService.ts +45 -1
  39. package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
  40. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  41. package/Server/Services/CephClusterService.ts +109 -1
  42. package/Server/Services/DatabaseServerService.ts +116 -1
  43. package/Server/Services/DockerHostService.ts +109 -1
  44. package/Server/Services/DockerSwarmClusterService.ts +113 -1
  45. package/Server/Services/HostService.ts +126 -4
  46. package/Server/Services/IncidentService.ts +280 -104
  47. package/Server/Services/IncidentSeverityService.ts +66 -15
  48. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  49. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  50. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  51. package/Server/Services/KubernetesClusterService.ts +129 -21
  52. package/Server/Services/MetricRecordingRuleService.ts +47 -0
  53. package/Server/Services/PodmanHostService.ts +109 -1
  54. package/Server/Services/ProjectService.ts +32 -0
  55. package/Server/Services/ProxmoxClusterService.ts +110 -1
  56. package/Server/Services/ProxmoxResourceService.ts +322 -5
  57. package/Server/Services/ResourceAiAccessService.ts +1439 -0
  58. package/Server/Services/ResourceAiAgentJobService.ts +202 -0
  59. package/Server/Services/ResourceAiAgentService.ts +2432 -0
  60. package/Server/Services/RunnerJobService.ts +852 -108
  61. package/Server/Services/RunnerService.ts +1 -1
  62. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  63. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  64. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  65. package/Server/Services/TelemetryUsageBillingService.ts +28 -0
  66. package/Server/Services/TraceRecordingRuleService.ts +33 -0
  67. package/Server/Services/VMwareVCenterService.ts +110 -1
  68. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  69. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  70. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  71. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1475 -51
  72. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1000 -43
  73. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
  74. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
  75. package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
  76. package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
  77. package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
  78. package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
  79. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
  80. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +120 -7
  81. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
  82. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
  83. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  84. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  85. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  86. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +402 -14
  87. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
  88. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
  89. package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
  90. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
  91. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  92. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  93. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  94. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  95. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  96. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
  97. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
  98. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
  99. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
  100. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
  101. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
  102. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +1073 -213
  103. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
  104. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
  105. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
  106. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
  107. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
  108. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
  109. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  110. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  111. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  112. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  113. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  114. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  115. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  116. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  117. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  118. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  119. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  120. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  121. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  122. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  123. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  124. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  125. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  126. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  127. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +184 -19
  128. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  129. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  130. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  131. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  132. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  133. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  134. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  135. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  136. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  137. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  138. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  139. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  140. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  141. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  142. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
  143. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  144. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  145. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  146. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  147. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  148. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  149. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  150. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  151. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  152. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  153. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  154. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  155. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  156. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  157. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  158. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  159. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  160. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  161. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  162. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  163. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  164. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  165. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  166. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  167. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  168. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  169. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  170. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  171. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  172. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  173. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  174. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  175. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  176. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  177. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  178. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  179. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  180. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  181. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  182. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  183. package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
  184. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  185. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  186. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  187. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  188. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  189. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  190. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  191. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  192. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  193. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  194. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  195. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  196. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  197. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  198. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  199. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  200. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  201. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  202. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  203. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  204. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  205. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  206. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  207. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  208. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  209. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  210. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  211. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  212. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  213. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  214. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  215. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  216. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  217. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  218. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  219. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  220. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  221. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  222. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  223. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  224. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  225. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  226. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  227. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  228. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  229. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  230. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  231. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  232. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  233. package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
  234. package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
  235. package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
  236. package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
  237. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
  238. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
  239. package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
  240. package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
  241. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  242. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  243. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
  244. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  245. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  246. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  247. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  248. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  249. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  250. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  251. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  252. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  253. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  254. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  255. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  256. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  257. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  258. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  259. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  260. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  261. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  262. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  263. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  264. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  265. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  266. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  267. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  268. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  269. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  270. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  271. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  272. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  273. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  274. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  275. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  276. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  277. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
  278. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  279. package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
  280. package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
  281. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  282. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  283. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  284. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  285. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  286. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  287. package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
  288. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  289. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  290. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  291. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  292. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  293. package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
  294. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  295. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
  296. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  297. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  298. package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
  299. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  300. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
  301. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +119 -4
  302. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +196 -2
  303. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +123 -2
  304. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  305. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  306. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  307. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  308. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  309. package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
  310. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  311. package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
  312. package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
  313. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
  314. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  315. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  316. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  317. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  318. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  319. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  320. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  321. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  322. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  323. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  324. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  325. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  326. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  327. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  328. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
  329. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  330. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  331. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  332. package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
  333. package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
  334. package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
  335. package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
  336. package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
  337. package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
  338. package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
  339. package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
  340. package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
  341. package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
  342. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +153 -15
  343. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  344. package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
  345. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  346. package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
  347. package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
  348. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  349. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  350. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  351. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  352. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  353. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  354. package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
  355. package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
  356. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  357. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  358. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  359. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  360. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
  361. package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
  362. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  363. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  364. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
  365. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  366. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  367. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  368. package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
  369. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  370. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  371. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  372. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  373. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
  374. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  375. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
  376. package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
  377. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
  378. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
  379. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
  380. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
  381. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
  382. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  383. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
  384. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  385. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  386. package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
  387. package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
  388. package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
  389. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
  390. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  391. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  392. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  393. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  394. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  395. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  396. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  397. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +75 -3
  398. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
  399. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
  400. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
  401. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
  402. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
  403. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
  404. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
  405. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
  406. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
  407. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
  408. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
  409. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
  410. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
  411. package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
  412. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  413. package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
  414. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  415. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  416. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
  417. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
  418. package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
  419. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  420. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  421. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
  422. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
  423. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
  424. package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
  425. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
  426. package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
  427. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  428. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  429. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  430. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  431. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  432. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  433. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  434. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  435. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  436. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  437. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  438. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  439. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  440. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  441. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  442. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  443. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  444. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  445. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  446. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  447. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  448. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  449. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  450. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  451. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  452. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  453. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  454. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  455. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  456. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  457. package/Tests/UI/Components/Input.test.tsx +80 -1
  458. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  459. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  460. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  461. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  462. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  463. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  464. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  465. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  466. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  467. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  468. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  469. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  470. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  471. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  472. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  473. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  474. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  475. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  476. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  477. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  478. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  479. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  480. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  481. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
  482. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  483. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  484. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  485. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  486. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  487. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  488. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  489. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  490. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  491. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  492. package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
  493. package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
  494. package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
  495. package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
  496. package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
  497. package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
  498. package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
  499. package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
  500. package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
  501. package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
  502. package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
  503. package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
  504. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
  505. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
  506. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
  507. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
  508. package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
  509. package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
  510. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  511. package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
  512. package/Types/AI/AIRunType.ts +9 -4
  513. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  514. package/Types/AI/ResourceAiAccessApi.ts +140 -0
  515. package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
  516. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +197 -18
  517. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  518. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  519. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  520. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  521. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  522. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  523. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
  524. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
  525. package/Types/Monitor/RumAlertTemplates.ts +351 -4
  526. package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
  527. package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
  528. package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
  529. package/Types/Runbook/RunbookStepType.ts +29 -0
  530. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  531. package/Types/Team/ComplianceRule.ts +434 -0
  532. package/Types/Team/ComplianceRuleType.ts +21 -0
  533. package/Types/Team/TeamComplianceStatus.ts +81 -0
  534. package/Types/Telemetry/TelemetryIngestSurface.ts +40 -0
  535. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  536. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  537. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  538. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  539. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  540. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  541. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  542. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  543. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  544. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  545. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  546. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  547. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  548. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  549. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  550. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  551. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  552. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  553. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  554. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  555. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  556. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  557. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  558. package/UI/Components/Input/Input.tsx +90 -57
  559. package/UI/Components/List/ListRow.tsx +8 -74
  560. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  561. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  562. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  563. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  564. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  565. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  566. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  567. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  568. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  569. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  570. package/UI/Components/Table/TableBody.tsx +8 -2
  571. package/UI/Components/Table/TableRow.tsx +20 -140
  572. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  573. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  574. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  575. package/UI/Components/TelemetryViewer/types.ts +2 -0
  576. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  577. package/UI/Components/Toggle/Toggle.tsx +26 -0
  578. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  579. package/UI/Styles/Theme.css +14 -2
  580. package/Utils/AI/InvestigationReport.ts +121 -15
  581. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  582. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  583. package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
  584. package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
  585. package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
  586. package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
  587. package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
  588. package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
  589. package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
  590. package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
  591. package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
  592. package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
  593. package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
  594. package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
  595. package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
  596. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  597. package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
  598. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  599. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  600. package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
  601. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  602. package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
  603. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  604. package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
  605. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  606. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
  607. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  608. package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
  609. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  610. package/build/dist/Models/DatabaseModels/Host.js +219 -0
  611. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  612. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  613. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  614. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  615. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  616. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  617. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  618. package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
  619. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  620. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  621. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  622. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
  623. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  624. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  625. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  626. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
  627. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
  628. package/build/dist/Models/DatabaseModels/RunnerJob.js +219 -0
  629. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  630. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  631. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  632. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
  633. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  634. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  635. package/build/dist/Server/API/AIChatAPI.js +6 -1
  636. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  637. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  638. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  639. package/build/dist/Server/API/AutoRemediationAPI.js +374 -24
  640. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  641. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  642. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  643. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  644. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  645. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  646. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  647. package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
  648. package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
  649. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  650. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  651. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  652. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  653. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  654. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  655. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  656. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  657. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
  658. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
  659. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  660. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  661. package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
  662. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  663. package/build/dist/Server/Middleware/TelemetryIngest.js +41 -19
  664. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  665. package/build/dist/Server/Services/AIService.js +59 -1
  666. package/build/dist/Server/Services/AIService.js.map +1 -1
  667. package/build/dist/Server/Services/AlertService.js +248 -13
  668. package/build/dist/Server/Services/AlertService.js.map +1 -1
  669. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  670. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  671. package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
  672. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  673. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
  674. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  675. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  676. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  677. package/build/dist/Server/Services/CephClusterService.js +104 -0
  678. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  679. package/build/dist/Server/Services/DatabaseServerService.js +99 -0
  680. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
  681. package/build/dist/Server/Services/DockerHostService.js +104 -0
  682. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  683. package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
  684. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  685. package/build/dist/Server/Services/HostService.js +114 -2
  686. package/build/dist/Server/Services/HostService.js.map +1 -1
  687. package/build/dist/Server/Services/IncidentService.js +199 -72
  688. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  689. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  690. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  691. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  692. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  693. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  694. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  695. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  696. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  697. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  698. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  699. package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
  700. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
  701. package/build/dist/Server/Services/PodmanHostService.js +104 -0
  702. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  703. package/build/dist/Server/Services/ProjectService.js +22 -0
  704. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  705. package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
  706. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  707. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  708. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  709. package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
  710. package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
  711. package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
  712. package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
  713. package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
  714. package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
  715. package/build/dist/Server/Services/RunnerJobService.js +522 -97
  716. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  717. package/build/dist/Server/Services/RunnerService.js +1 -1
  718. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  719. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  720. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  721. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  722. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  723. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  724. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  725. package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
  726. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  727. package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
  728. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
  729. package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
  730. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
  731. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  732. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  733. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  734. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  735. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  736. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  737. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1056 -43
  738. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  739. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +683 -42
  740. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  741. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
  742. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  743. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
  744. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
  745. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
  746. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
  747. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
  748. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
  749. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
  750. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
  751. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
  752. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
  753. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
  754. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
  755. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +89 -9
  756. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  757. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
  758. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  759. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
  760. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  761. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  762. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  763. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  764. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  765. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  766. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +294 -15
  767. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  768. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
  769. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  770. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
  771. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  772. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
  773. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
  774. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
  775. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
  776. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  777. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  778. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  779. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  780. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  781. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  782. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  783. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  784. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  785. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  786. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
  787. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  788. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
  789. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  790. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
  791. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  792. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
  793. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  794. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
  795. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  796. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
  797. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  798. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +768 -152
  799. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  800. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
  801. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
  802. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
  803. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
  804. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
  805. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
  806. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
  807. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
  808. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
  809. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
  810. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
  811. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
  812. package/build/dist/Types/AI/AIRunType.js +9 -4
  813. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  814. package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
  815. package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
  816. package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
  817. package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
  818. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +126 -45
  819. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  820. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  821. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  822. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  823. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  824. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  825. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  826. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  827. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  828. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  829. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  830. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
  831. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  832. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
  833. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  834. package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
  835. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  836. package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
  837. package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
  838. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
  839. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
  840. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
  841. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
  842. package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
  843. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  844. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  845. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  846. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  847. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  848. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  849. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  850. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  851. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  852. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +36 -0
  853. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  854. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  855. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  856. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  857. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  858. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  859. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  860. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  861. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  862. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  863. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  864. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  865. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  866. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  867. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  868. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  869. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  870. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  871. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  872. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  873. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  874. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  875. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  876. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  877. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  878. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  879. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  880. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  881. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  882. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  883. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  884. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  885. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  886. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  887. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  888. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  889. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  890. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  891. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  892. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  893. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  894. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  895. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  896. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  897. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  898. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  899. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  900. package/build/dist/UI/Components/Input/Input.js +69 -54
  901. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  902. package/build/dist/UI/Components/List/ListRow.js +4 -39
  903. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  904. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  905. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  906. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  907. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  908. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  909. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  910. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  911. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  912. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  913. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  914. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  915. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  916. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  917. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  918. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  919. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  920. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  921. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  922. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  923. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  924. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  925. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  926. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  927. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  928. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  929. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  930. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  931. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  932. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  933. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  934. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  935. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  936. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  937. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  938. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  939. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  940. package/build/dist/Utils/AI/InvestigationReport.js +71 -11
  941. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  942. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  943. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  944. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  945. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  946. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
  947. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
  948. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
  949. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
  950. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
  951. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
  952. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
  953. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
  954. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
  955. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
  956. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
  957. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
  958. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
  959. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
  960. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
  961. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
  962. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
  963. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
  964. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
  965. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
  966. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
  967. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
  968. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
  969. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
  970. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
  971. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
  972. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  973. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  974. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
  975. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
  976. package/package.json +1 -1
@@ -17,12 +17,29 @@ import {
17
17
  } from "@testing-library/react";
18
18
  import * as React from "react";
19
19
  import { MemoryRouter } from "react-router-dom";
20
+ import {
21
+ Edge,
22
+ FitViewOptions,
23
+ Node,
24
+ NodeChange,
25
+ ReactFlowInstance,
26
+ Viewport,
27
+ } from "reactflow";
20
28
  import EntityType from "../../../Types/Telemetry/EntityType";
21
29
  import EntityRelationshipType from "../../../Types/Telemetry/EntityRelationshipType";
22
30
  import EntitySource from "../../../Types/Telemetry/EntitySource";
23
31
  import TimeRange from "../../../Types/Time/TimeRange";
32
+ import { ComponentProps as FlowViewportGuardProps } from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/FlowViewportGuard";
33
+ import {
34
+ FlowExtent,
35
+ UNBOUNDED_FLOW_EXTENT,
36
+ noticeOffsetInCanvas,
37
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/FlowViewport";
24
38
  import { ServiceOperationalStatus } from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/OperationalOverlay";
25
- import ServiceMapGraph from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/ServiceMapGraph";
39
+ import ServiceMapGraph, {
40
+ OUT_OF_VIEW_NOTICE_DELAY_MS,
41
+ SERVICE_MAP_PAN_MARGIN,
42
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/ServiceMapGraph";
26
43
  import {
27
44
  EntityDetailTarget,
28
45
  TopologyEntity,
@@ -34,15 +51,28 @@ import getJestMockFunction, { MockFunction } from "../../MockType";
34
51
 
35
52
  /*
36
53
  * The Service Map component: what a person sees first, how they narrow it
37
- * down, and how they get to details. Graph drawing itself (React Flow) is
38
- * replaced by a stand-in that renders nodes and edges as buttons, and the
39
- * detail drawer (which fetches its own connections) by a stand-in that
40
- * records the props it was given, so these tests exercise the component's
41
- * decisions, not the canvas or the drawer.
54
+ * down, how they get to details, and how the map stays on its canvas.
55
+ *
56
+ * Graph drawing itself (React Flow) is replaced by a stand-in that renders
57
+ * nodes and edges as buttons, renders its children and records every props
58
+ * object it is given, so a test can tell a rebuilt nodes array from the one
59
+ * handed back unchanged; its Controls record their props too. The viewport
60
+ * guard (which reads React Flow's store) is replaced by a stand-in that keeps
61
+ * the real guard's side of the contract with the map: it reports a React
62
+ * Flow instance once the viewport is ready and, on unmount, withdraws it and
63
+ * reports the drawing in view and the pan extent unbounded. What the real
64
+ * guard measures on a canvas (the pan extent, whether the drawing is in
65
+ * view) the tests report through the props it was last given, and a pan or
66
+ * zoom by hand through React Flow's recorded onMoveStart and onMoveEnd, the
67
+ * way React Flow reports one. The detail drawer (which fetches its own
68
+ * connections) is a stand-in that records the props it was given, and a
69
+ * background tab is stood in for by shadowing document.visibilityState.
70
+ * jsdom lays nothing out, so where the canvas is on screen, the window's
71
+ * height and the out-of-view notice's height are stood in for as well (see
72
+ * standInForLayout). So these tests exercise the component's decisions, not
73
+ * the canvas, the guard or the drawer.
42
74
  */
43
75
 
44
- const mockFitView: MockFunction = getJestMockFunction();
45
- mockFitView.mockReturnValue(true);
46
76
  const mockSetQuery: MockFunction = getJestMockFunction();
47
77
  mockSetQuery.mockImplementation((values: Record<string, string | null>) => {
48
78
  const url: URL = new URL(window.location.href);
@@ -59,6 +89,12 @@ let mockStatuses: Map<string, ServiceOperationalStatus> = new Map<
59
89
  string,
60
90
  ServiceOperationalStatus
61
91
  >();
92
+ /*
93
+ * While set, the operational overlay answers only once this settles, so a
94
+ * test can have the cards drawn (and measured) before the overlay arrives
95
+ * and draws them again.
96
+ */
97
+ let mockStatusesReady: Promise<void> | null = null;
62
98
  /*
63
99
  * Every drawer instance, by the entity it was mounted for. The map keeps one
64
100
  * drawer while the user moves between entities (a remount would replay the
@@ -68,6 +104,78 @@ const mockDrawerMounts: Array<string> = [];
68
104
  /* The props of the drawer as last rendered. */
69
105
  let mockDrawerProps: Record<string, unknown> | null = null;
70
106
 
107
+ /* What the map hands React Flow, as far as these tests look at it. */
108
+ interface MockFlowProps {
109
+ nodes: Array<Node>;
110
+ edges: Array<Edge>;
111
+ children?: React.ReactNode;
112
+ translateExtent?: FlowExtent;
113
+ minZoom?: number;
114
+ maxZoom?: number;
115
+ zoomOnScroll?: boolean;
116
+ zoomOnPinch?: boolean;
117
+ panOnScroll?: boolean;
118
+ preventScrolling?: boolean;
119
+ zoomOnDoubleClick?: boolean;
120
+ nodesDraggable?: boolean;
121
+ fitView?: boolean;
122
+ onInit?: (instance: ReactFlowInstance) => void;
123
+ onNodesChange: (changes: Array<NodeChange>) => void;
124
+ /*
125
+ * React Flow reports a pan or zoom by hand (one with a source event) as it
126
+ * starts, and again as it ends if the transform changed at all. A move it
127
+ * makes itself (a fit, a zoom button, setViewport) reports neither.
128
+ */
129
+ onMoveStart: (event: MouseEvent | TouchEvent, viewport: Viewport) => void;
130
+ onMoveEnd: (event: MouseEvent | TouchEvent, viewport: Viewport) => void;
131
+ onNodeClick: (event: React.MouseEvent, node: Node) => void;
132
+ onEdgeClick: (event: React.MouseEvent, edge: Edge) => void;
133
+ onEdgeMouseEnter: (event: React.MouseEvent, edge: Edge) => void;
134
+ onEdgeMouseLeave: (event: React.MouseEvent, edge: Edge) => void;
135
+ }
136
+
137
+ /* What the map hands React Flow's zoom and fit buttons. */
138
+ interface MockControlsProps {
139
+ showInteractive?: boolean;
140
+ fitViewOptions?: FitViewOptions;
141
+ onZoomIn: () => void;
142
+ onZoomOut: () => void;
143
+ onFitView: () => void;
144
+ }
145
+
146
+ /* The part of a React Flow instance the map uses. */
147
+ interface MockFlowInstance {
148
+ fitView: MockFunction;
149
+ viewportInitialized: boolean;
150
+ }
151
+
152
+ /* Every props object React Flow was rendered with, oldest first. */
153
+ const mockFlowRenders: Array<MockFlowProps> = [];
154
+ /* How many times React Flow was mounted: each one draws the canvas afresh. */
155
+ let mockFlowMounts: number = 0;
156
+ /* The props of React Flow's controls as last rendered. */
157
+ let mockControlsProps: MockControlsProps | null = null;
158
+ /* The props of the viewport guard as last rendered. */
159
+ let mockGuardProps: FlowViewportGuardProps | null = null;
160
+ /* One instance per mount of the viewport guard, oldest first. */
161
+ const mockFlowInstances: Array<MockFlowInstance> = [];
162
+ /*
163
+ * Whether a newly mounted canvas has a usable viewport. The real guard
164
+ * reports no instance until it has one.
165
+ */
166
+ let mockViewportInitialized: boolean = true;
167
+ /*
168
+ * What document.visibilityState says while a test stands in for the tab
169
+ * (see setTabVisibility), or null while jsdom's own getter answers: jsdom's
170
+ * tab is always visible.
171
+ */
172
+ let mockTabVisibility: DocumentVisibilityState | null = null;
173
+ /*
174
+ * Hands jsdom back what a test stood in for with standInForLayout, run by
175
+ * afterEach, last first.
176
+ */
177
+ const mockLayoutRestores: Array<() => void> = [];
178
+
71
179
  jest.mock("../../../UI/Utils/Translation", () => {
72
180
  return {
73
181
  __esModule: true,
@@ -104,6 +212,9 @@ jest.mock(
104
212
  () => {
105
213
  return {
106
214
  fetchServiceOperationalStatuses: async () => {
215
+ if (mockStatusesReady) {
216
+ await mockStatusesReady;
217
+ }
107
218
  return mockStatuses;
108
219
  },
109
220
  };
@@ -278,6 +389,46 @@ jest.mock(
278
389
  };
279
390
  },
280
391
  );
392
+ /*
393
+ * The viewport guard, keeping the real guard's side of its contract with
394
+ * the map: it reports the live React Flow instance once the viewport is
395
+ * ready (none until then) and, on unmount, withdraws it and reports the
396
+ * drawing in view and the pan extent unbounded. Each mount reports an
397
+ * instance of its own, so a test can tell the canvas on screen from one
398
+ * that is gone.
399
+ */
400
+ jest.mock(
401
+ "../../../../App/FeatureSet/Dashboard/src/Components/Topology/FlowViewportGuard",
402
+ () => {
403
+ return {
404
+ __esModule: true,
405
+ default: (props: FlowViewportGuardProps) => {
406
+ mockGuardProps = props;
407
+ const latest: React.MutableRefObject<FlowViewportGuardProps> =
408
+ React.useRef<FlowViewportGuardProps>(props);
409
+ latest.current = props;
410
+ React.useEffect(() => {
411
+ const instance: MockFlowInstance = {
412
+ fitView: getJestMockFunction().mockReturnValue(true),
413
+ viewportInitialized: mockViewportInitialized,
414
+ };
415
+ mockFlowInstances.push(instance);
416
+ latest.current.onInstance(
417
+ instance.viewportInitialized
418
+ ? (instance as unknown as ReactFlowInstance)
419
+ : null,
420
+ );
421
+ return () => {
422
+ latest.current.onInstance(null);
423
+ latest.current.onDrawingInViewChange(true);
424
+ latest.current.onExtentChange(UNBOUNDED_FLOW_EXTENT);
425
+ };
426
+ }, []);
427
+ return <></>;
428
+ },
429
+ };
430
+ },
431
+ );
281
432
  jest.mock("reactflow", () => {
282
433
  return {
283
434
  __esModule: true,
@@ -286,107 +437,72 @@ jest.mock("reactflow", () => {
286
437
  Background: () => {
287
438
  return null;
288
439
  },
289
- Controls: () => {
440
+ Controls: (props: MockControlsProps) => {
441
+ mockControlsProps = props;
290
442
  return null;
291
443
  },
292
444
  Handle: () => {
293
445
  return null;
294
446
  },
295
447
  Position: { Left: "left", Right: "right", Top: "top", Bottom: "bottom" },
296
- default: (props: {
297
- nodes: Array<{
298
- id: string;
299
- position: { x: number; y: number };
300
- data: { label: string; dimmed: boolean; footer?: string };
301
- }>;
302
- edges: Array<{
303
- id: string;
304
- label?: string;
305
- animated?: boolean;
306
- labelShowBg?: boolean;
307
- labelStyle?: React.CSSProperties;
308
- labelBgStyle?: React.CSSProperties;
309
- labelBgPadding?: [number, number];
310
- labelBgBorderRadius?: number;
311
- }>;
312
- onInit: (value: unknown) => void;
313
- onNodeClick: (event: React.MouseEvent, node: unknown) => void;
314
- onEdgeClick: (event: React.MouseEvent, edge: unknown) => void;
315
- onEdgeMouseEnter: (event: React.MouseEvent, edge: unknown) => void;
316
- onEdgeMouseLeave: () => void;
317
- }) => {
448
+ default: (props: MockFlowProps) => {
449
+ mockFlowRenders.push(props);
318
450
  React.useEffect(() => {
319
- props.onInit({ fitView: mockFitView });
451
+ mockFlowMounts += 1;
320
452
  }, []);
321
453
  return (
322
454
  <div data-testid="rendered-graph">
323
- {props.nodes.map(
324
- (node: {
325
- id: string;
326
- position: { x: number; y: number };
327
- data: { label: string; dimmed: boolean; footer?: string };
328
- }) => {
329
- return (
330
- <button
331
- key={node.id}
332
- data-testid={`map-node-${node.id}`}
333
- data-context={String(node.data.dimmed)}
334
- data-x={String(node.position.x)}
335
- data-footer={node.data.footer || ""}
336
- onClick={(event: React.MouseEvent) => {
337
- props.onNodeClick(event, node);
338
- }}
339
- >
340
- {node.data.label}
341
- </button>
342
- );
343
- },
344
- )}
345
- {props.edges.map(
346
- (edge: {
347
- id: string;
348
- label?: string;
349
- animated?: boolean;
350
- labelShowBg?: boolean;
351
- labelStyle?: React.CSSProperties;
352
- labelBgStyle?: React.CSSProperties;
353
- labelBgPadding?: [number, number];
354
- labelBgBorderRadius?: number;
355
- }) => {
356
- return (
357
- <button
358
- key={edge.id}
359
- data-testid={`map-edge-${edge.id}`}
360
- data-animated={String(edge.animated)}
361
- data-label-show-background={String(edge.labelShowBg)}
362
- data-label-color={String(edge.labelStyle?.fill || "")}
363
- data-label-background={String(edge.labelBgStyle?.fill || "")}
364
- data-label-background-opacity={String(
365
- edge.labelBgStyle?.fillOpacity || "",
366
- )}
367
- data-label-border={String(edge.labelBgStyle?.stroke || "")}
368
- data-label-border-width={String(
369
- edge.labelBgStyle?.strokeWidth || "",
370
- )}
371
- data-label-padding={edge.labelBgPadding?.join(",") || ""}
372
- data-label-border-radius={String(
373
- edge.labelBgBorderRadius || "",
374
- )}
375
- onClick={(event: React.MouseEvent) => {
376
- props.onEdgeClick(event, edge);
377
- }}
378
- onMouseEnter={(event: React.MouseEvent) => {
379
- props.onEdgeMouseEnter(event, edge);
380
- }}
381
- onMouseLeave={() => {
382
- props.onEdgeMouseLeave();
383
- }}
384
- >
385
- {edge.label || "Connection"}
386
- </button>
387
- );
388
- },
389
- )}
455
+ {props.nodes.map((node: Node) => {
456
+ return (
457
+ <button
458
+ key={node.id}
459
+ data-testid={`map-node-${node.id}`}
460
+ data-context={String(node.data.dimmed)}
461
+ data-x={String(node.position.x)}
462
+ data-footer={node.data.footer || ""}
463
+ onClick={(event: React.MouseEvent) => {
464
+ props.onNodeClick(event, node);
465
+ }}
466
+ >
467
+ {node.data.label}
468
+ </button>
469
+ );
470
+ })}
471
+ {props.edges.map((edge: Edge) => {
472
+ return (
473
+ <button
474
+ key={edge.id}
475
+ data-testid={`map-edge-${edge.id}`}
476
+ data-animated={String(edge.animated)}
477
+ data-label-show-background={String(edge.labelShowBg)}
478
+ data-label-color={String(edge.labelStyle?.fill || "")}
479
+ data-label-background={String(edge.labelBgStyle?.fill || "")}
480
+ data-label-background-opacity={String(
481
+ edge.labelBgStyle?.fillOpacity || "",
482
+ )}
483
+ data-label-border={String(edge.labelBgStyle?.stroke || "")}
484
+ data-label-border-width={String(
485
+ edge.labelBgStyle?.strokeWidth || "",
486
+ )}
487
+ data-label-padding={edge.labelBgPadding?.join(",") || ""}
488
+ data-label-border-radius={String(
489
+ edge.labelBgBorderRadius || "",
490
+ )}
491
+ onClick={(event: React.MouseEvent) => {
492
+ props.onEdgeClick(event, edge);
493
+ }}
494
+ onMouseEnter={(event: React.MouseEvent) => {
495
+ props.onEdgeMouseEnter(event, edge);
496
+ }}
497
+ onMouseLeave={(event: React.MouseEvent) => {
498
+ props.onEdgeMouseLeave(event, edge);
499
+ }}
500
+ >
501
+ {edge.label || "Connection"}
502
+ </button>
503
+ );
504
+ })}
505
+ {props.children}
390
506
  </div>
391
507
  );
392
508
  },
@@ -507,17 +623,309 @@ function drawerTarget(): EntityDetailTarget {
507
623
  return mockDrawerProps!["entity"] as EntityDetailTarget;
508
624
  }
509
625
 
626
+ /* How every fit of the map frames it: the guard, the toolbar, the controls. */
627
+ const MAP_FRAMING: FitViewOptions = { padding: 0.18, maxZoom: 1 };
628
+ /* Fit to screen: the same framing, animated. */
629
+ const ANIMATED_FIT: FitViewOptions = {
630
+ padding: 0.18,
631
+ maxZoom: 1,
632
+ duration: 300,
633
+ };
634
+
635
+ /* The props React Flow was last rendered with. */
636
+ function flowProps(): MockFlowProps {
637
+ const latest: MockFlowProps | undefined =
638
+ mockFlowRenders[mockFlowRenders.length - 1];
639
+ if (!latest) {
640
+ throw new Error("React Flow was never rendered");
641
+ }
642
+ return latest;
643
+ }
644
+
645
+ /* The props the viewport guard was last rendered with. */
646
+ function guardProps(): FlowViewportGuardProps {
647
+ if (!mockGuardProps) {
648
+ throw new Error("The viewport guard was never rendered");
649
+ }
650
+ return mockGuardProps;
651
+ }
652
+
653
+ /* The props React Flow's controls were last rendered with. */
654
+ function controlsProps(): MockControlsProps {
655
+ if (!mockControlsProps) {
656
+ throw new Error("React Flow's controls were never rendered");
657
+ }
658
+ return mockControlsProps;
659
+ }
660
+
661
+ /* The instance of the guard mounted `index`-th, reported or withheld. */
662
+ function flowInstance(index: number): MockFlowInstance {
663
+ const instance: MockFlowInstance | undefined = mockFlowInstances[index];
664
+ if (!instance) {
665
+ throw new Error(`No viewport guard was mounted at #${index}`);
666
+ }
667
+ return instance;
668
+ }
669
+
670
+ /* The instance the guard of the canvas on screen reported. */
671
+ function liveInstance(): MockFlowInstance {
672
+ const instance: MockFlowInstance = flowInstance(mockFlowInstances.length - 1);
673
+ expect(instance.viewportInitialized).toBe(true);
674
+ return instance;
675
+ }
676
+
677
+ function nodeById(nodes: Array<Node>, id: string): Node {
678
+ const node: Node | undefined = nodes.find((candidate: Node): boolean => {
679
+ return candidate.id === id;
680
+ });
681
+ if (!node) {
682
+ throw new Error(`No node ${id} was drawn`);
683
+ }
684
+ return node;
685
+ }
686
+
687
+ function edgeById(edges: Array<Edge>, id: string): Edge {
688
+ const found: Edge | undefined = edges.find((candidate: Edge): boolean => {
689
+ return candidate.id === id;
690
+ });
691
+ if (!found) {
692
+ throw new Error(`No edge ${id} was drawn`);
693
+ }
694
+ return found;
695
+ }
696
+
697
+ function idsOf(nodes: Array<Node>): Array<string> {
698
+ return nodes.map((node: Node): string => {
699
+ return node.id;
700
+ });
701
+ }
702
+
703
+ /* The guard's report of whether any of the drawing is on the canvas. */
704
+ function reportDrawingInView(inView: boolean): void {
705
+ act(() => {
706
+ guardProps().onDrawingInViewChange(inView);
707
+ });
708
+ }
709
+
710
+ /* Let time pass on the fake clock. */
711
+ function advance(ms: number): void {
712
+ act(() => {
713
+ jest.advanceTimersByTime(ms);
714
+ });
715
+ }
716
+
717
+ function outOfViewNotice(): HTMLElement | null {
718
+ return screen.queryByTestId("service-map-out-of-view");
719
+ }
720
+
721
+ /* The out-of-view notice's own box: its message and its button. */
722
+ function outOfViewNoticeBox(): HTMLElement {
723
+ return within(screen.getByTestId("service-map-out-of-view")).getByRole(
724
+ "status",
725
+ );
726
+ }
727
+
728
+ /*
729
+ * The page, as far as placing the out-of-view notice goes. jsdom lays
730
+ * nothing out: every box it measures is empty, and its window is always 768
731
+ * pixels high.
732
+ */
733
+ interface PageLayout {
734
+ /* The canvas's top, down from the window's: negative once scrolled past. */
735
+ canvasTop: number;
736
+ canvasHeight: number;
737
+ /* The window's height (window.innerHeight). */
738
+ windowHeight: number;
739
+ /* The notice's own height, once it is drawn. */
740
+ noticeHeight: number;
741
+ }
742
+
743
+ /* The notice's box, for the stand-in for its height. */
744
+ const OUT_OF_VIEW_NOTICE_BOX: string =
745
+ '[data-testid="service-map-out-of-view"] > [role="status"]';
746
+
747
+ /*
748
+ * Lay the page out as `page` says, for the rest of the test. The canvas's
749
+ * box, the window's height and the notice's height are read from it each
750
+ * time they are measured, so changing it lays the page out anew. Returns
751
+ * the canvas's measurements, one call each.
752
+ */
753
+ function standInForLayout(page: PageLayout): MockFunction {
754
+ const measureCanvas: MockFunction = getJestMockFunction().mockImplementation(
755
+ (): DOMRect => {
756
+ const box: Omit<DOMRect, "toJSON"> = {
757
+ x: 0,
758
+ y: page.canvasTop,
759
+ left: 0,
760
+ top: page.canvasTop,
761
+ right: 1024,
762
+ bottom: page.canvasTop + page.canvasHeight,
763
+ width: 1024,
764
+ height: page.canvasHeight,
765
+ };
766
+ return {
767
+ ...box,
768
+ toJSON: (): Omit<DOMRect, "toJSON"> => {
769
+ return box;
770
+ },
771
+ };
772
+ },
773
+ );
774
+ // The canvas goes at cleanup, and its stand-in with it.
775
+ screen.getByTestId("service-map-canvas").getBoundingClientRect =
776
+ measureCanvas;
777
+
778
+ const jsdomWindowHeight: PropertyDescriptor | undefined =
779
+ Object.getOwnPropertyDescriptor(window, "innerHeight");
780
+ Object.defineProperty(window, "innerHeight", {
781
+ configurable: true,
782
+ get: (): number => {
783
+ return page.windowHeight;
784
+ },
785
+ });
786
+ const jsdomOffsetHeight: PropertyDescriptor | undefined =
787
+ Object.getOwnPropertyDescriptor(HTMLElement.prototype, "offsetHeight");
788
+ Object.defineProperty(HTMLElement.prototype, "offsetHeight", {
789
+ configurable: true,
790
+ enumerable: true,
791
+ get(this: HTMLElement): number {
792
+ if (this.matches(OUT_OF_VIEW_NOTICE_BOX)) {
793
+ return page.noticeHeight;
794
+ }
795
+ return jsdomOffsetHeight?.get
796
+ ? Number(jsdomOffsetHeight.get.call(this))
797
+ : 0;
798
+ },
799
+ });
800
+
801
+ mockLayoutRestores.push((): void => {
802
+ if (jsdomWindowHeight) {
803
+ Object.defineProperty(window, "innerHeight", jsdomWindowHeight);
804
+ } else {
805
+ Reflect.deleteProperty(window, "innerHeight");
806
+ }
807
+ if (jsdomOffsetHeight) {
808
+ Object.defineProperty(
809
+ HTMLElement.prototype,
810
+ "offsetHeight",
811
+ jsdomOffsetHeight,
812
+ );
813
+ } else {
814
+ Reflect.deleteProperty(HTMLElement.prototype, "offsetHeight");
815
+ }
816
+ });
817
+ return measureCanvas;
818
+ }
819
+
820
+ /* The page scrolling: the document, or an element the page scrolls in. */
821
+ function scrollPage(scroller: Document | HTMLElement = document): void {
822
+ fireEvent.scroll(scroller);
823
+ }
824
+
825
+ /* The browser window being resized. */
826
+ function resizeWindow(): void {
827
+ fireEvent(window, new Event("resize"));
828
+ }
829
+
830
+ /* The browser drawing its next frame: one every 16 ms on the fake clock. */
831
+ function nextFrame(): void {
832
+ advance(16);
833
+ }
834
+
835
+ /*
836
+ * A view the automatic framing left a large map in, as React Flow reported
837
+ * it in a browser: the whole drawing fitted at an eighth of its size, where
838
+ * the pan extent pins it.
839
+ */
840
+ const FRAMED_VIEW: Viewport = { x: 720.3138, y: 67.1186, zoom: 0.1269 };
841
+ /*
842
+ * The same view as React Flow reports it after a press on it: d3-zoom
843
+ * re-applies the pan extent on every pointer move, and the view comes back
844
+ * a rounding error from where it was.
845
+ */
846
+ const FRAMED_VIEW_AFTER_PRESS: Viewport = {
847
+ x: 720.3138,
848
+ y: 67.11864406779664,
849
+ zoom: 0.1269,
850
+ };
851
+ /*
852
+ * A view the automatic framing left a smaller map in: drawn at full size,
853
+ * where the pan extent leaves the view a little room, so the pointer's slip
854
+ * during a click pans it. (Its coordinates are exact in binary, so a slip of
855
+ * exactly 4 px is 4 px, not a rounding error either side of it.)
856
+ */
857
+ const SMALL_MAP_VIEW: Viewport = { x: 296.5, y: 104.25, zoom: 1 };
858
+
859
+ /* React Flow reporting that a pan or zoom by hand began, and where. */
860
+ function startGesture(at: Viewport): void {
861
+ act(() => {
862
+ flowProps().onMoveStart(new MouseEvent("mousedown"), at);
863
+ });
864
+ }
865
+
866
+ /* React Flow reporting where a pan or zoom by hand left the view. */
867
+ function endGesture(at: Viewport): void {
868
+ act(() => {
869
+ flowProps().onMoveEnd(new MouseEvent("mouseup"), at);
870
+ });
871
+ }
872
+
873
+ /* A whole pan or zoom by hand: where it began, then where it came to rest. */
874
+ function gesture(from: Viewport, to: Viewport): void {
875
+ startGesture(from);
876
+ endGesture(to);
877
+ }
878
+
879
+ /*
880
+ * Put the page in a tab that is hidden or seen, without telling it so. The
881
+ * getter shadows jsdom's (on Document.prototype) until afterEach hands the
882
+ * document its own back.
883
+ */
884
+ function setTabVisibility(state: DocumentVisibilityState): void {
885
+ if (mockTabVisibility === null) {
886
+ Object.defineProperty(document, "visibilityState", {
887
+ configurable: true,
888
+ get: (): DocumentVisibilityState => {
889
+ return mockTabVisibility || "visible";
890
+ },
891
+ });
892
+ }
893
+ mockTabVisibility = state;
894
+ }
895
+
896
+ /* The browser hiding or showing the tab, and telling the page so. */
897
+ function changeTabVisibility(state: DocumentVisibilityState): void {
898
+ setTabVisibility(state);
899
+ act(() => {
900
+ document.dispatchEvent(new Event("visibilitychange"));
901
+ });
902
+ }
903
+
510
904
  beforeEach(() => {
511
905
  window.history.replaceState({}, "", "/");
512
906
  mockStatuses = new Map<string, ServiceOperationalStatus>();
907
+ mockStatusesReady = null;
513
908
  mockSetQuery.mockClear();
514
- mockFitView.mockClear();
515
909
  mockDrawerMounts.length = 0;
516
910
  mockDrawerProps = null;
911
+ mockFlowRenders.length = 0;
912
+ mockFlowMounts = 0;
913
+ mockControlsProps = null;
914
+ mockGuardProps = null;
915
+ mockFlowInstances.length = 0;
916
+ mockViewportInitialized = true;
517
917
  });
518
918
  afterEach(() => {
519
919
  cleanup();
520
920
  jest.useRealTimers();
921
+ if (mockTabVisibility !== null) {
922
+ // Drop the shadowing getter: jsdom's own, on the prototype, answers again.
923
+ Reflect.deleteProperty(document, "visibilityState");
924
+ mockTabVisibility = null;
925
+ }
926
+ for (const restore of mockLayoutRestores.splice(0).reverse()) {
927
+ restore();
928
+ }
521
929
  });
522
930
 
523
931
  describe("first impression", () => {
@@ -828,22 +1236,39 @@ describe("narrowing the map", () => {
828
1236
  fireEvent.change(screen.getByRole("textbox", { name: "Search services" }), {
829
1237
  target: { value: "web" },
830
1238
  });
831
- mockFitView.mockClear();
1239
+ const webDrawing: string = guardProps().drawingKey;
1240
+ expect(webDrawing.split("|").sort()).toEqual(["api", "web"]);
832
1241
  fireEvent.change(screen.getByRole("textbox", { name: "Search services" }), {
833
1242
  target: { value: "postgres" },
834
1243
  });
835
- expect(mockFitView).toHaveBeenCalled();
1244
+ /*
1245
+ * The guard frames the drawing again whenever the one it is handed
1246
+ * changes, once React Flow holds it: the key names exactly the cards
1247
+ * React Flow was given, in order.
1248
+ */
1249
+ expect(guardProps().drawingKey).not.toBe(webDrawing);
1250
+ expect(guardProps().drawingKey).toBe(idsOf(flowProps().nodes).join("|"));
1251
+ expect(guardProps().drawingKey.split("|").sort()).toEqual([
1252
+ "api",
1253
+ "postgres",
1254
+ ]);
1255
+ expect(guardProps().fitViewOptions).toEqual(MAP_FRAMING);
1256
+ // The canvas on screen re-frames; it is not drawn again from scratch.
1257
+ expect(mockFlowMounts).toBe(1);
836
1258
  });
837
1259
 
838
1260
  test("fit to screen recovers the viewport", async () => {
839
1261
  await renderGraph();
840
- mockFitView.mockClear();
1262
+ const instance: MockFlowInstance = liveInstance();
841
1263
  fireEvent.click(screen.getByRole("button", { name: "Fit to screen" }));
842
- expect(mockFitView).toHaveBeenCalledWith({
1264
+ expect(instance.fitView).toHaveBeenCalledTimes(1);
1265
+ expect(instance.fitView).toHaveBeenCalledWith({
843
1266
  padding: 0.18,
844
1267
  maxZoom: 1,
845
1268
  duration: 300,
846
1269
  });
1270
+ // It fitted, so the canvas is left as it is.
1271
+ expect(mockFlowMounts).toBe(1);
847
1272
  });
848
1273
  });
849
1274
 
@@ -1269,3 +1694,717 @@ describe("table", () => {
1269
1694
  );
1270
1695
  });
1271
1696
  });
1697
+
1698
+ /*
1699
+ * Issue #4117: the map went blank at random and only a page reload brought
1700
+ * it back. Hovering a connection handed React Flow a new array of cards
1701
+ * without their sizes, which could leave every card unmeasured (hidden) for
1702
+ * good; and the wheel, a double-click or a dragged card could carry the
1703
+ * drawing off the canvas with nothing to bring it back. The map now builds
1704
+ * cards only when they change and hands back the sizes React Flow measured,
1705
+ * leaves the wheel to the page, holds the view to the drawing the guard
1706
+ * measured, says when none of the drawing is in view, and draws the canvas
1707
+ * again when a fit cannot bring it back.
1708
+ *
1709
+ * The view stays the automatic framing, which the guard fits again when the
1710
+ * canvas is resized, until a gesture really moves it: a press on a map the
1711
+ * pan extent pins in place comes back a rounding error away, and React Flow
1712
+ * reports that as a move. The out-of-view notice sits at the canvas's
1713
+ * top-left, and a map drawn in a background tab (where nothing is measured)
1714
+ * is judged only once the tab is seen.
1715
+ */
1716
+ describe("keeping the map on screen (issue #4117)", () => {
1717
+ test("the wheel scrolls the page, a double-click does not zoom and cards cannot be dragged away", async () => {
1718
+ await renderGraph();
1719
+ const flow: MockFlowProps = flowProps();
1720
+ expect(flow.zoomOnScroll).toBe(false);
1721
+ expect(flow.preventScrolling).toBe(false);
1722
+ expect(flow.panOnScroll).not.toBe(true);
1723
+ // React Flow reads Ctrl + wheel as a pinch, so both still zoom.
1724
+ expect(flow.zoomOnPinch).not.toBe(false);
1725
+ expect(flow.zoomOnDoubleClick).toBe(false);
1726
+ // Holding a card past the edge used to pan the whole map away.
1727
+ expect(flow.nodesDraggable).toBe(false);
1728
+ // Framing is the guard's, once React Flow has measured the drawing.
1729
+ expect(flow.fitView).toBeUndefined();
1730
+ expect(flow.onInit).toBeUndefined();
1731
+ });
1732
+
1733
+ test("hovering or selecting a connection redraws connections, never the cards", async () => {
1734
+ await renderGraph();
1735
+ const cards: Array<Node> = flowProps().nodes;
1736
+ const connections: Array<Edge> = flowProps().edges;
1737
+ const rendersBefore: number = mockFlowRenders.length;
1738
+
1739
+ fireEvent.mouseEnter(screen.getByTestId("map-edge-web->api"));
1740
+ expect(screen.getByTestId("map-edge-web->api")).toHaveTextContent(
1741
+ "600/min",
1742
+ );
1743
+ expect(flowProps().edges).not.toBe(connections);
1744
+ fireEvent.mouseLeave(screen.getByTestId("map-edge-web->api"));
1745
+ expect(screen.getByTestId("map-edge-web->api")).toHaveTextContent(
1746
+ "Connection",
1747
+ );
1748
+
1749
+ fireEvent.click(screen.getByTestId("map-edge-api->postgres"));
1750
+ expect(
1751
+ screen.getByRole("dialog", { name: "Connection details" }),
1752
+ ).toBeInTheDocument();
1753
+ // The selected connection is drawn thicker...
1754
+ expect(
1755
+ Number(edgeById(flowProps().edges, "api->postgres").style?.strokeWidth),
1756
+ ).toBeGreaterThan(
1757
+ Number(edgeById(connections, "api->postgres").style?.strokeWidth),
1758
+ );
1759
+
1760
+ fireEvent.change(
1761
+ screen.getByRole("combobox", { name: "Connection labels" }),
1762
+ { target: { value: "latency" } },
1763
+ );
1764
+ expect(screen.getByTestId("map-edge-api->postgres")).toHaveTextContent(
1765
+ "25ms",
1766
+ );
1767
+
1768
+ // ...but not one of these renders handed React Flow new cards.
1769
+ const renders: Array<MockFlowProps> = mockFlowRenders.slice(rendersBefore);
1770
+ expect(renders.length).toBeGreaterThanOrEqual(4);
1771
+ for (const flowRender of renders) {
1772
+ expect(flowRender.nodes).toBe(cards);
1773
+ }
1774
+ });
1775
+
1776
+ test("sizes React Flow measured are handed back on every card drawn again", async () => {
1777
+ await renderGraph();
1778
+ const unmeasured: Array<Node> = flowProps().nodes;
1779
+ expect(nodeById(unmeasured, "web")).not.toHaveProperty("width");
1780
+ act(() => {
1781
+ flowProps().onNodesChange([
1782
+ {
1783
+ id: "web",
1784
+ type: "dimensions",
1785
+ dimensions: { width: 256, height: 110 },
1786
+ },
1787
+ {
1788
+ id: "api",
1789
+ type: "dimensions",
1790
+ dimensions: { width: 256, height: 128 },
1791
+ },
1792
+ // A card that grew is measured again; the latest size is the one kept.
1793
+ {
1794
+ id: "api",
1795
+ type: "dimensions",
1796
+ dimensions: { width: 256, height: 146 },
1797
+ },
1798
+ ]);
1799
+ });
1800
+
1801
+ // Selecting a card draws every card again.
1802
+ fireEvent.click(screen.getByTestId("map-node-api"));
1803
+ const redrawn: Array<Node> = flowProps().nodes;
1804
+ expect(redrawn).not.toBe(unmeasured);
1805
+ expect(nodeById(redrawn, "api").data.selected).toBe(true);
1806
+ expect(nodeById(redrawn, "web")).toMatchObject({ width: 256, height: 110 });
1807
+ expect(nodeById(redrawn, "api")).toMatchObject({ width: 256, height: 146 });
1808
+ // A card React Flow has not measured yet is left for it to measure.
1809
+ expect(nodeById(redrawn, "postgres")).not.toHaveProperty("width");
1810
+ expect(nodeById(redrawn, "postgres")).not.toHaveProperty("height");
1811
+ });
1812
+
1813
+ test("cards drawn again when the incident overlay arrives keep their measured sizes", async () => {
1814
+ let releaseOverlay: () => void = (): void => {};
1815
+ mockStatusesReady = new Promise<void>((resolve: () => void): void => {
1816
+ releaseOverlay = resolve;
1817
+ });
1818
+ mockStatuses.set("api", {
1819
+ serviceId: "api-id",
1820
+ activeIncidentCount: 2,
1821
+ worstIncidentSeverityName: "Critical",
1822
+ worstIncidentSeverityColor: "#dc2626",
1823
+ incidents: [],
1824
+ activeAlertCount: 0,
1825
+ worstAlertSeverityName: null,
1826
+ worstAlertSeverityColor: null,
1827
+ alerts: [],
1828
+ });
1829
+ await renderGraph();
1830
+ const drawn: Array<Node> = flowProps().nodes;
1831
+ expect(nodeById(drawn, "api").data.entry.incidentCount).toBe(0);
1832
+ act(() => {
1833
+ flowProps().onNodesChange(
1834
+ drawn.map((node: Node): NodeChange => {
1835
+ return {
1836
+ id: node.id,
1837
+ type: "dimensions",
1838
+ dimensions: { width: 256, height: 128 },
1839
+ };
1840
+ }),
1841
+ );
1842
+ });
1843
+
1844
+ // The overlay lands just after React Flow measured the cards.
1845
+ await act(async () => {
1846
+ releaseOverlay();
1847
+ });
1848
+ const redrawn: Array<Node> = flowProps().nodes;
1849
+ expect(redrawn).not.toBe(drawn);
1850
+ expect(nodeById(redrawn, "api").data.entry.incidentCount).toBe(2);
1851
+ expect(idsOf(redrawn)).toEqual(idsOf(drawn));
1852
+ for (const node of redrawn) {
1853
+ expect(node).toMatchObject({ width: 256, height: 128 });
1854
+ }
1855
+ });
1856
+
1857
+ test("only a real measured size is kept: other changes and empty sizes are not", async () => {
1858
+ await renderGraph();
1859
+ const before: Array<Node> = flowProps().nodes;
1860
+ act(() => {
1861
+ flowProps().onNodesChange([
1862
+ { id: "web", type: "position", position: { x: 40, y: 40 } },
1863
+ { id: "web", type: "select", selected: true },
1864
+ { id: "api", type: "dimensions" },
1865
+ {
1866
+ id: "api",
1867
+ type: "dimensions",
1868
+ dimensions: { width: 0, height: 0 },
1869
+ },
1870
+ {
1871
+ id: "postgres",
1872
+ type: "dimensions",
1873
+ dimensions: { width: 256, height: 0 },
1874
+ },
1875
+ { id: "postgres", type: "remove" },
1876
+ ]);
1877
+ });
1878
+ fireEvent.click(screen.getByTestId("map-node-web"));
1879
+ expect(flowProps().nodes).not.toBe(before);
1880
+ for (const node of flowProps().nodes) {
1881
+ expect(node).not.toHaveProperty("width");
1882
+ expect(node).not.toHaveProperty("height");
1883
+ }
1884
+ });
1885
+
1886
+ test("the view pans within the drawing the guard measured, and a new canvas starts unbounded", async () => {
1887
+ await renderGraph();
1888
+ // Until the guard has measured the drawing there is nothing to hold to.
1889
+ expect(flowProps().translateExtent).toEqual(UNBOUNDED_FLOW_EXTENT);
1890
+ expect(guardProps().panMargin).toBe(SERVICE_MAP_PAN_MARGIN);
1891
+ /*
1892
+ * The slack around the drawing is less than the least the canvas ever
1893
+ * shows, so the view cannot come to rest in it with no card in sight.
1894
+ */
1895
+ const canvasHeight: number = parseFloat(
1896
+ screen.getByTestId("service-map-canvas").style.height,
1897
+ );
1898
+ expect(SERVICE_MAP_PAN_MARGIN).toBeGreaterThan(0);
1899
+ expect(SERVICE_MAP_PAN_MARGIN).toBeLessThan(
1900
+ canvasHeight / (flowProps().maxZoom || 1),
1901
+ );
1902
+
1903
+ const measured: FlowExtent = [
1904
+ [-128, -128],
1905
+ [1060, 404],
1906
+ ];
1907
+ act(() => {
1908
+ guardProps().onExtentChange(measured);
1909
+ });
1910
+ expect(flowProps().translateExtent).toBe(measured);
1911
+
1912
+ fireEvent.click(screen.getByTestId("service-map-view-list"));
1913
+ const rendersBefore: number = mockFlowRenders.length;
1914
+ fireEvent.click(screen.getByTestId("service-map-view-map"));
1915
+ expect(mockFlowMounts).toBe(2);
1916
+ /*
1917
+ * The old drawing's extent must not hold the new canvas before its
1918
+ * guard has measured the new drawing.
1919
+ */
1920
+ expect(mockFlowRenders[rendersBefore]?.translateExtent).toEqual(
1921
+ UNBOUNDED_FLOW_EXTENT,
1922
+ );
1923
+ expect(flowProps().translateExtent).toEqual(UNBOUNDED_FLOW_EXTENT);
1924
+ });
1925
+
1926
+ test("moving the view by hand ends the automatic framing; fitting restores it", async () => {
1927
+ await renderGraph();
1928
+ const autoFrame: React.MutableRefObject<boolean> = guardProps().autoFrame;
1929
+ expect(autoFrame.current).toBe(true);
1930
+
1931
+ // A pan or zoom by hand: a resized canvas no longer re-frames the map.
1932
+ gesture(FRAMED_VIEW, { x: 120, y: -40, zoom: 0.6 });
1933
+ expect(autoFrame.current).toBe(false);
1934
+ fireEvent.click(screen.getByTestId("service-map-fit"));
1935
+ expect(autoFrame.current).toBe(true);
1936
+
1937
+ act(() => {
1938
+ controlsProps().onZoomIn();
1939
+ });
1940
+ expect(autoFrame.current).toBe(false);
1941
+ act(() => {
1942
+ controlsProps().onFitView();
1943
+ });
1944
+ expect(autoFrame.current).toBe(true);
1945
+ act(() => {
1946
+ controlsProps().onZoomOut();
1947
+ });
1948
+ expect(autoFrame.current).toBe(false);
1949
+
1950
+ // Every fit frames the map the same way, whichever button asked.
1951
+ expect(controlsProps().fitViewOptions).toEqual(MAP_FRAMING);
1952
+ expect(controlsProps().showInteractive).toBe(false);
1953
+ expect(guardProps().fitViewOptions).toEqual(MAP_FRAMING);
1954
+ expect(guardProps().autoFrame).toBe(autoFrame);
1955
+ });
1956
+
1957
+ test("a press that leaves the view where it was keeps the automatic framing", async () => {
1958
+ await renderGraph();
1959
+ const autoFrame: React.MutableRefObject<boolean> = guardProps().autoFrame;
1960
+
1961
+ /*
1962
+ * A press, or a drag attempt, on a map the pan extent pins in place:
1963
+ * nothing moved, but React Flow reports the view a rounding error away.
1964
+ * Taking that for a move left the next narrower window with the drawing
1965
+ * off the canvas.
1966
+ */
1967
+ gesture(FRAMED_VIEW, FRAMED_VIEW_AFTER_PRESS);
1968
+ expect(autoFrame.current).toBe(true);
1969
+
1970
+ // Nor does a zoom change of 0.05%.
1971
+ gesture(FRAMED_VIEW, { ...FRAMED_VIEW, zoom: FRAMED_VIEW.zoom * 1.0005 });
1972
+ expect(autoFrame.current).toBe(true);
1973
+ });
1974
+
1975
+ test("the slip of a click, up to 4 px each way, keeps the automatic framing", async () => {
1976
+ await renderGraph();
1977
+ const autoFrame: React.MutableRefObject<boolean> = guardProps().autoFrame;
1978
+ /*
1979
+ * The pointer slips a pixel or two during a click, and where the pan
1980
+ * extent leaves the view room, the view slips with it. Taking that for
1981
+ * the user's move left the drawing clipped by the next narrower window.
1982
+ * The slop allowed is 4 px along each axis, the drag threshold operating
1983
+ * systems use.
1984
+ */
1985
+ const slips: Array<[string, Viewport]> = [
1986
+ ["a pixel right", { ...SMALL_MAP_VIEW, x: SMALL_MAP_VIEW.x + 1 }],
1987
+ ["a pixel up", { ...SMALL_MAP_VIEW, y: SMALL_MAP_VIEW.y - 1 }],
1988
+ ["4 px left", { ...SMALL_MAP_VIEW, x: SMALL_MAP_VIEW.x - 4 }],
1989
+ ["4 px down", { ...SMALL_MAP_VIEW, y: SMALL_MAP_VIEW.y + 4 }],
1990
+ [
1991
+ "4 px right and 4 px up",
1992
+ { ...SMALL_MAP_VIEW, x: SMALL_MAP_VIEW.x + 4, y: SMALL_MAP_VIEW.y - 4 },
1993
+ ],
1994
+ [
1995
+ "4 px left and 0.05% closer",
1996
+ {
1997
+ x: SMALL_MAP_VIEW.x - 4,
1998
+ y: SMALL_MAP_VIEW.y,
1999
+ zoom: SMALL_MAP_VIEW.zoom * 1.0005,
2000
+ },
2001
+ ],
2002
+ ];
2003
+ for (const [slip, to] of slips) {
2004
+ gesture(SMALL_MAP_VIEW, to);
2005
+ expect([slip, autoFrame.current]).toEqual([slip, true]);
2006
+ }
2007
+ });
2008
+
2009
+ test("a pan of more than 4 px or a zoom of 0.2% ends the automatic framing", async () => {
2010
+ await renderGraph();
2011
+ const autoFrame: React.MutableRefObject<boolean> = guardProps().autoFrame;
2012
+ const moves: Array<[string, Viewport]> = [
2013
+ ["4.1 px right", { ...SMALL_MAP_VIEW, x: SMALL_MAP_VIEW.x + 4.1 }],
2014
+ ["4.1 px left", { ...SMALL_MAP_VIEW, x: SMALL_MAP_VIEW.x - 4.1 }],
2015
+ ["4.1 px up", { ...SMALL_MAP_VIEW, y: SMALL_MAP_VIEW.y - 4.1 }],
2016
+ ["4.1 px down", { ...SMALL_MAP_VIEW, y: SMALL_MAP_VIEW.y + 4.1 }],
2017
+ ["0.2% closer", { ...SMALL_MAP_VIEW, zoom: SMALL_MAP_VIEW.zoom * 1.002 }],
2018
+ [
2019
+ "0.2% further",
2020
+ { ...SMALL_MAP_VIEW, zoom: SMALL_MAP_VIEW.zoom / 1.002 },
2021
+ ],
2022
+ ];
2023
+ for (const [move, to] of moves) {
2024
+ // Fit to screen hands the view back to the automatic framing.
2025
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2026
+ expect([move, autoFrame.current]).toEqual([move, true]);
2027
+ gesture(SMALL_MAP_VIEW, to);
2028
+ expect([move, autoFrame.current]).toEqual([move, false]);
2029
+ }
2030
+ });
2031
+
2032
+ test("a move reported without its start ends the automatic framing", async () => {
2033
+ await renderGraph();
2034
+ const autoFrame: React.MutableRefObject<boolean> = guardProps().autoFrame;
2035
+ // With nothing to compare it with, a move counts as a move, never as noise.
2036
+ endGesture(FRAMED_VIEW_AFTER_PRESS);
2037
+ expect(autoFrame.current).toBe(false);
2038
+ });
2039
+
2040
+ test("each gesture is judged from its own start, which is forgotten once it ends", async () => {
2041
+ await renderGraph();
2042
+ const autoFrame: React.MutableRefObject<boolean> = guardProps().autoFrame;
2043
+
2044
+ // React Flow reports no end for a press that changed nothing at all...
2045
+ startGesture({ x: 64, y: 32, zoom: 1 });
2046
+ // ...and none for a move of its own, such as a fit.
2047
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2048
+ expect(autoFrame.current).toBe(true);
2049
+
2050
+ // The next press is judged from where it began, not where the last did.
2051
+ gesture(FRAMED_VIEW, FRAMED_VIEW_AFTER_PRESS);
2052
+ expect(autoFrame.current).toBe(true);
2053
+
2054
+ // Its start went with it: a later end on its own is a move.
2055
+ endGesture(FRAMED_VIEW_AFTER_PRESS);
2056
+ expect(autoFrame.current).toBe(false);
2057
+ });
2058
+
2059
+ test("an empty canvas is announced only once it has stayed empty for a moment", async () => {
2060
+ jest.useFakeTimers();
2061
+ await renderGraph();
2062
+ // A new drawing is hidden for a frame or two while React Flow measures it.
2063
+ reportDrawingInView(false);
2064
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS - 1);
2065
+ reportDrawingInView(true);
2066
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 2);
2067
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2068
+
2069
+ reportDrawingInView(false);
2070
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS - 1);
2071
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2072
+ advance(1);
2073
+ const notice: HTMLElement = screen.getByTestId("service-map-out-of-view");
2074
+ expect(within(notice).getByRole("status")).toHaveTextContent(
2075
+ "The map is out of view.",
2076
+ );
2077
+ expect(
2078
+ within(notice).getByRole("button", { name: "Fit to screen" }),
2079
+ ).toBeInTheDocument();
2080
+
2081
+ // As soon as any of the drawing is back in view, the notice goes.
2082
+ reportDrawingInView(true);
2083
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2084
+ });
2085
+
2086
+ test("the notice sits on the canvas's left, clear of a drawer, and still announces itself", async () => {
2087
+ jest.useFakeTimers();
2088
+ await renderGraph();
2089
+ reportDrawingInView(false);
2090
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2091
+
2092
+ const notice: HTMLElement = screen.getByTestId("service-map-out-of-view");
2093
+ // Over the whole canvas, and cut off at its edges...
2094
+ expect(screen.getByTestId("service-map-canvas")).toContainElement(notice);
2095
+ expect(notice).toHaveClass("absolute", "inset-0", "overflow-hidden");
2096
+ // ...yet only the notice itself takes the pointer; the rest is the map's.
2097
+ expect(notice).toHaveClass("pointer-events-none");
2098
+
2099
+ const box: HTMLElement = outOfViewNoticeBox();
2100
+ expect(box).toHaveClass("pointer-events-auto", "absolute");
2101
+ /*
2102
+ * On the canvas's left, clear of a drawer open on the right, and never
2103
+ * wider than the canvas less an inset on each side. How far down is for
2104
+ * the part of the canvas on screen to say (see noticeOffsetInCanvas); a
2105
+ * canvas with no height, which is all jsdom lays out, keeps the notice
2106
+ * at the inset.
2107
+ */
2108
+ expect(box.style.left).toBe("16px");
2109
+ expect(box.style.maxWidth).toBe("calc(100% - 32px)");
2110
+ expect(box.style.top).toBe("16px");
2111
+ expect(box).toHaveTextContent("The map is out of view.");
2112
+ expect(
2113
+ within(box).getByRole("button", { name: "Fit to screen" }),
2114
+ ).toBeInTheDocument();
2115
+ });
2116
+
2117
+ test("the notice follows the part of the canvas on screen as the page scrolls and the window resizes", async () => {
2118
+ jest.useFakeTimers();
2119
+ await renderGraph();
2120
+ /*
2121
+ * A canvas 880 px tall whose top the page has scrolled 280 px past, in a
2122
+ * window 600 px high: the notice goes in the middle of the part of the
2123
+ * canvas on screen, less half its own height.
2124
+ */
2125
+ const page: PageLayout = {
2126
+ canvasTop: -280,
2127
+ canvasHeight: 880,
2128
+ windowHeight: 600,
2129
+ noticeHeight: 54,
2130
+ };
2131
+ const measureCanvas: MockFunction = standInForLayout(page);
2132
+ reportDrawingInView(false);
2133
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2134
+ expect(outOfViewNoticeBox().style.top).toBe(
2135
+ `${noticeOffsetInCanvas(-280, 880, 600, 54, 16)}px`,
2136
+ );
2137
+
2138
+ /*
2139
+ * The page scrolls 200 px further: the notice follows in the next frame,
2140
+ * measuring the canvas once however many scroll events the frame brings.
2141
+ */
2142
+ page.canvasTop = -480;
2143
+ const measured: number = measureCanvas.mock.calls.length;
2144
+ scrollPage();
2145
+ scrollPage();
2146
+ nextFrame();
2147
+ expect(measureCanvas).toHaveBeenCalledTimes(measured + 1);
2148
+ expect(outOfViewNoticeBox().style.top).toBe(
2149
+ `${noticeOffsetInCanvas(-480, 880, 600, 54, 16)}px`,
2150
+ );
2151
+
2152
+ // The window is made shorter.
2153
+ page.windowHeight = 300;
2154
+ resizeWindow();
2155
+ nextFrame();
2156
+ expect(outOfViewNoticeBox().style.top).toBe(
2157
+ `${noticeOffsetInCanvas(-480, 880, 300, 54, 16)}px`,
2158
+ );
2159
+
2160
+ /*
2161
+ * A page that scrolls in an element rather than the document: the
2162
+ * element's scroll does not bubble up to the document, and still counts.
2163
+ */
2164
+ page.canvasTop = -380;
2165
+ scrollPage(document.body);
2166
+ nextFrame();
2167
+ expect(outOfViewNoticeBox().style.top).toBe(
2168
+ `${noticeOffsetInCanvas(-380, 880, 300, 54, 16)}px`,
2169
+ );
2170
+ });
2171
+
2172
+ test("once the notice goes, the page's scrolls and resizes are no longer followed", async () => {
2173
+ jest.useFakeTimers();
2174
+ await renderGraph();
2175
+ const page: PageLayout = {
2176
+ canvasTop: -280,
2177
+ canvasHeight: 880,
2178
+ windowHeight: 600,
2179
+ noticeHeight: 54,
2180
+ };
2181
+ const measureCanvas: MockFunction = standInForLayout(page);
2182
+ reportDrawingInView(false);
2183
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2184
+ expect(outOfViewNotice()).toBeInTheDocument();
2185
+
2186
+ /*
2187
+ * The drawing is back in view: the notice goes, and stops listening. No
2188
+ * frame is pending as it goes, so a listener left behind would ask for
2189
+ * one, and be caught measuring the canvas.
2190
+ */
2191
+ reportDrawingInView(true);
2192
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2193
+ const measuredAtHiding: number = measureCanvas.mock.calls.length;
2194
+ page.canvasTop = -480;
2195
+ page.windowHeight = 300;
2196
+ scrollPage();
2197
+ scrollPage(document.body);
2198
+ resizeWindow();
2199
+ nextFrame();
2200
+ expect(measureCanvas).toHaveBeenCalledTimes(measuredAtHiding);
2201
+
2202
+ // Shown again, the notice is placed for the page as it is now...
2203
+ reportDrawingInView(false);
2204
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2205
+ expect(outOfViewNoticeBox().style.top).toBe(
2206
+ `${noticeOffsetInCanvas(-480, 880, 300, 54, 16)}px`,
2207
+ );
2208
+ // ...and follows it again.
2209
+ page.canvasTop = -380;
2210
+ scrollPage();
2211
+ nextFrame();
2212
+ expect(outOfViewNoticeBox().style.top).toBe(
2213
+ `${noticeOffsetInCanvas(-380, 880, 300, 54, 16)}px`,
2214
+ );
2215
+
2216
+ // A frame a scroll asked for is dropped if the notice goes before it.
2217
+ page.canvasTop = -280;
2218
+ scrollPage();
2219
+ reportDrawingInView(true);
2220
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2221
+ const measuredAtSecondHiding: number = measureCanvas.mock.calls.length;
2222
+ nextFrame();
2223
+ expect(measureCanvas).toHaveBeenCalledTimes(measuredAtSecondHiding);
2224
+ });
2225
+
2226
+ test("a map drawn in a background tab is judged only once the tab is seen", async () => {
2227
+ jest.useFakeTimers();
2228
+ setTabVisibility("hidden");
2229
+ await renderGraph();
2230
+ // A hidden tab measures nothing, so no card is reported in view...
2231
+ reportDrawingInView(false);
2232
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 5);
2233
+ // ...and nothing is said about a map no one can see.
2234
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2235
+ // A visibilitychange that leaves the tab hidden changes nothing.
2236
+ changeTabVisibility("hidden");
2237
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 2);
2238
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2239
+
2240
+ // Once the tab is seen the map has the usual moment to be measured.
2241
+ changeTabVisibility("visible");
2242
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS - 1);
2243
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2244
+ advance(1);
2245
+ const notice: HTMLElement = screen.getByTestId("service-map-out-of-view");
2246
+ expect(within(notice).getByRole("status")).toHaveTextContent(
2247
+ "The map is out of view.",
2248
+ );
2249
+ });
2250
+
2251
+ test("a map measured in view once its tab is seen never shows the notice", async () => {
2252
+ jest.useFakeTimers();
2253
+ setTabVisibility("hidden");
2254
+ await renderGraph();
2255
+ reportDrawingInView(false);
2256
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 5);
2257
+
2258
+ changeTabVisibility("visible");
2259
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS - 1);
2260
+ // React Flow measures the cards in the first frames back: they are in view.
2261
+ reportDrawingInView(true);
2262
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 5);
2263
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2264
+ });
2265
+
2266
+ test("a map back in view before its tab is seen leaves nothing waiting for the tab", async () => {
2267
+ jest.useFakeTimers();
2268
+ setTabVisibility("hidden");
2269
+ await renderGraph();
2270
+ reportDrawingInView(false);
2271
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2272
+ /*
2273
+ * The guard needs no measuring to say the drawing is in view again: it
2274
+ * does for a canvas with no cards, and for one that goes.
2275
+ */
2276
+ reportDrawingInView(true);
2277
+
2278
+ changeTabVisibility("visible");
2279
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 5);
2280
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2281
+ });
2282
+
2283
+ test("the notice's Fit to screen frames the map with the canvas on screen", async () => {
2284
+ jest.useFakeTimers();
2285
+ await renderGraph();
2286
+ const instance: MockFlowInstance = liveInstance();
2287
+ gesture(FRAMED_VIEW, { x: -4000, y: 0, zoom: 0.4 });
2288
+ expect(guardProps().autoFrame.current).toBe(false);
2289
+ reportDrawingInView(false);
2290
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2291
+
2292
+ fireEvent.click(
2293
+ within(screen.getByTestId("service-map-out-of-view")).getByRole(
2294
+ "button",
2295
+ { name: "Fit to screen" },
2296
+ ),
2297
+ );
2298
+ expect(instance.fitView).toHaveBeenCalledTimes(1);
2299
+ expect(instance.fitView).toHaveBeenCalledWith(ANIMATED_FIT);
2300
+ expect(guardProps().autoFrame.current).toBe(true);
2301
+ expect(mockFlowMounts).toBe(1);
2302
+ // The fit brings the drawing back into view, and the notice goes.
2303
+ reportDrawingInView(true);
2304
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2305
+ });
2306
+
2307
+ test("a canvas that cannot fit is drawn again from scratch", async () => {
2308
+ jest.useFakeTimers();
2309
+ await renderGraph();
2310
+ const stuck: MockFlowInstance = liveInstance();
2311
+ // React Flow has no measured card to frame: fitView does nothing.
2312
+ stuck.fitView.mockReturnValue(false);
2313
+ reportDrawingInView(false);
2314
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2315
+
2316
+ fireEvent.click(
2317
+ within(screen.getByTestId("service-map-out-of-view")).getByRole(
2318
+ "button",
2319
+ { name: "Fit to screen" },
2320
+ ),
2321
+ );
2322
+ expect(stuck.fitView).toHaveBeenCalledWith(ANIMATED_FIT);
2323
+ expect(mockFlowMounts).toBe(2);
2324
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2325
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 2);
2326
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2327
+
2328
+ // The fresh canvas draws the same map, and is the one fitted from now on.
2329
+ expect(guardProps().drawingKey).toBe(idsOf(flowProps().nodes).join("|"));
2330
+ const fresh: MockFlowInstance = liveInstance();
2331
+ expect(fresh).not.toBe(stuck);
2332
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2333
+ expect(fresh.fitView).toHaveBeenCalledWith(ANIMATED_FIT);
2334
+ expect(stuck.fitView).toHaveBeenCalledTimes(1);
2335
+ expect(mockFlowMounts).toBe(2);
2336
+ });
2337
+
2338
+ test("with no live canvas, Fit to screen draws it again rather than doing nothing", async () => {
2339
+ // The first canvas never reports a usable viewport.
2340
+ mockViewportInitialized = false;
2341
+ await renderGraph();
2342
+ const unready: MockFlowInstance = flowInstance(0);
2343
+ mockViewportInitialized = true;
2344
+
2345
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2346
+ expect(unready.fitView).not.toHaveBeenCalled();
2347
+ expect(mockFlowMounts).toBe(2);
2348
+
2349
+ // The fresh canvas is ready, and Fit to screen now frames the map with it.
2350
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2351
+ expect(liveInstance().fitView).toHaveBeenCalledWith(ANIMATED_FIT);
2352
+ expect(mockFlowMounts).toBe(2);
2353
+ });
2354
+
2355
+ test("after Map, Table, Map, Fit to screen fits the new canvas, never the one that is gone", async () => {
2356
+ await renderGraph();
2357
+ const first: MockFlowInstance = liveInstance();
2358
+ fireEvent.click(screen.getByTestId("service-map-view-list"));
2359
+ expect(screen.queryByTestId("rendered-graph")).not.toBeInTheDocument();
2360
+ fireEvent.click(screen.getByTestId("service-map-view-map"));
2361
+ const second: MockFlowInstance = liveInstance();
2362
+ expect(second).not.toBe(first);
2363
+
2364
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2365
+ expect(second.fitView).toHaveBeenCalledTimes(1);
2366
+ expect(second.fitView).toHaveBeenCalledWith(ANIMATED_FIT);
2367
+ expect(first.fitView).not.toHaveBeenCalled();
2368
+ expect(mockFlowMounts).toBe(2);
2369
+ });
2370
+
2371
+ test("a canvas that is gone is never fitted, even before the new one is ready", async () => {
2372
+ await renderGraph();
2373
+ const gone: MockFlowInstance = liveInstance();
2374
+ fireEvent.click(screen.getByTestId("service-map-view-list"));
2375
+ mockViewportInitialized = false;
2376
+ fireEvent.click(screen.getByTestId("service-map-view-map"));
2377
+
2378
+ fireEvent.click(screen.getByTestId("service-map-fit"));
2379
+ expect(gone.fitView).not.toHaveBeenCalled();
2380
+ // With nothing live to fit, the canvas is drawn again instead.
2381
+ expect(mockFlowMounts).toBe(3);
2382
+ });
2383
+
2384
+ test("leaving the map while it is out of view brings no stale notice back", async () => {
2385
+ jest.useFakeTimers();
2386
+ await renderGraph();
2387
+ reportDrawingInView(false);
2388
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS);
2389
+ expect(outOfViewNotice()).toBeInTheDocument();
2390
+
2391
+ fireEvent.click(screen.getByTestId("service-map-view-list"));
2392
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2393
+ fireEvent.click(screen.getByTestId("service-map-view-map"));
2394
+ advance(OUT_OF_VIEW_NOTICE_DELAY_MS * 2);
2395
+ expect(outOfViewNotice()).not.toBeInTheDocument();
2396
+ });
2397
+
2398
+ test("the zoom hint shows with the map, not the table", async () => {
2399
+ await renderGraph();
2400
+ expect(screen.getByTestId("service-map-zoom-hint")).toHaveTextContent(
2401
+ "Ctrl + scroll or pinch to zoom",
2402
+ );
2403
+ fireEvent.click(screen.getByTestId("service-map-view-list"));
2404
+ expect(
2405
+ screen.queryByTestId("service-map-zoom-hint"),
2406
+ ).not.toBeInTheDocument();
2407
+ fireEvent.click(screen.getByTestId("service-map-view-map"));
2408
+ expect(screen.getByTestId("service-map-zoom-hint")).toBeInTheDocument();
2409
+ });
2410
+ });