@oneuptime/common 14.0.7 → 14.0.9

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 (1064) hide show
  1. package/Models/DatabaseModels/Incident.ts +267 -1
  2. package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
  3. package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
  4. package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
  5. package/Models/DatabaseModels/Index.ts +2 -0
  6. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  7. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  8. package/Models/DatabaseModels/Project.ts +55 -5
  9. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  10. package/Models/DatabaseModels/RunnerJob.ts +75 -0
  11. package/Models/DatabaseModels/StatusPage.ts +55 -0
  12. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  13. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  14. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  15. package/Server/API/AIChatAPI.ts +6 -3
  16. package/Server/API/AIInvestigationAPI.ts +17 -10
  17. package/Server/API/AutoRemediationAPI.ts +207 -25
  18. package/Server/API/IncidentAPI.ts +162 -0
  19. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  20. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  21. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  22. package/Server/API/StatusPageAPI.ts +487 -171
  23. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  24. package/Server/Infrastructure/GlobalCache.ts +32 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
  36. package/Server/Infrastructure/Semaphore.ts +10 -0
  37. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  38. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  39. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  40. package/Server/Middleware/TelemetryIngest.ts +41 -18
  41. package/Server/Services/AIService.ts +70 -1
  42. package/Server/Services/AlertService.ts +352 -11
  43. package/Server/Services/AlertSeverityService.ts +65 -15
  44. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  45. package/Server/Services/DatabaseService.ts +93 -10
  46. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  47. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  48. package/Server/Services/IncidentEpisodeService.ts +15 -0
  49. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  50. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  51. package/Server/Services/IncidentService.ts +1788 -142
  52. package/Server/Services/IncidentSeverityService.ts +66 -15
  53. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  54. package/Server/Services/IncidentTemplateService.ts +109 -0
  55. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  56. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  57. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  58. package/Server/Services/KubernetesClusterService.ts +129 -21
  59. package/Server/Services/ProjectService.ts +32 -0
  60. package/Server/Services/ProxmoxResourceService.ts +322 -5
  61. package/Server/Services/RunnerJobService.ts +241 -104
  62. package/Server/Services/RunnerService.ts +1 -1
  63. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  64. package/Server/Services/ScheduledMaintenanceService.ts +805 -19
  65. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  66. package/Server/Services/SmsService.ts +10 -0
  67. package/Server/Services/StatusPageService.ts +65 -8
  68. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  69. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  70. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  71. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  72. package/Server/Types/Markdown.ts +153 -6
  73. package/Server/Types/Workflow/Components/Email.ts +61 -2
  74. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  75. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  76. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  77. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
  78. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
  79. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
  80. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  81. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  82. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  83. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
  84. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  85. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  86. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  87. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  88. package/Server/Utils/SSRFProtection.ts +76 -16
  89. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  90. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  91. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  92. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  93. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  94. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  95. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  96. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  97. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  98. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  99. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  100. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  101. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  102. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  103. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  104. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  105. package/Server/Utils/StatusPageResource.ts +51 -20
  106. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  107. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  108. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  109. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  110. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  111. package/Server/Utils/VM/VMAPI.ts +1 -0
  112. package/Server/Utils/VM/VMRunner.ts +10 -0
  113. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  114. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
  115. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  116. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  117. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  118. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  119. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  120. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  121. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  122. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  123. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  124. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  125. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  126. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  127. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  128. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  129. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  130. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  131. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  132. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  133. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  134. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  135. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  136. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  137. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  138. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  139. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
  140. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  141. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  142. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  143. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  144. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  145. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  146. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  147. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  148. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  149. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  150. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  151. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  152. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  153. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  154. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  155. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  156. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  157. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  158. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  159. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  160. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  161. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  162. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  163. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  164. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  165. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  166. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  167. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  168. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  169. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  170. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  171. package/Tests/App/Dashboard/EventNotes.test.tsx +778 -2
  172. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  173. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  174. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +828 -17
  175. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  176. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  177. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  178. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  179. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  180. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  181. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  182. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  183. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  184. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  185. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  186. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  187. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  188. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  189. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  190. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  191. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  192. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  193. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  194. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  195. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  196. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  197. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  198. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  199. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  200. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  201. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  202. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  203. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  204. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  205. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  206. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  207. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  208. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  209. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  210. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  211. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  212. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  213. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  214. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  215. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  216. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  217. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  218. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  219. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  220. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  221. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  222. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  223. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  224. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  225. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  226. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  227. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  228. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  229. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  230. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  231. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  232. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  233. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  234. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  235. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  236. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  237. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  238. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  239. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  240. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  241. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  242. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  243. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  244. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  245. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  246. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  247. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  248. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  249. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  250. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  251. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  252. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  253. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  254. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  255. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  256. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  257. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  258. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  259. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  260. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  261. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  262. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  263. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  264. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  265. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  266. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  267. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  268. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  269. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  270. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  271. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  272. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  273. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  274. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  275. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  276. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  277. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  278. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  279. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  280. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  281. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  282. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  283. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  284. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  285. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  286. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  287. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  288. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  289. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  290. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  291. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  292. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  293. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  294. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  295. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  296. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  297. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  298. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  299. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  300. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  301. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  302. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  303. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  304. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  305. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  306. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  307. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  308. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  309. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  310. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  311. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  312. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  313. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  314. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  315. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  316. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  317. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  318. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  319. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  320. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  321. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  322. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  323. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  324. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  325. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  326. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  327. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  328. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  329. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  330. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  331. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  332. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  333. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  334. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  335. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  336. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  337. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  338. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
  339. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
  340. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
  341. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  342. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  343. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  344. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  345. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  346. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  347. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  348. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  349. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  350. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  351. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  352. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  353. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  354. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  355. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  356. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  357. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  358. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  359. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  360. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  361. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  362. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  363. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  364. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  365. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  366. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  367. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  368. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  369. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  370. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  371. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  372. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  373. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  374. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  375. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  376. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  377. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  378. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  379. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
  380. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  381. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  382. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  383. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  384. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  385. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  386. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  387. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  388. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  389. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  390. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  391. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  392. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  393. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  394. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  395. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  396. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  397. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  398. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  399. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  400. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  401. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  402. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  403. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  404. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  405. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  406. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  407. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  408. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  409. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  410. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  411. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  412. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  413. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  414. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  415. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  416. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  417. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  418. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  419. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  420. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  421. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  422. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  423. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  424. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  425. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  426. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  427. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  428. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  429. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  430. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  431. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  432. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  433. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  434. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  435. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  436. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  437. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  438. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  439. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  440. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  441. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  442. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  443. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  444. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  445. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  446. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  447. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  448. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  449. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  450. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  451. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  452. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  453. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  454. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  455. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  456. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  457. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  458. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  459. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
  460. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  461. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  462. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  463. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  464. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  465. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  466. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  467. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  468. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  469. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  470. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  471. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  472. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  473. package/Tests/Types/SafeHtml.test.ts +102 -0
  474. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  475. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  476. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  477. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  478. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  479. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  480. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  481. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  482. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  483. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  484. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  485. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  486. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  487. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  488. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  489. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  490. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  491. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  492. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  493. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  494. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  495. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  496. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  497. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  498. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  499. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  500. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  501. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  502. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  503. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  504. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  505. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  506. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  507. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  508. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  509. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  510. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  511. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  512. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  513. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  514. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  515. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  516. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  517. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  518. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  519. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  520. package/Tests/UI/Components/Input.test.tsx +80 -1
  521. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  522. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  523. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  524. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  525. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  526. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  527. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  528. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  529. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  530. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  531. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  532. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  533. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  534. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  535. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  536. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  537. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  538. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  539. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  540. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  541. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  542. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  543. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  544. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  545. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  546. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  547. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  548. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  549. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  550. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  551. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  552. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  553. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  554. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  555. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  556. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  557. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  558. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  559. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  560. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  561. package/Types/AI/AIRunType.ts +9 -4
  562. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  563. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
  564. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  565. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  566. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  567. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  568. package/Types/CustomField/CustomFieldType.ts +14 -0
  569. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  570. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  571. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  572. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  573. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  574. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  575. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  576. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  577. package/Types/SafeHtml.ts +93 -0
  578. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  579. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  580. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  581. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  582. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  583. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  584. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  585. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  586. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  587. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  588. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  589. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  590. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  591. package/Types/Team/ComplianceRule.ts +434 -0
  592. package/Types/Team/ComplianceRuleType.ts +21 -0
  593. package/Types/Team/TeamComplianceStatus.ts +81 -0
  594. package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
  595. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  596. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  597. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  598. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  599. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  600. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  601. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  602. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  603. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  604. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  605. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  606. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  607. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  608. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  609. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  610. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  611. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  612. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  613. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  614. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  615. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  616. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  617. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  618. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  619. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  620. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  621. package/UI/Components/Forms/ModelForm.tsx +70 -8
  622. package/UI/Components/Forms/Validation.ts +1 -1
  623. package/UI/Components/Input/Input.tsx +90 -57
  624. package/UI/Components/List/ListRow.tsx +8 -74
  625. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  626. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  627. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  628. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  629. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  630. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  631. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  632. package/UI/Components/ModelTable/BaseModelTable.tsx +21 -9
  633. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  634. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  635. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  636. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  637. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  638. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  639. package/UI/Components/Table/TableBody.tsx +8 -2
  640. package/UI/Components/Table/TableRow.tsx +20 -140
  641. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  642. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  643. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  644. package/UI/Components/TelemetryViewer/types.ts +2 -0
  645. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  646. package/UI/Components/Toggle/Toggle.tsx +26 -0
  647. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  648. package/UI/Styles/Theme.css +14 -2
  649. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  650. package/Utils/API.ts +247 -2
  651. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  652. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  653. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  654. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  655. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  656. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  657. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  658. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  659. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  660. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  661. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  662. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  663. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  664. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  665. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  666. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  667. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  668. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  669. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  670. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  671. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  672. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  673. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  674. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  675. package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
  676. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  677. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  678. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  679. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  680. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  681. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  682. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  683. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  684. package/build/dist/Server/API/AIChatAPI.js +6 -1
  685. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  686. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  687. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  688. package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
  689. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  690. package/build/dist/Server/API/IncidentAPI.js +100 -1
  691. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  692. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  693. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  694. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  695. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  696. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  697. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  698. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  699. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  700. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  701. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  702. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  703. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  704. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  705. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  706. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  707. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  708. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  709. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  710. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  711. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  712. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  713. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  714. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  715. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  716. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  717. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  718. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  719. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  720. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  721. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  722. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  723. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  724. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
  725. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  726. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  727. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  728. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  729. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  730. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  731. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  732. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  733. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  734. package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
  735. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  736. package/build/dist/Server/Services/AIService.js +59 -1
  737. package/build/dist/Server/Services/AIService.js.map +1 -1
  738. package/build/dist/Server/Services/AlertService.js +248 -13
  739. package/build/dist/Server/Services/AlertService.js.map +1 -1
  740. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  741. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  742. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  743. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  744. package/build/dist/Server/Services/DatabaseService.js +56 -7
  745. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  746. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  747. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  748. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  749. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  750. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  751. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  752. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  753. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  754. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  755. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  756. package/build/dist/Server/Services/IncidentService.js +1251 -72
  757. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  758. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  759. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  760. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  761. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  762. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  763. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  764. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  765. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  766. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  767. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  768. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  769. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  770. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  771. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  772. package/build/dist/Server/Services/ProjectService.js +22 -0
  773. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  774. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  775. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  776. package/build/dist/Server/Services/RunnerJobService.js +178 -92
  777. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  778. package/build/dist/Server/Services/RunnerService.js +1 -1
  779. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  780. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  781. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  782. package/build/dist/Server/Services/ScheduledMaintenanceService.js +582 -20
  783. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  784. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  785. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  786. package/build/dist/Server/Services/SmsService.js +2 -22
  787. package/build/dist/Server/Services/SmsService.js.map +1 -1
  788. package/build/dist/Server/Services/StatusPageService.js +47 -9
  789. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  790. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  791. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  792. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  793. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  794. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  795. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  796. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  797. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  798. package/build/dist/Server/Types/Markdown.js +110 -6
  799. package/build/dist/Server/Types/Markdown.js.map +1 -1
  800. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  801. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  802. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  803. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  804. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  805. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  806. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  807. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  808. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
  809. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  810. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
  811. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  812. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
  813. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  814. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  815. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  816. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  817. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  818. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  819. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
  820. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  821. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  822. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  823. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  824. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  825. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  826. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  827. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  828. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  829. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  830. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  831. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  832. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  833. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  834. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  835. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  836. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  837. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  838. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  839. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  840. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  841. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  842. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  843. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  844. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  845. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  846. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  847. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  848. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  849. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  850. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  851. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  852. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  853. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  854. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  855. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  856. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  857. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  858. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  859. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  860. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  861. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  862. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  863. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  864. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  865. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  866. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  867. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  868. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  869. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  870. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  871. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  872. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  873. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  874. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  875. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  876. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  877. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  878. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  879. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  880. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
  881. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  882. package/build/dist/Types/AI/AIRunType.js +9 -4
  883. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  884. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
  885. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  886. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  887. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  888. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  889. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  890. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  891. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  892. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  893. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  894. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  895. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  896. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  897. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  898. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  899. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  900. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  901. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  902. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  903. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  904. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  905. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  906. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  907. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  908. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  909. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  910. package/build/dist/Types/SafeHtml.js +84 -0
  911. package/build/dist/Types/SafeHtml.js.map +1 -0
  912. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  913. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  914. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  915. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  916. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  917. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  918. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  919. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  920. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  921. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  922. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  923. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  924. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  925. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  926. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  927. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  928. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  929. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  930. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  931. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  932. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  933. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  934. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  935. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  936. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  937. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  938. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  939. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  940. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  941. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  942. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  943. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  944. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
  945. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  946. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  947. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  948. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  949. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  950. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  951. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  952. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  953. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  954. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  955. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  956. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  957. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  958. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  959. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  960. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  961. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  962. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  963. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  964. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  965. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  966. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  967. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  968. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  969. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  970. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  971. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  972. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  973. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  974. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  975. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  976. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  977. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  978. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  979. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  980. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  981. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  982. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  983. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  984. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  985. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  986. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  987. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  988. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  989. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  990. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  991. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  992. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  993. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  994. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  995. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  996. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  997. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  998. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  999. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  1000. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  1001. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  1002. package/build/dist/UI/Components/Input/Input.js +69 -54
  1003. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  1004. package/build/dist/UI/Components/List/ListRow.js +4 -39
  1005. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  1006. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  1007. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1008. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  1009. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  1010. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  1011. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  1012. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  1013. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1014. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  1015. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1016. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  1017. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  1018. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -2
  1019. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1020. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  1021. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  1022. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  1023. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  1024. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  1025. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  1026. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  1027. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  1028. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  1029. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  1030. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  1031. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  1032. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  1033. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  1034. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  1035. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  1036. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  1037. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1038. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  1039. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1040. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  1041. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  1042. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  1043. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  1044. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  1045. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  1046. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  1047. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  1048. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  1049. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  1050. package/build/dist/Utils/API.js +177 -1
  1051. package/build/dist/Utils/API.js.map +1 -1
  1052. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  1053. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  1054. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  1055. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  1056. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  1057. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  1058. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  1059. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  1060. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  1061. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  1062. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  1063. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  1064. package/package.json +1 -1
