@oneuptime/common 12.0.4 → 12.0.5

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 (374) hide show
  1. package/Models/DatabaseModels/AIRun.ts +38 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +30 -0
  3. package/Models/DatabaseModels/Dashboard.ts +28 -0
  4. package/Models/DatabaseModels/Incident.ts +2 -0
  5. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  7. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  8. package/Models/DatabaseModels/IncidentFeed.ts +30 -0
  9. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  10. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  11. package/Models/DatabaseModels/LogSavedView.ts +33 -0
  12. package/Models/DatabaseModels/Project.ts +231 -9
  13. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  14. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  15. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
  17. package/Models/DatabaseModels/StatusPage.ts +28 -0
  18. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  19. package/Server/API/AIAgentDataAPI.ts +44 -55
  20. package/Server/API/AIInvestigationAPI.ts +175 -17
  21. package/Server/API/DashboardAPI.ts +10 -10
  22. package/Server/API/StatusPageAPI.ts +38 -19
  23. package/Server/EnvironmentConfig.ts +16 -0
  24. package/Server/Infrastructure/ClickhouseConfig.ts +15 -1
  25. package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Services/AIRunService.ts +78 -10
  33. package/Server/Services/AIService.ts +94 -22
  34. package/Server/Services/AlertFeedService.ts +5 -0
  35. package/Server/Services/AnalyticsDatabaseService.ts +29 -0
  36. package/Server/Services/DatabaseService.ts +43 -2
  37. package/Server/Services/IncidentFeedService.ts +5 -0
  38. package/Server/Services/LlmLogService.ts +46 -2
  39. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
  40. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
  41. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
  42. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
  43. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
  44. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
  45. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +223 -22
  46. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +25 -24
  47. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
  48. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
  49. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +25 -24
  50. package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
  51. package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
  52. package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
  53. package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
  54. package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
  55. package/Server/Utils/AI/SRE/README.md +1 -1
  56. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
  57. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
  58. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
  59. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
  60. package/Server/Utils/Monitor/MonitorMetricUtil.ts +46 -0
  61. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  62. package/Server/Utils/PasswordHash.ts +28 -4
  63. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
  64. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
  65. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
  66. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1184 -0
  67. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
  68. package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
  69. package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
  70. package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
  71. package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
  72. package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
  73. package/Tests/Server/API/AIInvestigationAPI.test.ts +594 -0
  74. package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
  75. package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
  76. package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
  77. package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
  78. package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
  79. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +396 -20
  80. package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
  81. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
  82. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
  83. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
  84. package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
  85. package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
  86. package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
  87. package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
  88. package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
  89. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
  90. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
  91. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
  92. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
  93. package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
  94. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
  95. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
  96. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
  97. package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
  98. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
  99. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +313 -20
  100. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  101. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
  102. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
  103. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
  104. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
  105. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
  106. package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
  107. package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
  108. package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
  109. package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
  110. package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
  111. package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
  112. package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
  113. package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
  114. package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
  115. package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
  116. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
  117. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
  118. package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
  119. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
  120. package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
  121. package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
  122. package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
  123. package/Tests/Types/Permission.test.ts +137 -0
  124. package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
  125. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
  126. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
  127. package/Tests/UI/Components/ErrorBoundary.test.tsx +158 -0
  128. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
  129. package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
  130. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
  131. package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
  132. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
  133. package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
  134. package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
  135. package/Tests/UI/Utils/Project.test.ts +31 -0
  136. package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
  137. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
  138. package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
  139. package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
  140. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
  141. package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
  142. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
  143. package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
  144. package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
  145. package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
  146. package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
  147. package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
  148. package/Types/AI/CodeFixTaskContext.ts +63 -3
  149. package/Types/AI/CodeFixTaskType.ts +3 -3
  150. package/Types/HashedString.ts +4 -3
  151. package/Types/Monitor/CriteriaFilter.ts +4 -0
  152. package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
  153. package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
  155. package/Types/Monitor/MonitorMetricType.ts +8 -0
  156. package/Types/Monitor/MonitorStep.ts +10 -1
  157. package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
  158. package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
  159. package/Types/Permission.ts +23 -175
  160. package/Types/Probe/ProbeMonitorResponse.ts +6 -0
  161. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
  162. package/UI/Components/ErrorBoundary.tsx +138 -11
  163. package/UI/Components/Feed/FeedItem.tsx +9 -2
  164. package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
  165. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  166. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
  167. package/UI/Components/MoreMenu/MoreMenu.tsx +285 -97
  168. package/UI/Components/MoreMenu/MoreMenuItem.tsx +8 -5
  169. package/UI/Utils/DownloadFile.ts +16 -8
  170. package/UI/Utils/ErrorSupportBundle.ts +805 -0
  171. package/UI/Utils/ModelAPI/ModelAPI.ts +19 -8
  172. package/UI/Utils/Project.ts +4 -0
  173. package/UI/Utils/StatusPage.ts +8 -2
  174. package/UI/Utils/UseDashboardGridDnd.ts +917 -0
  175. package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
  176. package/Utils/Dashboard/GridLayout.ts +523 -0
  177. package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
  178. package/Utils/Monitor/MonitorMetricType.ts +44 -2
  179. package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
  180. package/Utils/StatusPage/GroupTree.ts +157 -66
  181. package/Utils/StatusPage/ResourceUptime.ts +37 -2
  182. package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
  183. package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
  184. package/build/dist/Models/DatabaseModels/AIRun.js +39 -1
  185. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
  187. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
  189. package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  191. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  193. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  195. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  197. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
  199. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  201. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  203. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
  205. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/Project.js +241 -11
  207. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  209. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  211. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  212. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  213. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  214. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
  215. package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
  216. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  218. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/AIInvestigationAPI.js +111 -11
  222. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +8 -5
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/StatusPageAPI.js +29 -11
  226. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  227. package/build/dist/Server/EnvironmentConfig.js +14 -0
  228. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  229. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +12 -1
  230. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
  232. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  245. package/build/dist/Server/Services/AIRunService.js +58 -12
  246. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  247. package/build/dist/Server/Services/AIService.js +74 -21
  248. package/build/dist/Server/Services/AIService.js.map +1 -1
  249. package/build/dist/Server/Services/AlertFeedService.js +3 -0
  250. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +29 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/DatabaseService.js +21 -2
  254. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  255. package/build/dist/Server/Services/IncidentFeedService.js +3 -0
  256. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  257. package/build/dist/Server/Services/LlmLogService.js +22 -1
  258. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  259. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  260. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  261. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
  262. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
  263. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
  264. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  265. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
  266. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  267. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
  268. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  269. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
  270. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  271. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +156 -28
  272. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  273. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +21 -19
  274. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  275. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
  276. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  277. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
  278. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  279. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +21 -19
  280. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  281. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
  282. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
  283. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
  284. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  285. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
  286. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  287. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
  288. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
  289. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
  290. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
  291. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
  292. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  293. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
  294. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  295. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
  296. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  297. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
  298. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  299. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  300. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  301. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  302. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  303. package/build/dist/Server/Utils/PasswordHash.js +25 -4
  304. package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
  305. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
  306. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
  307. package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
  308. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  309. package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
  310. package/build/dist/Types/HashedString.js +4 -3
  311. package/build/dist/Types/HashedString.js.map +1 -1
  312. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  313. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  314. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
  315. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
  316. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
  317. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  318. package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
  319. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  320. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  321. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  322. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
  323. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
  324. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
  325. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
  326. package/build/dist/Types/Permission.js +23 -155
  327. package/build/dist/Types/Permission.js.map +1 -1
  328. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
  329. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  330. package/build/dist/UI/Components/ErrorBoundary.js +67 -8
  331. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  332. package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
  333. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  334. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
  335. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  336. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  338. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
  339. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  340. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +193 -65
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +1 -1
  343. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  344. package/build/dist/UI/Utils/DownloadFile.js +15 -8
  345. package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
  346. package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
  347. package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
  348. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -8
  349. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  350. package/build/dist/UI/Utils/Project.js +3 -0
  351. package/build/dist/UI/Utils/Project.js.map +1 -1
  352. package/build/dist/UI/Utils/StatusPage.js +4 -4
  353. package/build/dist/UI/Utils/StatusPage.js.map +1 -1
  354. package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
  355. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
  356. package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
  357. package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
  358. package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
  359. package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
  360. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
  361. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
  362. package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
  363. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  364. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
  365. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
  366. package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
  367. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
  368. package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
  369. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  370. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
  371. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
  372. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
  373. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
  374. package/package.json +1 -1
@@ -660,6 +660,68 @@ describe("ResourceGroupSection", () => {
660
660
  });
661
661
  });
662
662
 
663
+ /*
664
+ * A status page whose hierarchy is built but whose monitors are not added
665
+ * yet. This used to be a page that rendered nothing at all; the section has
666
+ * to draw the structure on its own, because that structure is the only thing
667
+ * the operator has put there so far.
668
+ */
669
+ describe("a hierarchy with no resources anywhere in it", () => {
670
+ type RenderEmptyFunction = (depth: number) => ReactElement;
671
+
672
+ const renderEmpty: RenderEmptyFunction = (depth: number): ReactElement => {
673
+ return (
674
+ <ResourceGroupSection
675
+ depth={depth}
676
+ name={`Level ${depth}`}
677
+ isInitiallyExpanded={true}
678
+ subGroupCount={depth < 3 ? 1 : 0}
679
+ testId={`empty-level-${depth}`}
680
+ subGroupsElement={depth < 3 ? renderEmpty(depth + 1) : undefined}
681
+ hasOwnResources={false}
682
+ />
683
+ );
684
+ };
685
+
686
+ test("every level is still drawn, and named", () => {
687
+ render(renderEmpty(0));
688
+
689
+ for (let depth: number = 0; depth <= 3; depth++) {
690
+ expect(screen.getByTestId(`empty-level-${depth}`)).toBeInTheDocument();
691
+ expect(screen.getByText(`Level ${depth}`)).toBeInTheDocument();
692
+ }
693
+ });
694
+
695
+ test("each level is still a working disclosure control", () => {
696
+ render(renderEmpty(0));
697
+
698
+ const header: HTMLElement = screen
699
+ .getByTestId("empty-level-1")
700
+ .querySelector("[data-testid='status-page-group-header']")!;
701
+
702
+ expect(header.getAttribute("aria-expanded")).toBe("true");
703
+ expect(screen.getByTestId("empty-level-3")).toBeInTheDocument();
704
+
705
+ fireEvent.click(header);
706
+
707
+ expect(header.getAttribute("aria-expanded")).toBe("false");
708
+ expect(screen.queryByTestId("empty-level-3")).not.toBeInTheDocument();
709
+ expect(screen.getByTestId("empty-level-1")).toBeInTheDocument();
710
+ });
711
+
712
+ /*
713
+ * No resources means no rolled up reading to show. A group header with a
714
+ * number on it that came from nowhere is worse than a header without one.
715
+ */
716
+ test("no rollup is drawn when the caller has none to give", () => {
717
+ render(renderEmpty(0));
718
+
719
+ expect(
720
+ screen.queryByTestId("status-page-group-rollup"),
721
+ ).not.toBeInTheDocument();
722
+ });
723
+ });
724
+
663
725
  describe("test ids", () => {
664
726
  test("the caller can override the default", () => {
665
727
  renderSection({ testId: "my-group" });
@@ -0,0 +1,237 @@
1
+ import "@testing-library/jest-dom";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { describe, expect, it } from "@jest/globals";
4
+ import * as React from "react";
5
+ import Hostname from "../../../Types/API/Hostname";
6
+ import MonitorType from "../../../Types/Monitor/MonitorType";
7
+ import ObjectID from "../../../Types/ObjectID";
8
+ import Port from "../../../Types/Port";
9
+ import ProbeMonitorResponse from "../../../Types/Probe/ProbeMonitorResponse";
10
+ import { RequestFailedPhase } from "../../../Types/Probe/RequestFailedDetails";
11
+ import SummaryInfo from "../../../../App/FeatureSet/Dashboard/src/Components/Monitor/SummaryView/SummaryInfo";
12
+
13
+ const MONITORED_AT: Date = new Date("2026-08-07T12:30:00.000Z");
14
+
15
+ function buildResponse(
16
+ overrides: Partial<ProbeMonitorResponse> = {},
17
+ ): ProbeMonitorResponse {
18
+ return {
19
+ projectId: new ObjectID("11111111-1111-4111-8111-111111111111"),
20
+ monitorId: new ObjectID("22222222-2222-4222-8222-222222222222"),
21
+ monitorStepId: new ObjectID("33333333-3333-4333-8333-333333333333"),
22
+ probeId: new ObjectID("44444444-4444-4444-8444-444444444444"),
23
+ monitorDestination: new Hostname("db.example.com"),
24
+ monitorDestinationPort: new Port(5432),
25
+ isOnline: true,
26
+ responseTimeInMs: 31.27,
27
+ failureCause: "",
28
+ monitoredAt: MONITORED_AT,
29
+ ...overrides,
30
+ };
31
+ }
32
+
33
+ function renderSummary(
34
+ monitorType: MonitorType,
35
+ response: ProbeMonitorResponse,
36
+ ): void {
37
+ render(
38
+ <SummaryInfo
39
+ monitorType={monitorType}
40
+ probeMonitorResponses={[response]}
41
+ probeName="London Probe"
42
+ />,
43
+ );
44
+ }
45
+
46
+ describe("Port monitor summary", () => {
47
+ it("labels the existing response time as the DNS plus TCP total", () => {
48
+ renderSummary(
49
+ MonitorType.Port,
50
+ buildResponse({
51
+ portTimings: {
52
+ dnsLookupInMs: 8.12,
53
+ tcpConnectInMs: 23.15,
54
+ totalConnectionInMs: 31.27,
55
+ },
56
+ }),
57
+ );
58
+
59
+ expect(screen.getByText("db.example.com:5432")).toBeInTheDocument();
60
+ expect(
61
+ screen.getByText("Total Connection Time (DNS + TCP)"),
62
+ ).toBeInTheDocument();
63
+ expect(screen.getByText("31.27 ms")).toBeInTheDocument();
64
+ expect(screen.queryByText("Response Time (in ms)")).not.toBeInTheDocument();
65
+ });
66
+
67
+ it("shows the measured DNS and TCP phases", () => {
68
+ renderSummary(
69
+ MonitorType.Port,
70
+ buildResponse({
71
+ portTimings: {
72
+ dnsLookupInMs: 8.123,
73
+ tcpConnectInMs: 23.147,
74
+ totalConnectionInMs: 31.27,
75
+ },
76
+ }),
77
+ );
78
+
79
+ expect(screen.getByText("Connection Phase Breakdown")).toBeInTheDocument();
80
+ expect(screen.getByText("DNS Lookup")).toBeInTheDocument();
81
+ expect(screen.getByText("8.12 ms")).toBeInTheDocument();
82
+ expect(screen.getByText("TCP Connect")).toBeInTheDocument();
83
+ expect(screen.getByText("23.15 ms")).toBeInTheDocument();
84
+ });
85
+
86
+ it("omits DNS for an IP target while retaining the TCP phase", () => {
87
+ renderSummary(
88
+ MonitorType.Port,
89
+ buildResponse({
90
+ monitorDestination: new Hostname("192.0.2.10"),
91
+ responseTimeInMs: 4.25,
92
+ portTimings: {
93
+ tcpConnectInMs: 4.25,
94
+ totalConnectionInMs: 4.25,
95
+ },
96
+ }),
97
+ );
98
+
99
+ expect(screen.getByText("192.0.2.10:5432")).toBeInTheDocument();
100
+ expect(screen.queryByText("DNS Lookup")).not.toBeInTheDocument();
101
+ expect(screen.getByText("TCP Connect")).toBeInTheDocument();
102
+ });
103
+
104
+ it("renders valid zero-duration measurements instead of missing data", () => {
105
+ renderSummary(
106
+ MonitorType.Port,
107
+ buildResponse({
108
+ responseTimeInMs: 0,
109
+ portTimings: {
110
+ dnsLookupInMs: 0,
111
+ tcpConnectInMs: 0,
112
+ totalConnectionInMs: 0,
113
+ },
114
+ }),
115
+ );
116
+
117
+ expect(screen.getAllByText("0 ms")).toHaveLength(3);
118
+ });
119
+
120
+ it("does not invent a missing TCP duration after a DNS-only failure", () => {
121
+ renderSummary(
122
+ MonitorType.Port,
123
+ buildResponse({
124
+ isOnline: false,
125
+ responseTimeInMs: 19,
126
+ failureCause: "DNS lookup failed",
127
+ portTimings: {
128
+ dnsLookupInMs: 19,
129
+ totalConnectionInMs: 19,
130
+ },
131
+ }),
132
+ );
133
+
134
+ expect(screen.getByText("DNS Lookup")).toBeInTheDocument();
135
+ expect(screen.queryByText("TCP Connect")).not.toBeInTheDocument();
136
+ });
137
+
138
+ it("keeps older results useful when no phase timings were recorded", () => {
139
+ renderSummary(MonitorType.Port, buildResponse());
140
+
141
+ expect(
142
+ screen.getByText("Total Connection Time (DNS + TCP)"),
143
+ ).toBeInTheDocument();
144
+ expect(screen.getByText("31.27 ms")).toBeInTheDocument();
145
+ expect(
146
+ screen.queryByText("Connection Phase Breakdown"),
147
+ ).not.toBeInTheDocument();
148
+ });
149
+
150
+ it("uses the compatibility response time for the displayed total", () => {
151
+ renderSummary(
152
+ MonitorType.Port,
153
+ buildResponse({
154
+ responseTimeInMs: 25,
155
+ portTimings: {
156
+ dnsLookupInMs: 5,
157
+ tcpConnectInMs: 20,
158
+ totalConnectionInMs: 999,
159
+ },
160
+ }),
161
+ );
162
+
163
+ expect(screen.getByText("25 ms")).toBeInTheDocument();
164
+ expect(screen.queryByText("999 ms")).not.toBeInTheDocument();
165
+ });
166
+
167
+ it("retains failure details and retry evidence in the dedicated view", () => {
168
+ renderSummary(
169
+ MonitorType.Port,
170
+ buildResponse({
171
+ isOnline: false,
172
+ failureCause: "Connection refused",
173
+ requestFailedDetails: {
174
+ failedPhase: RequestFailedPhase.TCPConnection,
175
+ errorCode: "ECONNREFUSED",
176
+ errorDescription: "The target rejected the TCP connection.",
177
+ },
178
+ probeAttempts: [
179
+ {
180
+ attemptNumber: 1,
181
+ attemptedAt: MONITORED_AT,
182
+ responseReceivedAt: MONITORED_AT,
183
+ responseTimeInMs: 11,
184
+ isOnline: false,
185
+ failureCause: "Connection refused",
186
+ },
187
+ {
188
+ attemptNumber: 2,
189
+ attemptedAt: MONITORED_AT,
190
+ responseReceivedAt: MONITORED_AT,
191
+ responseTimeInMs: 12,
192
+ isOnline: false,
193
+ failureCause: "Connection refused",
194
+ },
195
+ ],
196
+ totalAttempts: 2,
197
+ }),
198
+ );
199
+
200
+ expect(screen.getByText("Offline")).toBeInTheDocument();
201
+ expect(screen.getAllByText("Connection refused").length).toBeGreaterThan(0);
202
+ expect(screen.getByText("TCP Connection")).toBeInTheDocument();
203
+ expect(screen.getByText("ECONNREFUSED")).toBeInTheDocument();
204
+ expect(
205
+ screen.getByText("The target rejected the TCP connection."),
206
+ ).toBeInTheDocument();
207
+ expect(screen.getByText("Retry Attempts")).toBeInTheDocument();
208
+ expect(screen.getByText("Attempt 1/2")).toBeInTheDocument();
209
+ expect(screen.getByText("Attempt 2/2")).toBeInTheDocument();
210
+ });
211
+ });
212
+
213
+ describe("Ping and IP summaries", () => {
214
+ it.each([MonitorType.Ping, MonitorType.IP])(
215
+ "keeps %s on the existing Ping view",
216
+ (monitorType: MonitorType) => {
217
+ renderSummary(
218
+ monitorType,
219
+ buildResponse({
220
+ portTimings: {
221
+ dnsLookupInMs: 8,
222
+ tcpConnectInMs: 23,
223
+ totalConnectionInMs: 31,
224
+ },
225
+ }),
226
+ );
227
+
228
+ expect(screen.getByText("Response Time (in ms)")).toBeInTheDocument();
229
+ expect(
230
+ screen.queryByText("Total Connection Time (DNS + TCP)"),
231
+ ).not.toBeInTheDocument();
232
+ expect(
233
+ screen.queryByText("Connection Phase Breakdown"),
234
+ ).not.toBeInTheDocument();
235
+ },
236
+ );
237
+ });
@@ -0,0 +1,91 @@
1
+ import "@testing-library/jest-dom/extend-expect";
2
+ import { render, RenderResult } from "@testing-library/react";
3
+ /*
4
+ * The Dashboard has its own copy of react, so a component imported from there
5
+ * would otherwise call hooks on a DIFFERENT React instance than the one
6
+ * react-dom renders with. Common's jest moduleNameMapper pins react and
7
+ * react-dom to this project's single copy for every importer, which is what
8
+ * makes importing Dashboard source from here work (same arrangement as
9
+ * Common/Tests/UI/Rum/ReplayStage.test.tsx).
10
+ */
11
+ import * as React from "react";
12
+ import { describe, expect, it } from "@jest/globals";
13
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
14
+ import OneUptimeDate from "../../../Types/Date";
15
+ import TelemetrySnapshotWindowAlert from "../../../../App/FeatureSet/Dashboard/src/Components/Telemetry/TelemetrySnapshotWindowAlert";
16
+
17
+ /*
18
+ * The badge that tells someone reading an incident WHICH slice of time the
19
+ * telemetry cards below are showing. Its absence on the log card is a large
20
+ * part of why the rolling-window bug went unnoticed for so long: with nothing
21
+ * on screen claiming a window, an hour of unrelated logs read as the
22
+ * incident's own logs.
23
+ *
24
+ * Two behaviours matter and neither is reachable from a pure test: that it
25
+ * renders the stored window, and that it renders NOTHING when there is no
26
+ * window — the backwards-compatible path for incidents created before monitors
27
+ * stamped one, where a badge would be worse than no badge.
28
+ */
29
+ describe("TelemetrySnapshotWindowAlert", () => {
30
+ const START_DATE: Date = new Date("2026-08-04T10:00:00.000Z");
31
+ const END_DATE: Date = new Date("2026-08-04T10:01:00.000Z");
32
+
33
+ it("renders the snapshot window", () => {
34
+ const result: RenderResult = render(
35
+ <TelemetrySnapshotWindowAlert
36
+ window={new InBetween<Date>(START_DATE, END_DATE)}
37
+ />,
38
+ );
39
+
40
+ /*
41
+ * Assert against the app's own formatter rather than a hard-coded string:
42
+ * the output is timezone- and 12/24h-preference dependent, and pinning a
43
+ * literal here would make this test a clock/locale trap rather than a
44
+ * check that the window reaches the screen.
45
+ */
46
+ const expected: string = OneUptimeDate.getInBetweenDatesAsFormattedString(
47
+ new InBetween<Date>(START_DATE, END_DATE),
48
+ );
49
+
50
+ expect(result.container.textContent).toContain(expected);
51
+
52
+ // Both edges are named, not just the start.
53
+ expect(expected).toContain(" - ");
54
+ });
55
+
56
+ it("renders nothing when the incident stored no window", () => {
57
+ for (const value of [null, undefined]) {
58
+ const result: RenderResult = render(
59
+ <TelemetrySnapshotWindowAlert window={value} />,
60
+ );
61
+
62
+ expect(result.container.textContent).toBe("");
63
+ result.unmount();
64
+ }
65
+ });
66
+
67
+ it("renders a window restored from storage, whose bounds are ISO strings", () => {
68
+ /*
69
+ * The shape the incident page actually holds if hydration were ever
70
+ * skipped. The formatter normalizes strings, so the badge must still show
71
+ * real dates rather than "Invalid Date".
72
+ */
73
+ const result: RenderResult = render(
74
+ <TelemetrySnapshotWindowAlert
75
+ window={
76
+ new InBetween<Date>(
77
+ START_DATE.toISOString() as unknown as Date,
78
+ END_DATE.toISOString() as unknown as Date,
79
+ )
80
+ }
81
+ />,
82
+ );
83
+
84
+ expect(result.container.textContent).not.toContain("Invalid Date");
85
+ expect(result.container.textContent).toContain(
86
+ OneUptimeDate.getInBetweenDatesAsFormattedString(
87
+ new InBetween<Date>(START_DATE, END_DATE),
88
+ ),
89
+ );
90
+ });
91
+ });
@@ -0,0 +1,103 @@
1
+ import downloadFile from "../../../UI/Utils/DownloadFile";
2
+
3
+ describe("downloadFile", () => {
4
+ let createObjectUrlMock: jest.Mock;
5
+ let revokeObjectUrlMock: jest.Mock;
6
+ let clickSpy: jest.SpyInstance;
7
+ let capturedAnchor: HTMLAnchorElement | null;
8
+ let originalCreateObjectUrl: typeof window.URL.createObjectURL | undefined;
9
+ let originalRevokeObjectUrl: typeof window.URL.revokeObjectURL | undefined;
10
+
11
+ beforeEach(() => {
12
+ capturedAnchor = null;
13
+ originalCreateObjectUrl = window.URL.createObjectURL;
14
+ originalRevokeObjectUrl = window.URL.revokeObjectURL;
15
+ createObjectUrlMock = jest.fn().mockReturnValue("blob:support-bundle");
16
+ revokeObjectUrlMock = jest.fn();
17
+
18
+ window.URL.createObjectURL = createObjectUrlMock;
19
+ window.URL.revokeObjectURL = revokeObjectUrlMock;
20
+
21
+ clickSpy = jest
22
+ .spyOn(HTMLAnchorElement.prototype, "click")
23
+ .mockImplementation(() => {});
24
+
25
+ const originalCreateElement: typeof document.createElement =
26
+ document.createElement.bind(document);
27
+
28
+ jest.spyOn(document, "createElement").mockImplementation(((
29
+ tagName: string,
30
+ ): HTMLElement => {
31
+ const element: HTMLElement = originalCreateElement(tagName);
32
+
33
+ if (tagName === "a") {
34
+ capturedAnchor = element as HTMLAnchorElement;
35
+ }
36
+
37
+ return element;
38
+ }) as typeof document.createElement);
39
+ });
40
+
41
+ afterEach(() => {
42
+ jest.restoreAllMocks();
43
+
44
+ if (originalCreateObjectUrl) {
45
+ window.URL.createObjectURL = originalCreateObjectUrl;
46
+ } else {
47
+ delete (window.URL as { createObjectURL?: unknown }).createObjectURL;
48
+ }
49
+
50
+ if (originalRevokeObjectUrl) {
51
+ window.URL.revokeObjectURL = originalRevokeObjectUrl;
52
+ } else {
53
+ delete (window.URL as { revokeObjectURL?: unknown }).revokeObjectURL;
54
+ }
55
+ });
56
+
57
+ test("creates a Blob with the requested MIME type for string content", () => {
58
+ downloadFile({
59
+ content: "diagnostics",
60
+ filename: "support.json",
61
+ mimeType: "application/json;charset=utf-8;",
62
+ });
63
+
64
+ const blob: Blob = createObjectUrlMock.mock.calls[0]![0] as Blob;
65
+ expect(blob).toBeInstanceOf(Blob);
66
+ expect(blob.type).toBe("application/json;charset=utf-8;");
67
+ });
68
+
69
+ test("reuses Blob content rather than wrapping it in another Blob", () => {
70
+ const blob: Blob = new Blob(["diagnostics"], {
71
+ type: "application/json",
72
+ });
73
+
74
+ downloadFile({ content: blob, filename: "support.json" });
75
+
76
+ expect(createObjectUrlMock).toHaveBeenCalledWith(blob);
77
+ });
78
+
79
+ test("clicks a safe download anchor and cleans it up", () => {
80
+ downloadFile({ content: "diagnostics", filename: "support.json" });
81
+
82
+ expect(clickSpy).toHaveBeenCalledTimes(1);
83
+ expect(capturedAnchor).not.toBeNull();
84
+ expect(capturedAnchor!.href).toContain("blob:support-bundle");
85
+ expect(capturedAnchor!.download).toBe("support.json");
86
+ expect(capturedAnchor!.rel).toBe("noopener");
87
+ expect(document.body.contains(capturedAnchor)).toBe(false);
88
+ expect(revokeObjectUrlMock).toHaveBeenCalledWith("blob:support-bundle");
89
+ });
90
+
91
+ test("still removes the anchor and revokes the object URL when click fails", () => {
92
+ clickSpy.mockImplementation(() => {
93
+ throw new Error("Download blocked");
94
+ });
95
+
96
+ expect(() => {
97
+ downloadFile({ content: "diagnostics", filename: "support.json" });
98
+ }).toThrow("Download blocked");
99
+
100
+ expect(document.body.contains(capturedAnchor)).toBe(false);
101
+ expect(revokeObjectUrlMock).toHaveBeenCalledWith("blob:support-bundle");
102
+ });
103
+ });