@@ -0,0 +1,1757 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+ import {
10
+ RenderHookResult,
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ renderHook,
15
+ } from "@testing-library/react";
16
+ import type { SpyInstance } from "jest-mock";
17
+ import getJestMockFunction, { MockFunction } from "../../../MockType";
18
+ import useHistogramRangeSelection, {
19
+ HISTOGRAM_PRESS_STILLNESS_PX,
20
+ HistogramPlaceholderProps,
21
+ HistogramPointerState,
22
+ HistogramRangeSelectionOptions,
23
+ HistogramRangeSelectionState,
24
+ } from "../../../../UI/Components/Charts/Utils/useHistogramRangeSelection";
25
+ import {
26
+ ChartPointerEvent,
27
+ DOUBLE_CLICK_DISAMBIGUATION_MS,
28
+ } from "../../../../UI/Components/Charts/ChartLibrary/Utils/DoubleClick";
29
+
30
+ /*
31
+ * Issue #4116. Right after a drag zooms an explorer (Traces, Logs, ...),
32
+ * its histogram refetches, and a reader double-clicks to undo the zoom at
33
+ * exactly that moment. When the new buckets landed during the double-
34
+ * click's SECOND press, recharts re-keyed the bars and the selection band
35
+ * under the press, and Chrome sends no click and no dblclick for a press
36
+ * whose node left the page: the chart stayed zoomed. Worse, the hook read
37
+ * that press's release - whose bar now came from the NEW data, at the same
38
+ * tooltip index - as a drag from an old bar to a new one, and zoomed the
39
+ * explorer to a bogus window. New buckets landing during the FIRST press
40
+ * zoomed in once more before the reset.
41
+ *
42
+ * These tests drive useHistogramRangeSelection the way recharts 3 drives a
43
+ * chart root's handlers: each gets the chart state (the bar under the
44
+ * pointer, `activeLabel`) and the React event (the browser's click count,
45
+ * `detail`, and `clientX`). Pinned here:
46
+ *
47
+ * - on a zoomed chart the second press of a double-click (detail 2) IS
48
+ * the zoom-out: it cancels the first click's zoom-in without a render,
49
+ * starts no selection, and its release - over the chart or, heard by
50
+ * the page, off it - zooms out in the next task even when no dblclick
51
+ * follows, and exactly once when one does;
52
+ * - a press becomes a drag only when the POINTER moves (more than
53
+ * HISTOGRAM_PRESS_STILLNESS_PX) onto another bar. Another bar coming
54
+ * under a still pointer is not a drag. It is new data, and the press
55
+ * zooms nowhere, when it shows at the very spot pressed (the same
56
+ * tooltip index) or when the pointer never moved at all, not by a
57
+ * pixel: recharts clamps a stale index onto the last bar of shorter
58
+ * data, so a press right of where a shorter refetch ends is let go at
59
+ * another index, as after a drift. When a hand's drift crossed into
60
+ * the next bar, it is a click on the bar it pressed;
61
+ * - a second press whose own release never came (the window lost focus
62
+ * mid-press) is ended by the next release heard, which the browser
63
+ * counts 1 - a later, unrelated click's: it zooms nothing out, clears
64
+ * the band the first click painted, and leaves no page-wide listener
65
+ * behind;
66
+ * - a click waiting to zoom in is dropped, band and all, the moment the
67
+ * host stops offering onZoomOut (the zoom ended another way), and so
68
+ * is the band a double-click's second press kept for the zoom-out; a
69
+ * drag's band stays;
70
+ * - a box standing in for the chart (placeholderProps: its loader, its
71
+ * empty box) takes a double-click's second press as the chart does:
72
+ * its release, over the chart that replaced the box by then or
73
+ * anywhere on the page, zooms out in the next task, once, dblclick or
74
+ * not. A press on it counted 1, a right-button press, or any press on
75
+ * an unzoomed chart is nothing, and listens for nothing;
76
+ * - only the main button selects;
77
+ * - a press let go off the chart, which only the page hears, is read at
78
+ * the pointer's place: let go where it went down, a click on the bar
79
+ * it pressed; after crossing bars, a drag to the last one;
80
+ * - drags, flicks and clicks select what they always did, an unzoomed
81
+ * chart has no double-click gesture, and a host that hands the
82
+ * handlers no event keeps the old label-only reading.
83
+ *
84
+ * Before the fix, every case here in which new bars land mid-press, a
85
+ * second press has to zoom out without its dblclick, or a few pixels of
86
+ * the pointer's own movement decide the drag, failed. Before its
87
+ * follow-ups, so did a later click's release after a lost one (it zoomed
88
+ * out), a click still waiting when the zoom ended another way (it zoomed
89
+ * back in), and a right-click (it zoomed into its bar). Before commit
90
+ * b6ec899df4, so did a still press right of where a shorter refetch ends
91
+ * (it zoomed into the bar the refetch left out, 10:25), every case of a
92
+ * box standing in for the chart (there were no placeholderProps to spread
93
+ * on one), and the band a second press kept for a zoom-out that is not
94
+ * coming (it stayed lit). The rest - drags, flicks and clicks, a hand's
95
+ * drift, hosts that hand no event, the unzoomed chart, and double-clicks
96
+ * that keep their dblclick - guard what already worked.
97
+ */
98
+
99
+ const MINUTE_MS: number = 60 * 1000;
100
+
101
+ /*
102
+ * Neighbouring one-minute bars, labelled as recharts reports them (the
103
+ * instant each bucket starts), and the clientX each is pressed at.
104
+ */
105
+ const BAR_A: string = "2026-09-28T10:00:00.000Z";
106
+ const BAR_B: string = "2026-09-28T10:01:00.000Z";
107
+ const BAR_C: string = "2026-09-28T10:02:00.000Z";
108
+ const X_A: number = 100;
109
+ const X_B: number = 140;
110
+ const X_C: number = 170;
111
+
112
+ /*
113
+ * A bar of the chart's NEW data. When the refetched buckets land under a
114
+ * press, recharts hands the handlers whichever bar now sits at the
115
+ * pointer's tooltip index: at A's spot, this one.
116
+ */
117
+ const LANDED_BAR: string = "2026-09-28T10:04:05.000Z";
118
+
119
+ // The instant `index` one-minute bars after `from`.
120
+ function minuteBarAfter(from: string, index: number): string {
121
+ return new Date(Date.parse(from) + index * MINUTE_MS).toISOString();
122
+ }
123
+
124
+ /*
125
+ * A chart of thirty bars, 10:00 to 10:29, pressed far right, on 10:25 (the
126
+ * 26th bar, index 25) at X_LATE - and 10:26 just right of it.
127
+ */
128
+ const HALF_HOUR_FROM: string = "2026-09-28T10:00:00.000Z";
129
+ const LATE_BAR: string = minuteBarAfter(HALF_HOUR_FROM, 25);
130
+ const BAR_AFTER_LATE_BAR: string = minuteBarAfter(HALF_HOUR_FROM, 26);
131
+ const X_LATE: number = 500;
132
+
133
+ /*
134
+ * A refetch of only ten bars, 11:00 to 11:09, landing under that press.
135
+ * recharts keeps the index the pointer last hovered, and one past the end
136
+ * of shorter data it clamps onto the LAST bar: the release of a press that
137
+ * never moved reports 11:09 at index 9. Had the pointer moved - or Chrome
138
+ * made up a move at the same spot, as it does when the page under a still
139
+ * pointer changes - recharts would hit-test the new bars instead and report
140
+ * the one under the pointer, 11:08. Either way the index changed as it
141
+ * does when a hand's drift crosses a bar's edge.
142
+ */
143
+ const REFETCH_FROM: string = "2026-09-28T11:00:00.000Z";
144
+ const REFETCH_LAST_BAR: string = minuteBarAfter(REFETCH_FROM, 9);
145
+ const REFETCH_BAR_UNDER_POINTER: string = minuteBarAfter(REFETCH_FROM, 8);
146
+
147
+ /*
148
+ * Where on the axis recharts reports each bar (`activeTooltipIndex`, a
149
+ * string in recharts 3). It works the index out again only when the
150
+ * pointer moves, so bars landing under a still pointer keep the pressed
151
+ * spot's index: the landed bar sits at A's.
152
+ */
153
+ const AXIS_INDEX: Record<string, string> = {
154
+ [BAR_A]: "0",
155
+ [BAR_B]: "1",
156
+ [BAR_C]: "2",
157
+ [LANDED_BAR]: "0",
158
+ [LATE_BAR]: "25",
159
+ [BAR_AFTER_LATE_BAR]: "26",
160
+ [REFETCH_BAR_UNDER_POINTER]: "8",
161
+ [REFETCH_LAST_BAR]: "9",
162
+ };
163
+
164
+ // No selection band on the chart.
165
+ const NO_BAND: [null, null] = [null, null];
166
+
167
+ interface Harness {
168
+ // The hook as the host last rendered it.
169
+ result: { current: HistogramRangeSelectionState };
170
+ rerender: (options: HistogramRangeSelectionOptions) => void;
171
+ unmount: () => void;
172
+ onTimeRangeSelect: MockFunction;
173
+ // Handed to the hook only on a zoomed chart.
174
+ onZoomOut: MockFunction;
175
+ // How many times the host has rendered the hook so far.
176
+ renderCount: () => number;
177
+ }
178
+
179
+ /*
180
+ * A host histogram with one-minute bars. A zoomed one shows a window a
181
+ * drag zoomed into, and so offers a way back out: `onZoomOut`.
182
+ */
183
+ function renderSelection(isZoomed: boolean): Harness {
184
+ const onTimeRangeSelect: MockFunction = getJestMockFunction();
185
+ const onZoomOut: MockFunction = getJestMockFunction();
186
+ const renders: { count: number } = { count: 0 };
187
+ const initialProps: HistogramRangeSelectionOptions = {
188
+ onTimeRangeSelect: onTimeRangeSelect,
189
+ onZoomOut: isZoomed ? onZoomOut : undefined,
190
+ bucketIntervalMs: MINUTE_MS,
191
+ };
192
+
193
+ const hook: RenderHookResult<
194
+ HistogramRangeSelectionState,
195
+ HistogramRangeSelectionOptions
196
+ > = renderHook(
197
+ (options: HistogramRangeSelectionOptions): HistogramRangeSelectionState => {
198
+ renders.count += 1;
199
+ return useHistogramRangeSelection(options);
200
+ },
201
+ { initialProps: initialProps },
202
+ );
203
+
204
+ return {
205
+ result: hook.result,
206
+ rerender: (options: HistogramRangeSelectionOptions): void => {
207
+ hook.rerender(options);
208
+ },
209
+ unmount: hook.unmount,
210
+ onTimeRangeSelect: onTimeRangeSelect,
211
+ onZoomOut: onZoomOut,
212
+ renderCount: (): number => {
213
+ return renders.count;
214
+ },
215
+ };
216
+ }
217
+
218
+ type PointerHandler = (
219
+ state?: HistogramPointerState | null,
220
+ event?: ChartPointerEvent | null,
221
+ ) => void;
222
+
223
+ /*
224
+ * Calls a chart-root handler as recharts does: with the bar under the
225
+ * pointer (none when `label` is undefined) and its place on the axis, and
226
+ * the React event. With no `event` at all it gets the bar alone, as from
227
+ * a host that hands none.
228
+ */
229
+ function send(
230
+ handler: PointerHandler,
231
+ label: string | undefined,
232
+ event?: ChartPointerEvent | null,
233
+ ): void {
234
+ act(() => {
235
+ const state: HistogramPointerState =
236
+ label === undefined
237
+ ? { activeLabel: undefined }
238
+ : { activeLabel: label, activeTooltipIndex: AXIS_INDEX[label] };
239
+ if (event === undefined) {
240
+ handler(state);
241
+ } else {
242
+ handler(state, event);
243
+ }
244
+ });
245
+ }
246
+
247
+ function press(
248
+ harness: Harness,
249
+ label: string | undefined,
250
+ event?: ChartPointerEvent | null,
251
+ ): void {
252
+ send(harness.result.current.onMouseDown, label, event);
253
+ }
254
+
255
+ function move(
256
+ harness: Harness,
257
+ label: string | undefined,
258
+ event?: ChartPointerEvent | null,
259
+ ): void {
260
+ send(harness.result.current.onMouseMove, label, event);
261
+ }
262
+
263
+ function release(
264
+ harness: Harness,
265
+ label: string | undefined,
266
+ event?: ChartPointerEvent | null,
267
+ ): void {
268
+ send(harness.result.current.onMouseUp, label, event);
269
+ }
270
+
271
+ // The browser's dblclick, which the host hands to the hook.
272
+ function dblclick(harness: Harness): void {
273
+ act(() => {
274
+ harness.result.current.onDoubleClick();
275
+ });
276
+ }
277
+
278
+ type PlaceholderPressEvent = Parameters<
279
+ HistogramPlaceholderProps["onMouseDown"]
280
+ >[0];
281
+
282
+ /*
283
+ * A press on a box standing in for the chart - its loader, its empty box -
284
+ * which the host spreads placeholderProps onto. React hands it the event;
285
+ * the click count and the button are all the hook reads of it.
286
+ */
287
+ function pressPlaceholder(harness: Harness, event: ChartPointerEvent): void {
288
+ act(() => {
289
+ harness.result.current.placeholderProps.onMouseDown(
290
+ event as unknown as PlaceholderPressEvent,
291
+ );
292
+ });
293
+ }
294
+
295
+ /*
296
+ * The main button going down or coming up at `clientX`. `clicks` is the
297
+ * browser's click count (MouseEvent.detail): 2 on both halves of a
298
+ * double-click's second click.
299
+ */
300
+ function mainButton(clientX: number, clicks: number): ChartPointerEvent {
301
+ return { clientX: clientX, detail: clicks, button: 0 };
302
+ }
303
+
304
+ // The pointer moving to `clientX` with the button held. A move counts no clicks.
305
+ function pointerAt(clientX: number): ChartPointerEvent {
306
+ return { clientX: clientX, detail: 0, button: 0 };
307
+ }
308
+
309
+ /*
310
+ * The main button coming up away from the chart, where only the page hears
311
+ * it: the native event carries the click count and where the pointer is
312
+ * (clientX 0, left of every bar, unless said), but no bar.
313
+ */
314
+ function releaseOnThePage(clicks: number, clientX: number = 0): void {
315
+ fireEvent.mouseUp(document.body, {
316
+ button: 0,
317
+ detail: clicks,
318
+ clientX: clientX,
319
+ });
320
+ }
321
+
322
+ /*
323
+ * Another button than the main one going down or coming up at `clientX`:
324
+ * 1 is the middle button, 2 the right one.
325
+ */
326
+ function otherButton(
327
+ button: number,
328
+ clientX: number,
329
+ clicks: number,
330
+ ): ChartPointerEvent {
331
+ return { clientX: clientX, detail: clicks, button: button };
332
+ }
333
+
334
+ /*
335
+ * The page-wide mouseup listeners a press adds to hear its release off the
336
+ * chart, and the ones taken away again.
337
+ */
338
+ interface PageListenerSpies {
339
+ add: SpyInstance<Window["addEventListener"]>;
340
+ remove: SpyInstance<Window["removeEventListener"]>;
341
+ }
342
+
343
+ function spyOnPageListeners(): PageListenerSpies {
344
+ return {
345
+ add: jest.spyOn(window, "addEventListener"),
346
+ remove: jest.spyOn(window, "removeEventListener"),
347
+ };
348
+ }
349
+
350
+ function mouseUpListeners(calls: Array<Array<unknown>>): Array<unknown> {
351
+ return calls
352
+ .filter((call: Array<unknown>): boolean => {
353
+ return call[0] === "mouseup";
354
+ })
355
+ .map((call: Array<unknown>): unknown => {
356
+ return call[1];
357
+ });
358
+ }
359
+
360
+ // The mouseup listeners the page still has: added and not yet taken away.
361
+ function pageListenersLeft(spies: PageListenerSpies): Array<unknown> {
362
+ const removed: Array<unknown> = mouseUpListeners(spies.remove.mock.calls);
363
+
364
+ return mouseUpListeners(spies.add.mock.calls).filter(
365
+ (added: unknown): boolean => {
366
+ return !removed.includes(added);
367
+ },
368
+ );
369
+ }
370
+
371
+ // The host re-rendering the chart with its zoom gone: no onZoomOut on offer.
372
+ function endZoomAnotherWay(harness: Harness): void {
373
+ harness.rerender({
374
+ onTimeRangeSelect: harness.onTimeRangeSelect,
375
+ onZoomOut: undefined,
376
+ bucketIntervalMs: MINUTE_MS,
377
+ });
378
+ }
379
+
380
+ /*
381
+ * A double-click's first click on bar A, and its second press there: what
382
+ * the browser sends up to the second release.
383
+ */
384
+ function clickAThenPressAgain(harness: Harness): void {
385
+ press(harness, BAR_A, mainButton(X_A, 1));
386
+ release(harness, BAR_A, mainButton(X_A, 1));
387
+ press(harness, BAR_A, mainButton(X_A, 2));
388
+ }
389
+
390
+ function letTimePass(ms: number): void {
391
+ act(() => {
392
+ jest.advanceTimersByTime(ms);
393
+ });
394
+ }
395
+
396
+ // The first and last bar the selection band spans.
397
+ function band(harness: Harness): [string | null, string | null] {
398
+ return [
399
+ harness.result.current.selectionStart,
400
+ harness.result.current.selectionEnd,
401
+ ];
402
+ }
403
+
404
+ // The window a selection from bar `first` through bar `last` zooms into.
405
+ function windowOf(first: string, last: string): [string, string] {
406
+ return [first, new Date(Date.parse(last) + MINUTE_MS).toISOString()];
407
+ }
408
+
409
+ function selections(onTimeRangeSelect: MockFunction): Array<[string, string]> {
410
+ return onTimeRangeSelect.mock.calls.map(
411
+ (call: Array<unknown>): [string, string] => {
412
+ return [(call[0] as Date).toISOString(), (call[1] as Date).toISOString()];
413
+ },
414
+ );
415
+ }
416
+
417
+ interface ChartUnderTest {
418
+ name: string;
419
+ isZoomed: boolean;
420
+ }
421
+
422
+ const CHARTS: Array<ChartUnderTest> = [
423
+ { name: "an unzoomed chart", isZoomed: false },
424
+ { name: "a zoomed chart", isZoomed: true },
425
+ ];
426
+
427
+ beforeEach(() => {
428
+ jest.useFakeTimers();
429
+ });
430
+
431
+ afterEach(() => {
432
+ cleanup();
433
+ jest.restoreAllMocks();
434
+ jest.useRealTimers();
435
+ });
436
+
437
+ describe("a double-click on a zoomed chart", () => {
438
+ test("the second press cancels the first click's zoom-in the moment it lands, renders nothing, and leaves the zoom-out to its release", () => {
439
+ const harness: Harness = renderSelection(true);
440
+
441
+ // The first click lights its bar while its zoom-in waits.
442
+ press(harness, BAR_A, mainButton(X_A, 1));
443
+ release(harness, BAR_A, mainButton(X_A, 1));
444
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
445
+ expect(jest.getTimerCount()).toBe(1);
446
+
447
+ /*
448
+ * The second press may land on that band: a render now could take
449
+ * the node away, and the browser's click and dblclick with it.
450
+ */
451
+ const rendersBefore: number = harness.renderCount();
452
+ press(harness, BAR_A, mainButton(X_A, 2));
453
+
454
+ expect(jest.getTimerCount()).toBe(0);
455
+ expect(harness.renderCount()).toBe(rendersBefore);
456
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
457
+ expect(harness.result.current.isDragging).toBe(false);
458
+
459
+ // However long the button is held, nothing zooms in, nor out yet.
460
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
461
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
462
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
463
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
464
+
465
+ release(harness, BAR_A, mainButton(X_A, 2));
466
+ letTimePass(0);
467
+
468
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
469
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
470
+ });
471
+
472
+ test("new bars landing under the second press: its release zooms nowhere, and the chart zooms out once, in the next task", () => {
473
+ const harness: Harness = renderSelection(true);
474
+
475
+ clickAThenPressAgain(harness);
476
+ const rendersBefore: number = harness.renderCount();
477
+
478
+ // The new bars land; recharts reports the one now under the pointer.
479
+ release(harness, LANDED_BAR, mainButton(X_A, 2));
480
+
481
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
482
+ // The release renders nothing either: the band stays for the zoom-out.
483
+ expect(harness.renderCount()).toBe(rendersBefore);
484
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
485
+ // Not yet: a dblclick the browser still sends comes first.
486
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
487
+
488
+ // None came (its node was re-keyed away), and the next task zooms out.
489
+ letTimePass(0);
490
+
491
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
492
+ expect(band(harness)).toEqual(NO_BAND);
493
+
494
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
495
+
496
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
497
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
498
+ });
499
+
500
+ test("the second press starts no selection: a move to another bar while it is held paints nothing, and releasing there zooms out", () => {
501
+ const harness: Harness = renderSelection(true);
502
+
503
+ clickAThenPressAgain(harness);
504
+ move(harness, BAR_C, pointerAt(X_C));
505
+
506
+ expect(harness.result.current.isDragging).toBe(false);
507
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
508
+
509
+ release(harness, BAR_C, mainButton(X_C, 2));
510
+ letTimePass(0);
511
+
512
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
513
+
514
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
515
+
516
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
517
+ });
518
+
519
+ const SECOND_RELEASES: Array<{
520
+ name: string;
521
+ release: (harness: Harness) => void;
522
+ }> = [
523
+ {
524
+ name: "over the bar it pressed",
525
+ release: (harness: Harness): void => {
526
+ release(harness, BAR_A, mainButton(X_A, 2));
527
+ },
528
+ },
529
+ {
530
+ name: "over a bar of new data that landed under it",
531
+ release: (harness: Harness): void => {
532
+ release(harness, LANDED_BAR, mainButton(X_A, 2));
533
+ },
534
+ },
535
+ {
536
+ name: "off the chart, where only the page hears it",
537
+ release: (): void => {
538
+ releaseOnThePage(2);
539
+ },
540
+ },
541
+ ];
542
+
543
+ for (const secondRelease of SECOND_RELEASES) {
544
+ test(`one that keeps its dblclick zooms out exactly once, its second press released ${secondRelease.name}`, () => {
545
+ const harness: Harness = renderSelection(true);
546
+
547
+ clickAThenPressAgain(harness);
548
+ secondRelease.release(harness);
549
+ // The browser's dblclick, straight after the release's click.
550
+ dblclick(harness);
551
+
552
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
553
+ expect(band(harness)).toEqual(NO_BAND);
554
+
555
+ // The zoom-out the release left for the next task stood down.
556
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
557
+
558
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
559
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
560
+ });
561
+ }
562
+
563
+ const DBLCLICK_FATES: Array<{ name: string; arrives: boolean }> = [
564
+ { name: "whether its dblclick is lost", arrives: false },
565
+ { name: "or delivered", arrives: true },
566
+ ];
567
+
568
+ for (const dblclickFate of DBLCLICK_FATES) {
569
+ test(`new bars landing under the FIRST press: neither click zooms in, and the double-click zooms out once, ${dblclickFate.name}`, () => {
570
+ const harness: Harness = renderSelection(true);
571
+
572
+ press(harness, BAR_A, mainButton(X_A, 1));
573
+ release(harness, LANDED_BAR, mainButton(X_A, 1));
574
+
575
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
576
+ expect(band(harness)).toEqual(NO_BAND);
577
+
578
+ press(harness, LANDED_BAR, mainButton(X_A, 2));
579
+ release(harness, LANDED_BAR, mainButton(X_A, 2));
580
+ if (dblclickFate.arrives) {
581
+ dblclick(harness);
582
+ }
583
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
584
+
585
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
586
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
587
+ });
588
+ }
589
+
590
+ test("a second press with no bar under it (the new data has fewer bars) is still the zoom-out", () => {
591
+ const harness: Harness = renderSelection(true);
592
+
593
+ press(harness, BAR_A, mainButton(X_A, 1));
594
+ release(harness, BAR_A, mainButton(X_A, 1));
595
+ press(harness, undefined, mainButton(X_A, 2));
596
+ release(harness, undefined, mainButton(X_A, 2));
597
+ letTimePass(0);
598
+
599
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
600
+ expect(band(harness)).toEqual(NO_BAND);
601
+
602
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
603
+
604
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
605
+ });
606
+
607
+ /*
608
+ * The chart never hears a release off it; the page does, through the
609
+ * listener the press added. That is still the end of the zoom-out press:
610
+ * it zooms out, and the band it kept goes with the zoom.
611
+ */
612
+ test("a second press released off the chart, which only the page hears, is still the zoom-out: once, in the next task, and nothing zooms in", () => {
613
+ const harness: Harness = renderSelection(true);
614
+
615
+ clickAThenPressAgain(harness);
616
+ releaseOnThePage(2);
617
+
618
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
619
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
620
+
621
+ letTimePass(0);
622
+
623
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
624
+ expect(band(harness)).toEqual(NO_BAND);
625
+
626
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
627
+
628
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
629
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
630
+ });
631
+
632
+ /*
633
+ * A second press whose release the chart never heard must leave nothing
634
+ * armed behind: a later release over the chart that no press on it
635
+ * began (a drag from elsewhere on the page, ending over the chart) is
636
+ * not the end of that press, and must not zoom out at some random later
637
+ * moment.
638
+ */
639
+ test("after a second press released off the chart, a release over it that no press on it began sets nothing off, and a later drag selects as ever", () => {
640
+ const harness: Harness = renderSelection(true);
641
+
642
+ clickAThenPressAgain(harness);
643
+ releaseOnThePage(2);
644
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
645
+ const zoomOutsBefore: number = harness.onZoomOut.mock.calls.length;
646
+
647
+ release(harness, BAR_B, mainButton(X_B, 1));
648
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
649
+
650
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(zoomOutsBefore);
651
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
652
+
653
+ press(harness, BAR_B, mainButton(X_B, 1));
654
+ move(harness, BAR_C, pointerAt(X_C));
655
+
656
+ expect(harness.result.current.isDragging).toBe(true);
657
+ expect(band(harness)).toEqual([BAR_B, BAR_C]);
658
+
659
+ release(harness, BAR_C, mainButton(X_C, 1));
660
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
661
+
662
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
663
+ windowOf(BAR_B, BAR_C),
664
+ ]);
665
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(zoomOutsBefore);
666
+ });
667
+
668
+ /*
669
+ * The window can lose a release altogether (it lost focus mid-press).
670
+ * The next press is a new gesture, and the release the page hears at
671
+ * its end is that press's, not the lost one's.
672
+ */
673
+ test("a second press whose release never comes, then a drag let go on the page: the drag selects, and nothing zooms out", () => {
674
+ const harness: Harness = renderSelection(true);
675
+
676
+ clickAThenPressAgain(harness);
677
+ press(harness, BAR_B, mainButton(X_B, 1));
678
+ move(harness, BAR_C, pointerAt(X_C));
679
+
680
+ expect(harness.result.current.isDragging).toBe(true);
681
+ expect(band(harness)).toEqual([BAR_B, BAR_C]);
682
+
683
+ releaseOnThePage(1);
684
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
685
+
686
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
687
+ windowOf(BAR_B, BAR_C),
688
+ ]);
689
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
690
+ expect(band(harness)).toEqual(NO_BAND);
691
+ expect(harness.result.current.isDragging).toBe(false);
692
+ });
693
+
694
+ test("a second press released over the chart, and heard by the page as well, zooms out once", () => {
695
+ const harness: Harness = renderSelection(true);
696
+
697
+ clickAThenPressAgain(harness);
698
+ release(harness, BAR_A, mainButton(X_A, 2));
699
+ // The same mouseup, bubbling on up to the page.
700
+ releaseOnThePage(2);
701
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
702
+
703
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
704
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
705
+ });
706
+
707
+ /*
708
+ * The refetch that started all this re-renders the host in between, with
709
+ * a handler that knows the latest window.
710
+ */
711
+ test("the zoom-out calls the onZoomOut of the host's newest render", () => {
712
+ const harness: Harness = renderSelection(true);
713
+
714
+ clickAThenPressAgain(harness);
715
+ release(harness, LANDED_BAR, mainButton(X_A, 2));
716
+
717
+ const newestZoomOut: MockFunction = getJestMockFunction();
718
+ harness.rerender({
719
+ onTimeRangeSelect: harness.onTimeRangeSelect,
720
+ onZoomOut: newestZoomOut,
721
+ bucketIntervalMs: MINUTE_MS,
722
+ });
723
+ letTimePass(0);
724
+
725
+ expect(newestZoomOut).toHaveBeenCalledTimes(1);
726
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
727
+ });
728
+
729
+ test("a chart unmounted before its zoom-out runs never zooms out", () => {
730
+ const harness: Harness = renderSelection(true);
731
+
732
+ clickAThenPressAgain(harness);
733
+ release(harness, LANDED_BAR, mainButton(X_A, 2));
734
+ harness.unmount();
735
+
736
+ expect(jest.getTimerCount()).toBe(0);
737
+
738
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
739
+
740
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
741
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
742
+ });
743
+ });
744
+
745
+ describe("a press whose bar changes under a still pointer", () => {
746
+ for (const chart of CHARTS) {
747
+ test(`on ${chart.name}, is not a drag, paints nothing and zooms nowhere`, () => {
748
+ const harness: Harness = renderSelection(chart.isZoomed);
749
+
750
+ press(harness, BAR_A, mainButton(X_A, 1));
751
+ // The new bars land: recharts reports the one now under the pointer.
752
+ move(harness, LANDED_BAR, pointerAt(X_A));
753
+
754
+ expect(harness.result.current.isDragging).toBe(false);
755
+ expect(band(harness)).toEqual(NO_BAND);
756
+
757
+ // A hand on a mouse is never quite still.
758
+ move(harness, LANDED_BAR, pointerAt(X_A + 2));
759
+
760
+ expect(harness.result.current.isDragging).toBe(false);
761
+ expect(band(harness)).toEqual(NO_BAND);
762
+
763
+ release(harness, LANDED_BAR, mainButton(X_A + 1, 1));
764
+
765
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
766
+ expect(harness.result.current.isDragging).toBe(false);
767
+ expect(band(harness)).toEqual(NO_BAND);
768
+
769
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
770
+
771
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
772
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
773
+ });
774
+ }
775
+
776
+ /*
777
+ * Pressed at the very edge of its bar, so a few pixels take the pointer
778
+ * onto the next one.
779
+ */
780
+ const DIRECTIONS: Array<{
781
+ name: string;
782
+ pressedBar: string;
783
+ pressedX: number;
784
+ otherBar: string;
785
+ sign: number;
786
+ }> = [
787
+ {
788
+ name: "rightwards",
789
+ pressedBar: BAR_A,
790
+ pressedX: X_A,
791
+ otherBar: BAR_B,
792
+ sign: 1,
793
+ },
794
+ {
795
+ name: "leftwards",
796
+ pressedBar: BAR_B,
797
+ pressedX: X_B,
798
+ otherBar: BAR_A,
799
+ sign: -1,
800
+ },
801
+ ];
802
+
803
+ for (const direction of DIRECTIONS) {
804
+ test(`a pointer moved ${direction.name} by HISTOGRAM_PRESS_STILLNESS_PX has not left its bar; one pixel further, onto another bar, is a drag`, () => {
805
+ const harness: Harness = renderSelection(false);
806
+
807
+ press(harness, direction.pressedBar, mainButton(direction.pressedX, 1));
808
+ move(
809
+ harness,
810
+ direction.otherBar,
811
+ pointerAt(
812
+ direction.pressedX + direction.sign * HISTOGRAM_PRESS_STILLNESS_PX,
813
+ ),
814
+ );
815
+
816
+ expect(harness.result.current.isDragging).toBe(false);
817
+ expect(band(harness)).toEqual(NO_BAND);
818
+
819
+ const draggedX: number =
820
+ direction.pressedX +
821
+ direction.sign * (HISTOGRAM_PRESS_STILLNESS_PX + 1);
822
+ move(harness, direction.otherBar, pointerAt(draggedX));
823
+
824
+ expect(harness.result.current.isDragging).toBe(true);
825
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
826
+
827
+ release(harness, direction.otherBar, mainButton(draggedX, 1));
828
+
829
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
830
+ windowOf(BAR_A, BAR_B),
831
+ ]);
832
+ });
833
+
834
+ /*
835
+ * A trackpad click, or a slightly unsteady mouse, lets go a pixel or
836
+ * two over the neighbour of the bar it pressed. It is still a click on
837
+ * the bar pressed - not dropped as if new data had landed under it,
838
+ * which a first cut of the #4116 fix did.
839
+ */
840
+ for (const chart of CHARTS) {
841
+ test(`on ${chart.name}, a click that drifts ${direction.name} across its bar's edge zooms into the bar it pressed`, () => {
842
+ const harness: Harness = renderSelection(chart.isZoomed);
843
+ const driftedX: number =
844
+ direction.pressedX + direction.sign * HISTOGRAM_PRESS_STILLNESS_PX;
845
+
846
+ press(harness, direction.pressedBar, mainButton(direction.pressedX, 1));
847
+ move(harness, direction.otherBar, pointerAt(driftedX));
848
+ release(harness, direction.otherBar, mainButton(driftedX, 1));
849
+
850
+ expect(harness.result.current.isDragging).toBe(false);
851
+
852
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
853
+
854
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
855
+ windowOf(direction.pressedBar, direction.pressedBar),
856
+ ]);
857
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
858
+ });
859
+ }
860
+ }
861
+ });
862
+
863
+ /*
864
+ * A still press on a bar right of where a shorter refetch ends (commit
865
+ * b6ec899df4). recharts clamps the stale index onto the new last bar, so
866
+ * the release reports another index as well as another bar - just what a
867
+ * hand's drift across an edge reports - and the hook took it for one: a
868
+ * click on the pressed bar, which zoomed into 10:25, a bar no longer on the
869
+ * chart. What tells the two apart is the pointer: a drift moves it; new
870
+ * bars land under a pointer that never moved at all, not by a pixel. A move
871
+ * Chrome makes up at the very same spot, when the page under a still
872
+ * pointer changes, does not count as one.
873
+ */
874
+ describe("a still press on a bar right of where a shorter refetch ends", () => {
875
+ const BETWEEN_PRESS_AND_RELEASE: Array<{
876
+ name: string;
877
+ between: (harness: Harness) => void;
878
+ // What recharts reports under the pointer at the release.
879
+ releasedOver: string;
880
+ }> = [
881
+ {
882
+ name: "with no move between (recharts clamps its stale index onto the new last bar)",
883
+ between: (): void => {
884
+ // Nothing: the new bars land, and the pointer stays where it is.
885
+ },
886
+ releasedOver: REFETCH_LAST_BAR,
887
+ },
888
+ {
889
+ name: "with a move Chrome makes up at the same spot once the new bars are drawn (recharts hit-tests them)",
890
+ between: (harness: Harness): void => {
891
+ move(harness, REFETCH_BAR_UNDER_POINTER, pointerAt(X_LATE));
892
+ },
893
+ releasedOver: REFETCH_BAR_UNDER_POINTER,
894
+ },
895
+ {
896
+ name: "with a move Chrome makes up at the same spot before the new bars land",
897
+ between: (harness: Harness): void => {
898
+ move(harness, LATE_BAR, pointerAt(X_LATE));
899
+ },
900
+ releasedOver: REFETCH_LAST_BAR,
901
+ },
902
+ ];
903
+
904
+ for (const chart of CHARTS) {
905
+ for (const path of BETWEEN_PRESS_AND_RELEASE) {
906
+ test(`on ${chart.name}, ${path.name}: zooms nowhere, paints nothing and leaves nothing waiting`, () => {
907
+ const harness: Harness = renderSelection(chart.isZoomed);
908
+
909
+ press(harness, LATE_BAR, mainButton(X_LATE, 1));
910
+ path.between(harness);
911
+
912
+ expect(harness.result.current.isDragging).toBe(false);
913
+ expect(band(harness)).toEqual(NO_BAND);
914
+
915
+ release(harness, path.releasedOver, mainButton(X_LATE, 1));
916
+
917
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
918
+ expect(band(harness)).toEqual(NO_BAND);
919
+ expect(harness.result.current.isDragging).toBe(false);
920
+ // No click waits out the double-click window to zoom in later.
921
+ expect(jest.getTimerCount()).toBe(0);
922
+
923
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
924
+
925
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
926
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
927
+ expect(band(harness)).toEqual(NO_BAND);
928
+ });
929
+ }
930
+
931
+ /*
932
+ * The same chart, the same bars, but the pointer really moved: two
933
+ * pixels right, over the next bar. A hand's drift - the index moves
934
+ * with it, as with the clamp - and still a click on the bar pressed.
935
+ */
936
+ test(`on ${chart.name}, a real move of 2px across the pressed bar's edge is still a click on the bar it pressed`, () => {
937
+ const harness: Harness = renderSelection(chart.isZoomed);
938
+
939
+ press(harness, LATE_BAR, mainButton(X_LATE, 1));
940
+ move(harness, BAR_AFTER_LATE_BAR, pointerAt(X_LATE + 2));
941
+
942
+ expect(harness.result.current.isDragging).toBe(false);
943
+ expect(band(harness)).toEqual(NO_BAND);
944
+
945
+ release(harness, BAR_AFTER_LATE_BAR, mainButton(X_LATE + 2, 1));
946
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
947
+
948
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
949
+ windowOf(LATE_BAR, LATE_BAR),
950
+ ]);
951
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
952
+ expect(band(harness)).toEqual(NO_BAND);
953
+ });
954
+ }
955
+ });
956
+
957
+ describe("drags and clicks select what they always did", () => {
958
+ for (const chart of CHARTS) {
959
+ test(`on ${chart.name}, a drag paints its band as it goes and zooms into both end bars whole, at once`, () => {
960
+ const harness: Harness = renderSelection(chart.isZoomed);
961
+
962
+ press(harness, BAR_A, mainButton(X_A, 1));
963
+ move(harness, BAR_B, pointerAt(X_B));
964
+
965
+ expect(harness.result.current.isDragging).toBe(true);
966
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
967
+
968
+ release(harness, BAR_B, mainButton(X_B, 1));
969
+
970
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
971
+ windowOf(BAR_A, BAR_B),
972
+ ]);
973
+ expect(harness.result.current.isDragging).toBe(false);
974
+ expect(band(harness)).toEqual(NO_BAND);
975
+
976
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
977
+
978
+ expect(harness.onTimeRangeSelect).toHaveBeenCalledTimes(1);
979
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
980
+ });
981
+
982
+ test(`on ${chart.name}, the same drag right to left paints its band in time order and zooms into the same window`, () => {
983
+ const harness: Harness = renderSelection(chart.isZoomed);
984
+
985
+ press(harness, BAR_B, mainButton(X_B, 1));
986
+ move(harness, BAR_A, pointerAt(X_A));
987
+
988
+ expect(harness.result.current.isDragging).toBe(true);
989
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
990
+
991
+ release(harness, BAR_A, mainButton(X_A, 1));
992
+
993
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
994
+ windowOf(BAR_A, BAR_B),
995
+ ]);
996
+ });
997
+
998
+ test(`on ${chart.name}, a flick released over another bar before any of its moves came in is a drag to that bar`, () => {
999
+ const harness: Harness = renderSelection(chart.isZoomed);
1000
+
1001
+ press(harness, BAR_A, mainButton(X_A, 1));
1002
+ release(harness, BAR_C, mainButton(X_C, 1));
1003
+
1004
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1005
+ windowOf(BAR_A, BAR_C),
1006
+ ]);
1007
+ expect(band(harness)).toEqual(NO_BAND);
1008
+
1009
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1010
+
1011
+ expect(harness.onTimeRangeSelect).toHaveBeenCalledTimes(1);
1012
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1013
+ });
1014
+ }
1015
+
1016
+ test("on an unzoomed chart, a still click zooms into its bar at once", () => {
1017
+ const harness: Harness = renderSelection(false);
1018
+
1019
+ press(harness, BAR_A, mainButton(X_A, 1));
1020
+ release(harness, BAR_A, mainButton(X_A, 1));
1021
+
1022
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1023
+ windowOf(BAR_A, BAR_A),
1024
+ ]);
1025
+ expect(band(harness)).toEqual(NO_BAND);
1026
+ });
1027
+
1028
+ test("on a zoomed chart, a still click zooms into its bar only once the double-click window has passed, the bar lit meanwhile", () => {
1029
+ const harness: Harness = renderSelection(true);
1030
+
1031
+ press(harness, BAR_A, mainButton(X_A, 1));
1032
+ release(harness, BAR_A, mainButton(X_A, 1));
1033
+
1034
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
1035
+
1036
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS - 1);
1037
+
1038
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1039
+
1040
+ letTimePass(1);
1041
+
1042
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1043
+ windowOf(BAR_A, BAR_A),
1044
+ ]);
1045
+ expect(band(harness)).toEqual(NO_BAND);
1046
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1047
+ });
1048
+ });
1049
+
1050
+ const EVENTLESS_HOSTS: Array<{
1051
+ name: string;
1052
+ event: null | undefined;
1053
+ }> = [
1054
+ { name: "no event at all", event: undefined },
1055
+ { name: "a null event", event: null },
1056
+ ];
1057
+
1058
+ for (const host of EVENTLESS_HOSTS) {
1059
+ describe(`a host that hands the handlers ${host.name} keeps the label-only reading`, () => {
1060
+ test("a press, a move onto another bar and a release there drag as they always did", () => {
1061
+ const harness: Harness = renderSelection(false);
1062
+
1063
+ press(harness, BAR_A, host.event);
1064
+ move(harness, BAR_B, host.event);
1065
+
1066
+ expect(harness.result.current.isDragging).toBe(true);
1067
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
1068
+
1069
+ release(harness, BAR_B, host.event);
1070
+
1071
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1072
+ windowOf(BAR_A, BAR_B),
1073
+ ]);
1074
+ expect(band(harness)).toEqual(NO_BAND);
1075
+ });
1076
+
1077
+ test("a press and a release over another bar, with no move between, drag from the one to the other", () => {
1078
+ const harness: Harness = renderSelection(false);
1079
+
1080
+ press(harness, BAR_A, host.event);
1081
+ release(harness, BAR_B, host.event);
1082
+
1083
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1084
+ windowOf(BAR_A, BAR_B),
1085
+ ]);
1086
+ });
1087
+
1088
+ test("a press and a release over its own bar zoom into that bar", () => {
1089
+ const harness: Harness = renderSelection(false);
1090
+
1091
+ press(harness, BAR_A, host.event);
1092
+ release(harness, BAR_A, host.event);
1093
+
1094
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1095
+ windowOf(BAR_A, BAR_A),
1096
+ ]);
1097
+ });
1098
+
1099
+ test("on a zoomed chart, a double-click zooms out through its dblclick alone, and neither click zooms in", () => {
1100
+ const harness: Harness = renderSelection(true);
1101
+
1102
+ press(harness, BAR_A, host.event);
1103
+ release(harness, BAR_A, host.event);
1104
+ press(harness, BAR_A, host.event);
1105
+ release(harness, BAR_A, host.event);
1106
+
1107
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1108
+
1109
+ dblclick(harness);
1110
+
1111
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1112
+ expect(band(harness)).toEqual(NO_BAND);
1113
+
1114
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1115
+
1116
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1117
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1118
+ });
1119
+ });
1120
+ }
1121
+
1122
+ describe("an unzoomed chart, which offers no zoom-out", () => {
1123
+ test("a press the browser counts as a double-click's second is an ordinary press: a still click zooms into its bar at once", () => {
1124
+ const harness: Harness = renderSelection(false);
1125
+
1126
+ press(harness, BAR_A, mainButton(X_A, 2));
1127
+ release(harness, BAR_A, mainButton(X_A, 2));
1128
+
1129
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1130
+ windowOf(BAR_A, BAR_A),
1131
+ ]);
1132
+
1133
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1134
+
1135
+ expect(harness.onTimeRangeSelect).toHaveBeenCalledTimes(1);
1136
+ });
1137
+
1138
+ test("a drag from a press the browser counts as a double-click's second selects as any drag does", () => {
1139
+ const harness: Harness = renderSelection(false);
1140
+
1141
+ press(harness, BAR_A, mainButton(X_A, 2));
1142
+ move(harness, BAR_B, pointerAt(X_B));
1143
+
1144
+ expect(harness.result.current.isDragging).toBe(true);
1145
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
1146
+
1147
+ release(harness, BAR_B, mainButton(X_B, 2));
1148
+
1149
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1150
+ windowOf(BAR_A, BAR_B),
1151
+ ]);
1152
+ });
1153
+
1154
+ test("a dblclick with no click waiting does nothing", () => {
1155
+ const harness: Harness = renderSelection(false);
1156
+ const rendersBefore: number = harness.renderCount();
1157
+
1158
+ dblclick(harness);
1159
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1160
+
1161
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1162
+ expect(band(harness)).toEqual(NO_BAND);
1163
+ expect(harness.renderCount()).toBe(rendersBefore);
1164
+ });
1165
+ });
1166
+
1167
+ /*
1168
+ * The window can lose the release of a double-click's second press: it
1169
+ * lost focus with the button down. The page-wide listener that press added
1170
+ * then hears the next release, which belongs to a later, unrelated click -
1171
+ * one the browser counts 1. That release ends the second press, and must
1172
+ * not zoom the chart out.
1173
+ */
1174
+ describe("a second press whose own release never comes", () => {
1175
+ const LATER_RELEASES: Array<{
1176
+ name: string;
1177
+ release: (harness: Harness) => void;
1178
+ }> = [
1179
+ {
1180
+ name: "a click elsewhere on the page, which only the page hears",
1181
+ release: (): void => {
1182
+ releaseOnThePage(1, X_C + 400);
1183
+ },
1184
+ },
1185
+ {
1186
+ name: "a press made elsewhere on the page and let go over the chart",
1187
+ release: (harness: Harness): void => {
1188
+ release(harness, BAR_B, mainButton(X_B, 1));
1189
+ },
1190
+ },
1191
+ ];
1192
+
1193
+ for (const laterRelease of LATER_RELEASES) {
1194
+ test(`the release of ${laterRelease.name}, counted 1, ends it without zooming out, and the page stops listening`, () => {
1195
+ const harness: Harness = renderSelection(true);
1196
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1197
+
1198
+ clickAThenPressAgain(harness);
1199
+
1200
+ // The second press listens on the page for its release.
1201
+ expect(pageListenersLeft(pageListeners)).toHaveLength(1);
1202
+
1203
+ laterRelease.release(harness);
1204
+
1205
+ expect(jest.getTimerCount()).toBe(0);
1206
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1207
+
1208
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1209
+
1210
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1211
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1212
+
1213
+ // Later releases on the page, however counted, set nothing off either.
1214
+ releaseOnThePage(1);
1215
+ releaseOnThePage(2);
1216
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1217
+
1218
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1219
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1220
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1221
+ });
1222
+ }
1223
+
1224
+ test("after that, a double-click zooms out once, as ever", () => {
1225
+ const harness: Harness = renderSelection(true);
1226
+
1227
+ clickAThenPressAgain(harness);
1228
+ releaseOnThePage(1, X_C + 400);
1229
+ // The reader carries on for a while before double-clicking the chart.
1230
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1231
+
1232
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1233
+
1234
+ clickAThenPressAgain(harness);
1235
+ release(harness, BAR_A, mainButton(X_A, 2));
1236
+ letTimePass(0);
1237
+
1238
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1239
+
1240
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1241
+
1242
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1243
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1244
+ });
1245
+
1246
+ /*
1247
+ * The second press dropped the first click's zoom-in but kept its band,
1248
+ * for the zoom-out to clear. When a later click's release ends that
1249
+ * press, no zoom-out is coming, so the band goes then. Before commit
1250
+ * b6ec899df4 nothing cleared it: bar A stayed lit, as if about to open,
1251
+ * however long one waited, until the next click on the chart replaced
1252
+ * it. Same cause as the last test of the next describe.
1253
+ */
1254
+ for (const laterRelease of LATER_RELEASES) {
1255
+ test(`the first click's band, kept for a zoom-out that is not coming, goes when the release of ${laterRelease.name} ends the press`, () => {
1256
+ const harness: Harness = renderSelection(true);
1257
+
1258
+ clickAThenPressAgain(harness);
1259
+
1260
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
1261
+
1262
+ laterRelease.release(harness);
1263
+
1264
+ expect(band(harness)).toEqual(NO_BAND);
1265
+
1266
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1267
+
1268
+ expect(band(harness)).toEqual(NO_BAND);
1269
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1270
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1271
+ });
1272
+ }
1273
+ });
1274
+
1275
+ /*
1276
+ * A click on a zoomed chart waits out the double-click window before it
1277
+ * zooms in. If the zoom ends another way meanwhile - Reset zoom, the time
1278
+ * picker, an empty state's own double-click - the host stops offering
1279
+ * onZoomOut, and that click must not zoom back into the window the reader
1280
+ * has just left, nor leave its bar lit.
1281
+ */
1282
+ describe("a zoom that ends another way while a click on the chart waits", () => {
1283
+ test("the click waiting to zoom in is dropped, and its band goes at once", () => {
1284
+ const harness: Harness = renderSelection(true);
1285
+
1286
+ press(harness, BAR_A, mainButton(X_A, 1));
1287
+ release(harness, BAR_A, mainButton(X_A, 1));
1288
+
1289
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
1290
+ expect(jest.getTimerCount()).toBe(1);
1291
+
1292
+ endZoomAnotherWay(harness);
1293
+
1294
+ expect(band(harness)).toEqual(NO_BAND);
1295
+ expect(jest.getTimerCount()).toBe(0);
1296
+
1297
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1298
+
1299
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1300
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1301
+ expect(band(harness)).toEqual(NO_BAND);
1302
+ });
1303
+
1304
+ test("a click after that zooms in at once, as on any unzoomed chart", () => {
1305
+ const harness: Harness = renderSelection(true);
1306
+
1307
+ press(harness, BAR_A, mainButton(X_A, 1));
1308
+ release(harness, BAR_A, mainButton(X_A, 1));
1309
+ endZoomAnotherWay(harness);
1310
+
1311
+ press(harness, BAR_B, mainButton(X_B, 1));
1312
+ release(harness, BAR_B, mainButton(X_B, 1));
1313
+
1314
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1315
+ windowOf(BAR_B, BAR_B),
1316
+ ]);
1317
+ expect(band(harness)).toEqual(NO_BAND);
1318
+
1319
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1320
+
1321
+ expect(harness.onTimeRangeSelect).toHaveBeenCalledTimes(1);
1322
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1323
+ });
1324
+
1325
+ const SECOND_PRESS_ENDINGS: Array<{
1326
+ name: string;
1327
+ end: (harness: Harness) => void;
1328
+ }> = [
1329
+ {
1330
+ name: "let go over the chart, its dblclick following",
1331
+ end: (harness: Harness): void => {
1332
+ release(harness, BAR_A, mainButton(X_A, 2));
1333
+ dblclick(harness);
1334
+ },
1335
+ },
1336
+ {
1337
+ name: "let go over the chart, its dblclick lost",
1338
+ end: (harness: Harness): void => {
1339
+ release(harness, LANDED_BAR, mainButton(X_A, 2));
1340
+ },
1341
+ },
1342
+ {
1343
+ name: "let go off the chart",
1344
+ end: (): void => {
1345
+ releaseOnThePage(2);
1346
+ },
1347
+ },
1348
+ ];
1349
+
1350
+ for (const ending of SECOND_PRESS_ENDINGS) {
1351
+ test(`withdrawn during a double-click's second press, then that press ${ending.name}: nothing zooms in, and nothing zooms out`, () => {
1352
+ const harness: Harness = renderSelection(true);
1353
+
1354
+ clickAThenPressAgain(harness);
1355
+ endZoomAnotherWay(harness);
1356
+ ending.end(harness);
1357
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1358
+
1359
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1360
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1361
+ });
1362
+ }
1363
+
1364
+ /*
1365
+ * The second press already dropped the first click's zoom-in and kept
1366
+ * its band for the zoom-out to clear. Before commit b6ec899df4 the hook
1367
+ * cleared the band when onZoomOut went only if a click was still
1368
+ * waiting, so this one stayed - through the press's release and every
1369
+ * timer, until the next click on the chart: bar A lit, as if about to
1370
+ * open, on a chart that had left the zoom. Clearing it at once is safe:
1371
+ * with the zoom gone, a dblclick lost to that render has nothing to do.
1372
+ */
1373
+ test("withdrawn during a double-click's second press, the band that press kept for the zoom-out goes at once", () => {
1374
+ const harness: Harness = renderSelection(true);
1375
+
1376
+ clickAThenPressAgain(harness);
1377
+
1378
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
1379
+
1380
+ endZoomAnotherWay(harness);
1381
+
1382
+ expect(band(harness)).toEqual(NO_BAND);
1383
+ });
1384
+
1385
+ /*
1386
+ * A drag's band is the reader's selection in progress, not one kept for
1387
+ * a zoom-out: it stays, and the drag still selects when let go.
1388
+ */
1389
+ test("withdrawn in the middle of a drag, the drag keeps its band and still selects when let go", () => {
1390
+ const harness: Harness = renderSelection(true);
1391
+
1392
+ press(harness, BAR_A, mainButton(X_A, 1));
1393
+ move(harness, BAR_B, pointerAt(X_B));
1394
+
1395
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
1396
+
1397
+ endZoomAnotherWay(harness);
1398
+
1399
+ expect(band(harness)).toEqual([BAR_A, BAR_B]);
1400
+ expect(harness.result.current.isDragging).toBe(true);
1401
+
1402
+ move(harness, BAR_C, pointerAt(X_C));
1403
+ release(harness, BAR_C, mainButton(X_C, 1));
1404
+
1405
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1406
+ windowOf(BAR_A, BAR_C),
1407
+ ]);
1408
+ expect(band(harness)).toEqual(NO_BAND);
1409
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1410
+ });
1411
+ });
1412
+
1413
+ describe("only the main button selects: a right-click opens a context menu", () => {
1414
+ for (const chart of CHARTS) {
1415
+ test(`on ${chart.name}, a right-button press on a bar and its release select nothing, zoom nowhere, and listen for nothing on the page`, () => {
1416
+ const harness: Harness = renderSelection(chart.isZoomed);
1417
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1418
+ const rendersBefore: number = harness.renderCount();
1419
+
1420
+ press(harness, BAR_A, otherButton(2, X_A, 1));
1421
+
1422
+ expect(mouseUpListeners(pageListeners.add.mock.calls)).toEqual([]);
1423
+
1424
+ release(harness, BAR_A, otherButton(2, X_A, 1));
1425
+
1426
+ expect(band(harness)).toEqual(NO_BAND);
1427
+ expect(harness.result.current.isDragging).toBe(false);
1428
+ expect(jest.getTimerCount()).toBe(0);
1429
+ expect(harness.renderCount()).toBe(rendersBefore);
1430
+
1431
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1432
+
1433
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1434
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1435
+ });
1436
+
1437
+ test(`on ${chart.name}, a right-button double-click on a bar neither zooms in nor zooms out`, () => {
1438
+ const harness: Harness = renderSelection(chart.isZoomed);
1439
+
1440
+ press(harness, BAR_A, otherButton(2, X_A, 1));
1441
+ release(harness, BAR_A, otherButton(2, X_A, 1));
1442
+ press(harness, BAR_A, otherButton(2, X_A, 2));
1443
+ release(harness, BAR_A, otherButton(2, X_A, 2));
1444
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1445
+
1446
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1447
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1448
+ expect(band(harness)).toEqual(NO_BAND);
1449
+ });
1450
+
1451
+ test(`on ${chart.name}, a middle-button drag across bars paints no band and selects nothing`, () => {
1452
+ const harness: Harness = renderSelection(chart.isZoomed);
1453
+
1454
+ press(harness, BAR_A, otherButton(1, X_A, 1));
1455
+ move(harness, BAR_B, pointerAt(X_B));
1456
+ move(harness, BAR_C, pointerAt(X_C));
1457
+
1458
+ expect(harness.result.current.isDragging).toBe(false);
1459
+ expect(band(harness)).toEqual(NO_BAND);
1460
+
1461
+ release(harness, BAR_C, otherButton(1, X_C, 1));
1462
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1463
+
1464
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1465
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1466
+ expect(band(harness)).toEqual(NO_BAND);
1467
+ });
1468
+ }
1469
+ });
1470
+
1471
+ /*
1472
+ * A press let go off the chart - dragged out past its top or bottom edge,
1473
+ * say - is heard only by the page. The page's release has no bar, but it
1474
+ * does say where the pointer is, and that decides click or drag as over
1475
+ * the chart: a press that stayed put is a click on the bar it pressed.
1476
+ */
1477
+ describe("a press let go off the chart, where only the page hears it", () => {
1478
+ const STILL_RELEASES: Array<{ name: string; offsetPx: number }> = [
1479
+ { name: "straight above or below where it went down", offsetPx: 0 },
1480
+ {
1481
+ name: `${HISTOGRAM_PRESS_STILLNESS_PX}px right of where it went down`,
1482
+ offsetPx: HISTOGRAM_PRESS_STILLNESS_PX,
1483
+ },
1484
+ {
1485
+ name: `${HISTOGRAM_PRESS_STILLNESS_PX}px left of where it went down`,
1486
+ offsetPx: -HISTOGRAM_PRESS_STILLNESS_PX,
1487
+ },
1488
+ ];
1489
+
1490
+ for (const stillRelease of STILL_RELEASES) {
1491
+ test(`on an unzoomed chart, one let go ${stillRelease.name} is a click on the bar it pressed, and zooms into it at once`, () => {
1492
+ const harness: Harness = renderSelection(false);
1493
+
1494
+ press(harness, BAR_A, mainButton(X_A, 1));
1495
+ releaseOnThePage(1, X_A + stillRelease.offsetPx);
1496
+
1497
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1498
+ windowOf(BAR_A, BAR_A),
1499
+ ]);
1500
+ expect(band(harness)).toEqual(NO_BAND);
1501
+ expect(harness.result.current.isDragging).toBe(false);
1502
+
1503
+ // The press is over: the page's next release is nothing to it.
1504
+ releaseOnThePage(1, X_A + stillRelease.offsetPx);
1505
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1506
+
1507
+ expect(harness.onTimeRangeSelect).toHaveBeenCalledTimes(1);
1508
+ });
1509
+ }
1510
+
1511
+ test("on a zoomed chart, one let go straight above or below where it went down waits out the double-click window, its bar lit, then zooms into that bar", () => {
1512
+ const harness: Harness = renderSelection(true);
1513
+
1514
+ press(harness, BAR_A, mainButton(X_A, 1));
1515
+ releaseOnThePage(1, X_A);
1516
+
1517
+ expect(band(harness)).toEqual([BAR_A, BAR_A]);
1518
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1519
+
1520
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS);
1521
+
1522
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1523
+ windowOf(BAR_A, BAR_A),
1524
+ ]);
1525
+ expect(band(harness)).toEqual(NO_BAND);
1526
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1527
+ });
1528
+
1529
+ for (const chart of CHARTS) {
1530
+ test(`on ${chart.name}, one let go far away after crossing bars is a drag to the last bar it crossed, as before`, () => {
1531
+ const harness: Harness = renderSelection(chart.isZoomed);
1532
+
1533
+ press(harness, BAR_A, mainButton(X_A, 1));
1534
+ move(harness, BAR_B, pointerAt(X_B));
1535
+ move(harness, BAR_C, pointerAt(X_C));
1536
+
1537
+ expect(harness.result.current.isDragging).toBe(true);
1538
+ expect(band(harness)).toEqual([BAR_A, BAR_C]);
1539
+
1540
+ releaseOnThePage(1, X_C + 400);
1541
+
1542
+ expect(selections(harness.onTimeRangeSelect)).toEqual([
1543
+ windowOf(BAR_A, BAR_C),
1544
+ ]);
1545
+ expect(band(harness)).toEqual(NO_BAND);
1546
+ expect(harness.result.current.isDragging).toBe(false);
1547
+
1548
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1549
+
1550
+ expect(harness.onTimeRangeSelect).toHaveBeenCalledTimes(1);
1551
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1552
+ });
1553
+ }
1554
+ });
1555
+
1556
+ /*
1557
+ * A box standing in for the chart - its loader while a window loads, its
1558
+ * empty box when the window holds nothing - takes the double-click as the
1559
+ * chart does, through placeholderProps (commit b6ec899df4). Right after a
1560
+ * zoom the chart replaces its loader at any moment, a double-click in
1561
+ * progress included, and then the browser sends no dblclick: the node the
1562
+ * second press went down on is gone. So the box's own second press is the
1563
+ * zoom-out's press, and its release - over the chart that took the box's
1564
+ * place, whose handler hands the hook a bar of the new data, or anywhere
1565
+ * on the page - zooms out in the next task, as a second press on the
1566
+ * chart's own bars does.
1567
+ */
1568
+ describe("a press on the box standing in for the chart (placeholderProps)", () => {
1569
+ const RELEASES_AFTER_THE_SWAP: Array<{
1570
+ name: string;
1571
+ release: (harness: Harness) => void;
1572
+ }> = [
1573
+ {
1574
+ name: "over the chart that took the box's place",
1575
+ release: (harness: Harness): void => {
1576
+ release(harness, BAR_B, mainButton(X_B, 2));
1577
+ },
1578
+ },
1579
+ {
1580
+ name: "anywhere on the page",
1581
+ release: (): void => {
1582
+ releaseOnThePage(2, X_B);
1583
+ },
1584
+ },
1585
+ ];
1586
+
1587
+ for (const releaseAfterTheSwap of RELEASES_AFTER_THE_SWAP) {
1588
+ test(`on a zoomed chart, the second press of a double-click on the box, let go ${releaseAfterTheSwap.name}, zooms out once, in the next task, with no dblclick`, () => {
1589
+ const harness: Harness = renderSelection(true);
1590
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1591
+
1592
+ // The first click: nothing to it, and nothing listened for.
1593
+ pressPlaceholder(harness, mainButton(X_B, 1));
1594
+
1595
+ expect(mouseUpListeners(pageListeners.add.mock.calls)).toEqual([]);
1596
+
1597
+ // The second press listens on the page for its release.
1598
+ pressPlaceholder(harness, mainButton(X_B, 2));
1599
+
1600
+ expect(pageListenersLeft(pageListeners)).toHaveLength(1);
1601
+
1602
+ // The chart has replaced the box by the release.
1603
+ releaseAfterTheSwap.release(harness);
1604
+
1605
+ // Not yet: a dblclick the browser still sends would come first.
1606
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1607
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1608
+
1609
+ letTimePass(0);
1610
+
1611
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1612
+
1613
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1614
+
1615
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1616
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1617
+ expect(band(harness)).toEqual(NO_BAND);
1618
+ });
1619
+ }
1620
+
1621
+ test("on a zoomed chart, the same double-click with its dblclick delivered after all zooms out exactly once", () => {
1622
+ const harness: Harness = renderSelection(true);
1623
+
1624
+ pressPlaceholder(harness, mainButton(X_B, 1));
1625
+ pressPlaceholder(harness, mainButton(X_B, 2));
1626
+ release(harness, BAR_B, mainButton(X_B, 2));
1627
+ // The browser's dblclick, straight after the release's click.
1628
+ dblclick(harness);
1629
+
1630
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1631
+
1632
+ // The zoom-out the release left for the next task stood down.
1633
+ letTimePass(0);
1634
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1635
+
1636
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1637
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1638
+ });
1639
+
1640
+ test("on a zoomed chart, a double-click wholly on the box, its dblclick through placeholderProps, zooms out exactly once", () => {
1641
+ const harness: Harness = renderSelection(true);
1642
+
1643
+ pressPlaceholder(harness, mainButton(X_B, 1));
1644
+ releaseOnThePage(1, X_B);
1645
+ pressPlaceholder(harness, mainButton(X_B, 2));
1646
+ // The box is no chart: only the page hears its release.
1647
+ releaseOnThePage(2, X_B);
1648
+ act(() => {
1649
+ harness.result.current.placeholderProps.onDoubleClick();
1650
+ });
1651
+
1652
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1653
+
1654
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1655
+
1656
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1657
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1658
+ });
1659
+
1660
+ /*
1661
+ * A click on a bar was waiting out the double-click window when the
1662
+ * chart went and the box came (a refetch that clears the bars first):
1663
+ * the box's second press drops it, as the chart's own would have.
1664
+ */
1665
+ test("on a zoomed chart, a click on a bar still waiting to zoom in when the box took the chart's place: the box's second press drops it, and its release zooms out once", () => {
1666
+ const harness: Harness = renderSelection(true);
1667
+
1668
+ press(harness, BAR_A, mainButton(X_A, 1));
1669
+ release(harness, BAR_A, mainButton(X_A, 1));
1670
+
1671
+ expect(jest.getTimerCount()).toBe(1);
1672
+
1673
+ pressPlaceholder(harness, mainButton(X_A, 2));
1674
+
1675
+ expect(jest.getTimerCount()).toBe(0);
1676
+
1677
+ releaseOnThePage(2, X_A);
1678
+ letTimePass(0);
1679
+
1680
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1681
+ expect(band(harness)).toEqual(NO_BAND);
1682
+
1683
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1684
+
1685
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1686
+ expect(harness.onZoomOut).toHaveBeenCalledTimes(1);
1687
+ });
1688
+
1689
+ test("on an unzoomed chart, which offers no zoom-out, the same second press is nothing: it listens for nothing, and its release over the chart selects nothing", () => {
1690
+ const harness: Harness = renderSelection(false);
1691
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1692
+
1693
+ pressPlaceholder(harness, mainButton(X_B, 1));
1694
+ pressPlaceholder(harness, mainButton(X_B, 2));
1695
+
1696
+ expect(mouseUpListeners(pageListeners.add.mock.calls)).toEqual([]);
1697
+
1698
+ release(harness, BAR_B, mainButton(X_B, 2));
1699
+ releaseOnThePage(2, X_B);
1700
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1701
+
1702
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1703
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1704
+ expect(band(harness)).toEqual(NO_BAND);
1705
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1706
+ });
1707
+
1708
+ test("on a zoomed chart, a press on the box counted 1 is nothing: it listens for nothing, and no release after it sets anything off", () => {
1709
+ const harness: Harness = renderSelection(true);
1710
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1711
+
1712
+ pressPlaceholder(harness, mainButton(X_B, 1));
1713
+
1714
+ expect(mouseUpListeners(pageListeners.add.mock.calls)).toEqual([]);
1715
+
1716
+ release(harness, BAR_B, mainButton(X_B, 1));
1717
+ releaseOnThePage(1, X_B);
1718
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1719
+
1720
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1721
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1722
+ expect(band(harness)).toEqual(NO_BAND);
1723
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1724
+ });
1725
+
1726
+ test("on a zoomed chart, a right-button second press on the box is nothing: it listens for nothing, and its release zooms nothing out", () => {
1727
+ const harness: Harness = renderSelection(true);
1728
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1729
+
1730
+ pressPlaceholder(harness, otherButton(2, X_B, 1));
1731
+ pressPlaceholder(harness, otherButton(2, X_B, 2));
1732
+
1733
+ expect(mouseUpListeners(pageListeners.add.mock.calls)).toEqual([]);
1734
+
1735
+ release(harness, BAR_B, otherButton(2, X_B, 2));
1736
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1737
+
1738
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1739
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1740
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1741
+ });
1742
+
1743
+ test("on a zoomed chart whose zoom ends another way during the box's second press, its release zooms nothing out and leaves nothing listening", () => {
1744
+ const harness: Harness = renderSelection(true);
1745
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1746
+
1747
+ pressPlaceholder(harness, mainButton(X_B, 1));
1748
+ pressPlaceholder(harness, mainButton(X_B, 2));
1749
+ endZoomAnotherWay(harness);
1750
+ releaseOnThePage(2, X_B);
1751
+ letTimePass(DOUBLE_CLICK_DISAMBIGUATION_MS * 2);
1752
+
1753
+ expect(harness.onZoomOut).not.toHaveBeenCalled();
1754
+ expect(harness.onTimeRangeSelect).not.toHaveBeenCalled();
1755
+ expect(pageListenersLeft(pageListeners)).toEqual([]);
1756
+ });
1757
+ });