@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
@@ -0,0 +1,151 @@
1
+ import SnmpOid from "../../../../Types/Monitor/SnmpMonitor/SnmpOid";
2
+ import SnmpVendorTemplateUtil, {
3
+ SnmpVendorTemplate,
4
+ SnmpVendorTemplates,
5
+ } from "../../../../Types/Monitor/SnmpMonitor/SnmpVendorTemplate";
6
+
7
+ describe("SnmpVendorTemplateUtil", () => {
8
+ describe("getAll", () => {
9
+ test("returns every registered template", () => {
10
+ expect(SnmpVendorTemplateUtil.getAll()).toBe(SnmpVendorTemplates);
11
+ expect(SnmpVendorTemplateUtil.getAll().length).toBeGreaterThan(0);
12
+ });
13
+
14
+ test("every template has an id, label, description and at least one oid", () => {
15
+ const seenIds: Set<string> = new Set<string>();
16
+
17
+ for (const template of SnmpVendorTemplateUtil.getAll()) {
18
+ expect(template.id.length).toBeGreaterThan(0);
19
+ expect(template.label.length).toBeGreaterThan(0);
20
+ expect(template.description.length).toBeGreaterThan(0);
21
+ expect(template.oids.length).toBeGreaterThan(0);
22
+
23
+ // Ids are unique so getById is unambiguous.
24
+ expect(seenIds.has(template.id)).toBe(false);
25
+ seenIds.add(template.id);
26
+
27
+ // Each oid within a template carries an OID string.
28
+ for (const oid of template.oids) {
29
+ expect(oid.oid.length).toBeGreaterThan(0);
30
+ }
31
+ }
32
+ });
33
+ });
34
+
35
+ describe("getById", () => {
36
+ test("returns the matching template", () => {
37
+ const first: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
38
+ expect(SnmpVendorTemplateUtil.getById(first.id)).toBe(first);
39
+ });
40
+
41
+ test("returns undefined for an unknown id", () => {
42
+ expect(SnmpVendorTemplateUtil.getById("does-not-exist")).toBeUndefined();
43
+ });
44
+ });
45
+
46
+ describe("mergeOids", () => {
47
+ test("returns the existing list unchanged for an unknown template id", () => {
48
+ const existing: Array<SnmpOid> = [{ oid: "1.2.3", name: "custom" }];
49
+
50
+ const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
51
+ existing,
52
+ "does-not-exist",
53
+ );
54
+
55
+ expect(merged).toBe(existing);
56
+ });
57
+
58
+ test("appends all of a template's oids onto an empty list", () => {
59
+ const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
60
+
61
+ const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
62
+ [],
63
+ template.id,
64
+ );
65
+
66
+ expect(
67
+ merged.map((o: SnmpOid) => {
68
+ return o.oid;
69
+ }),
70
+ ).toEqual(
71
+ template.oids.map((o: SnmpOid) => {
72
+ return o.oid;
73
+ }),
74
+ );
75
+ });
76
+
77
+ test("does not duplicate an oid that already exists", () => {
78
+ const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
79
+ const firstOid: SnmpOid = template.oids[0]!;
80
+
81
+ const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
82
+ [{ oid: firstOid.oid, name: "already here" }],
83
+ template.id,
84
+ );
85
+
86
+ const occurrences: number = merged.filter((o: SnmpOid) => {
87
+ return o.oid === firstOid.oid;
88
+ }).length;
89
+ expect(occurrences).toBe(1);
90
+
91
+ // The pre-existing entry is preserved (not overwritten by the template's).
92
+ expect(merged[0]).toEqual({ oid: firstOid.oid, name: "already here" });
93
+
94
+ // The merged list has one entry per distinct oid in existing ∪ template.
95
+ expect(merged.length).toBe(template.oids.length);
96
+ });
97
+
98
+ test("is idempotent — applying the same template twice adds nothing new", () => {
99
+ const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
100
+
101
+ const once: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
102
+ [],
103
+ template.id,
104
+ );
105
+ const twice: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
106
+ once,
107
+ template.id,
108
+ );
109
+
110
+ expect(
111
+ twice.map((o: SnmpOid) => {
112
+ return o.oid;
113
+ }),
114
+ ).toEqual(
115
+ once.map((o: SnmpOid) => {
116
+ return o.oid;
117
+ }),
118
+ );
119
+ });
120
+
121
+ test("preserves the caller's existing oids and appends only new ones", () => {
122
+ const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
123
+ const custom: SnmpOid = { oid: "9.9.9.9", name: "custom metric" };
124
+
125
+ const merged: Array<SnmpOid> = SnmpVendorTemplateUtil.mergeOids(
126
+ [custom],
127
+ template.id,
128
+ );
129
+
130
+ // Existing custom oid stays first, template oids follow.
131
+ expect(merged[0]).toEqual(custom);
132
+ expect(merged.length).toBe(1 + template.oids.length);
133
+ for (const oid of template.oids) {
134
+ expect(
135
+ merged.map((o: SnmpOid) => {
136
+ return o.oid;
137
+ }),
138
+ ).toContain(oid.oid);
139
+ }
140
+ });
141
+
142
+ test("does not mutate the input array", () => {
143
+ const template: SnmpVendorTemplate = SnmpVendorTemplateUtil.getAll()[0]!;
144
+ const existing: Array<SnmpOid> = [{ oid: "9.9.9.9" }];
145
+
146
+ SnmpVendorTemplateUtil.mergeOids(existing, template.id);
147
+
148
+ expect(existing).toEqual([{ oid: "9.9.9.9" }]);
149
+ });
150
+ });
151
+ });
@@ -1,6 +1,7 @@
1
1
  import ObjectID from "../../Types/ObjectID";
2
2
  import Permission, {
3
3
  PermissionHelper,
4
+ PermissionProps,
4
5
  UserPermission,
5
6
  } from "../../Types/Permission";
6
7
  import PermissionScope from "../../Types/Database/AccessControl/PermissionScope";
@@ -15,6 +16,142 @@ describe("Permission", () => {
15
16
  });
16
17
  });
17
18
 
19
+ /*
20
+ * Integrity of the permission catalogue itself.
21
+ *
22
+ * Both invariants below were violated in this file for a long time and neither
23
+ * failure was visible from the outside:
24
+ *
25
+ * - CreateScheduledMaintenanceTemplateOwnerUser and ...OwnerTeam were declared
26
+ * with the string values of their non-template counterparts, so two distinct
27
+ * capabilities were one permission wearing two names. Granting either
28
+ * conferred both, and owner rows feed the Owned permission scope, so it
29
+ * widened access to scheduled maintenance events the holder did not own.
30
+ *
31
+ * - Sixteen permissions had their PermissionProps object listed twice, so the
32
+ * dashboard's granular picker offered each of them twice.
33
+ *
34
+ * A duplicate is silent by construction: TypeScript is happy, every lookup
35
+ * still resolves, and the only symptom is a picker entry that does not do what
36
+ * its label says. These tests are the tripwire.
37
+ */
38
+ describe("Permission catalogue integrity", () => {
39
+ test("no two enum members share a string value", () => {
40
+ const seen: Map<string, string> = new Map();
41
+ const collisions: Array<string> = [];
42
+
43
+ for (const [name, value] of Object.entries(Permission)) {
44
+ const previous: string | undefined = seen.get(value);
45
+
46
+ if (previous) {
47
+ collisions.push(`${previous} and ${name} both = "${value}"`);
48
+ continue;
49
+ }
50
+
51
+ seen.set(value, name);
52
+ }
53
+
54
+ expect(collisions).toEqual([]);
55
+ });
56
+
57
+ test("getAllPermissionProps has no duplicate permission entries", () => {
58
+ const counts: Map<Permission, number> = new Map();
59
+
60
+ for (const props of PermissionHelper.getAllPermissionProps()) {
61
+ counts.set(props.permission, (counts.get(props.permission) || 0) + 1);
62
+ }
63
+
64
+ const duplicates: Array<string> = [];
65
+ for (const [permission, count] of counts) {
66
+ if (count > 1) {
67
+ duplicates.push(`${permission} x${count}`);
68
+ }
69
+ }
70
+
71
+ expect(duplicates).toEqual([]);
72
+ });
73
+
74
+ test("every props entry round-trips through getTitle and getDescription", () => {
75
+ /*
76
+ * getTitle/getDescription take the FIRST match while
77
+ * getAllPermissionPropsAsDictionary is last-wins. With duplicates present
78
+ * those two disagree, and the dashboard and the API reference then show
79
+ * different text for the same permission.
80
+ */
81
+ for (const props of PermissionHelper.getAllPermissionProps()) {
82
+ expect(PermissionHelper.getTitle(props.permission)).toBe(props.title);
83
+ expect(PermissionHelper.getDescription(props.permission)).toBe(
84
+ props.description,
85
+ );
86
+ }
87
+ });
88
+
89
+ test("the props dictionary covers every props entry", () => {
90
+ const dictionary: Record<string, PermissionProps> =
91
+ PermissionHelper.getAllPermissionPropsAsDictionary() as Record<
92
+ string,
93
+ PermissionProps
94
+ >;
95
+
96
+ for (const props of PermissionHelper.getAllPermissionProps()) {
97
+ expect(dictionary[props.permission]).toBeDefined();
98
+ expect(dictionary[props.permission]!.title).toBe(props.title);
99
+ }
100
+ });
101
+
102
+ test("no props entry is keyed to a permission from another resource family", () => {
103
+ /*
104
+ * The template-owner bug was a mis-keyed entry: the title said "Template"
105
+ * while the permission pointed at the non-template member. Catch the shape
106
+ * generically — a title containing "Template" must belong to a permission
107
+ * whose name contains "Template", and vice versa.
108
+ */
109
+ const mismatched: Array<string> = [];
110
+
111
+ for (const props of PermissionHelper.getAllPermissionProps()) {
112
+ const titleSaysTemplate: boolean = props.title.includes("Template");
113
+ const keySaysTemplate: boolean = props.permission
114
+ .toString()
115
+ .includes("Template");
116
+
117
+ if (titleSaysTemplate !== keySaysTemplate) {
118
+ mismatched.push(`${props.permission} titled "${props.title}"`);
119
+ }
120
+ }
121
+
122
+ expect(mismatched).toEqual([]);
123
+ });
124
+
125
+ test("every tenant-assignable permission has a non-empty title and description", () => {
126
+ const bad: Array<string> = [];
127
+
128
+ for (const props of PermissionHelper.getTenantPermissionProps()) {
129
+ if (!props.title.trim() || !props.description.trim()) {
130
+ bad.push(props.permission.toString());
131
+ }
132
+ }
133
+
134
+ expect(bad).toEqual([]);
135
+ });
136
+
137
+ test("roles and granular permissions partition the assignable set", () => {
138
+ const assignable: Array<PermissionProps> =
139
+ PermissionHelper.getTenantPermissionProps();
140
+ const roles: Array<PermissionProps> =
141
+ PermissionHelper.getRolePermissionProps();
142
+
143
+ const granular: Array<PermissionProps> = assignable.filter(
144
+ (props: PermissionProps) => {
145
+ return !props.isRolePermission;
146
+ },
147
+ );
148
+
149
+ expect(roles.length + granular.length).toBe(assignable.length);
150
+ expect(roles.length).toBeGreaterThan(10);
151
+ expect(granular.length).toBeGreaterThan(100);
152
+ });
153
+ });
154
+
18
155
  const makeRow: (
19
156
  permission: Permission,
20
157
  scope: PermissionScope | undefined,
@@ -0,0 +1,101 @@
1
+ import SessionReplayMaskingMode, {
2
+ doesMaskingModeRecordReadableContent,
3
+ parseSessionReplayMaskingMode,
4
+ } from "../../../Types/Rum/SessionReplayMaskingMode";
5
+ import { describe, expect, test } from "@jest/globals";
6
+
7
+ describe("SessionReplayMaskingMode", () => {
8
+ const allModes: Array<SessionReplayMaskingMode> = Object.values(
9
+ SessionReplayMaskingMode,
10
+ );
11
+
12
+ describe("parseSessionReplayMaskingMode", () => {
13
+ test("returns each valid mode when given its own value", () => {
14
+ expect(
15
+ parseSessionReplayMaskingMode(
16
+ SessionReplayMaskingMode.MaskSensitiveInputsOnly,
17
+ ),
18
+ ).toBe(SessionReplayMaskingMode.MaskSensitiveInputsOnly);
19
+ expect(
20
+ parseSessionReplayMaskingMode(SessionReplayMaskingMode.MaskInputsOnly),
21
+ ).toBe(SessionReplayMaskingMode.MaskInputsOnly);
22
+ expect(
23
+ parseSessionReplayMaskingMode(SessionReplayMaskingMode.MaskAllText),
24
+ ).toBe(SessionReplayMaskingMode.MaskAllText);
25
+ });
26
+
27
+ test("round-trips every enum member", () => {
28
+ for (const mode of allModes) {
29
+ expect(parseSessionReplayMaskingMode(mode)).toBe(mode);
30
+ }
31
+ });
32
+
33
+ test("fails closed to MaskAllText for unrecognised values", () => {
34
+ const unknownValues: Array<unknown> = [
35
+ undefined,
36
+ null,
37
+ "",
38
+ "maskallText",
39
+ "MaskEverything",
40
+ "mask_inputs_only",
41
+ 0,
42
+ 1,
43
+ true,
44
+ false,
45
+ {},
46
+ [],
47
+ { mode: SessionReplayMaskingMode.MaskInputsOnly },
48
+ ];
49
+ for (const value of unknownValues) {
50
+ expect(parseSessionReplayMaskingMode(value)).toBe(
51
+ SessionReplayMaskingMode.MaskAllText,
52
+ );
53
+ }
54
+ });
55
+
56
+ test("is case-sensitive and does not coerce near-matches to a relaxed mode", () => {
57
+ // A relaxed-looking-but-wrong string must NOT downgrade privacy.
58
+ expect(parseSessionReplayMaskingMode("maskinputsonly")).toBe(
59
+ SessionReplayMaskingMode.MaskAllText,
60
+ );
61
+ expect(parseSessionReplayMaskingMode("MaskSensitiveInputsOnly ")).toBe(
62
+ SessionReplayMaskingMode.MaskAllText,
63
+ );
64
+ });
65
+ });
66
+
67
+ describe("doesMaskingModeRecordReadableContent", () => {
68
+ test("is true for both relaxed modes", () => {
69
+ expect(
70
+ doesMaskingModeRecordReadableContent(
71
+ SessionReplayMaskingMode.MaskSensitiveInputsOnly,
72
+ ),
73
+ ).toBe(true);
74
+ expect(
75
+ doesMaskingModeRecordReadableContent(
76
+ SessionReplayMaskingMode.MaskInputsOnly,
77
+ ),
78
+ ).toBe(true);
79
+ });
80
+
81
+ test("is false only for the wireframe mode", () => {
82
+ expect(
83
+ doesMaskingModeRecordReadableContent(
84
+ SessionReplayMaskingMode.MaskAllText,
85
+ ),
86
+ ).toBe(false);
87
+ });
88
+
89
+ test("MaskAllText is the single mode that does not record readable content", () => {
90
+ const recordingModes: Array<SessionReplayMaskingMode> = allModes.filter(
91
+ (mode: SessionReplayMaskingMode) => {
92
+ return doesMaskingModeRecordReadableContent(mode);
93
+ },
94
+ );
95
+ expect(recordingModes).not.toContain(
96
+ SessionReplayMaskingMode.MaskAllText,
97
+ );
98
+ expect(recordingModes.length).toBe(allModes.length - 1);
99
+ });
100
+ });
101
+ });
@@ -152,17 +152,17 @@ const renderCard: RenderCardFunction = (): void => {
152
152
  placeholder: "Every severity",
153
153
  },
154
154
  {
155
- field: { aiMaxConcurrentInvestigations: true },
155
+ field: { incidentAiMaxConcurrentInvestigations: true },
156
156
  stepId: "limits",
157
- title: "Max Concurrent Investigations",
157
+ title: "Max Concurrent Incident Investigations",
158
158
  required: false,
159
159
  fieldType: FormFieldSchemaType.Number,
160
160
  placeholder: "3",
161
161
  },
162
162
  {
163
- field: { aiDailyFixTaskLimit: true },
163
+ field: { incidentAiDailyFixTaskLimit: true },
164
164
  stepId: "fix-tasks",
165
- title: "Daily AI Fix Task Limit",
165
+ title: "Daily Incident AI Fix Task Limit",
166
166
  required: false,
167
167
  fieldType: FormFieldSchemaType.Number,
168
168
  placeholder: "25",
@@ -280,6 +280,6 @@ describe("AI investigation settings card", () => {
280
280
  * Only the first step's fields are on screen - which is the point of
281
281
  * steps, and proves stepId actually took rather than being ignored.
282
282
  */
283
- expect(screen.queryByText("Daily AI Fix Task Limit")).toBeNull();
283
+ expect(screen.queryByText("Daily Incident AI Fix Task Limit")).toBeNull();
284
284
  });
285
285
  });
@@ -283,7 +283,7 @@ describe("CardModelDetail edit modal", () => {
283
283
  expect(submitted.model.shouldAutoEnableProbeOnNewMonitors).toBe(false);
284
284
  });
285
285
 
286
- it("confirms the save so a successful edit is not just a modal that vanished", async () => {
286
+ it("closes the editor without showing a toast after a successful edit", async () => {
287
287
  getItemMock.mockResolvedValue(loadedProbe());
288
288
 
289
289
  renderProbeCard();
@@ -303,13 +303,11 @@ describe("CardModelDetail edit modal", () => {
303
303
 
304
304
  await waitFor(
305
305
  () => {
306
- expect(showToastMock).toHaveBeenCalled();
306
+ expect(screen.queryByText("Save Changes")).toBeNull();
307
307
  },
308
308
  { timeout: WAIT_TIMEOUT },
309
309
  );
310
310
 
311
- const toast: any = (showToastMock.mock.calls[0] as Array<any>)[0];
312
-
313
- expect(toast.title).toEqual("Changes saved");
311
+ expect(showToastMock).not.toHaveBeenCalled();
314
312
  });
315
313
  });
@@ -6,9 +6,21 @@ import ErrorBoundary, {
6
6
  isChunkLoadError,
7
7
  markChunkLoadErrorReload,
8
8
  } from "../../../UI/Components/ErrorBoundary";
9
+ import downloadFile from "../../../UI/Utils/DownloadFile";
10
+ import { ErrorSupportBundle } from "../../../UI/Utils/ErrorSupportBundle";
9
11
  import { fireEvent, render, screen } from "@testing-library/react";
10
12
  import React, { FunctionComponent, ReactElement, useEffect } from "react";
11
13
 
14
+ jest.mock("../../../UI/Utils/DownloadFile", () => {
15
+ return {
16
+ __esModule: true,
17
+ default: jest.fn(),
18
+ };
19
+ });
20
+
21
+ const downloadFileMock: jest.MockedFunction<typeof downloadFile> =
22
+ downloadFile as jest.MockedFunction<typeof downloadFile>;
23
+
12
24
  /*
13
25
  * The blank white screen users hit is React unmounting the entire tree when a
14
26
  * render throws and nothing catches it. These tests pin down the containment
@@ -70,6 +82,7 @@ describe("ErrorBoundary", () => {
70
82
 
71
83
  beforeEach(() => {
72
84
  consoleErrorSpy.mockClear();
85
+ downloadFileMock.mockReset();
73
86
  window.sessionStorage.clear();
74
87
  mockReload();
75
88
  });
@@ -115,6 +128,18 @@ describe("ErrorBoundary", () => {
115
128
 
116
129
  expect(reloadMock).not.toHaveBeenCalled();
117
130
  });
131
+
132
+ test("does not show support-bundle controls when the page is healthy", () => {
133
+ render(
134
+ <ErrorBoundary>
135
+ <div>Child Component</div>
136
+ </ErrorBoundary>,
137
+ );
138
+
139
+ expect(
140
+ screen.queryByRole("button", { name: "Download support bundle" }),
141
+ ).toBeNull();
142
+ });
118
143
  });
119
144
 
120
145
  describe("containment", () => {
@@ -208,6 +233,20 @@ describe("ErrorBoundary", () => {
208
233
  screen.getByTestId("error-boundary-error-message").textContent,
209
234
  ).toContain("reading 'queryConfigs'");
210
235
  });
236
+
237
+ test("offers a support bundle and explains what it contains", () => {
238
+ render(
239
+ <ErrorBoundary>
240
+ <ThrowOnRender />
241
+ </ErrorBoundary>,
242
+ );
243
+
244
+ expect(
245
+ screen.queryByRole("button", { name: "Download support bundle" }),
246
+ ).not.toBeNull();
247
+ expect(screen.queryByText(/scrubbed page URL/)).not.toBeNull();
248
+ expect(screen.queryByText(/never reads cookies/)).not.toBeNull();
249
+ });
211
250
  });
212
251
 
213
252
  describe("reporting", () => {
@@ -222,6 +261,9 @@ describe("ErrorBoundary", () => {
222
261
 
223
262
  expect(onError).toHaveBeenCalledTimes(1);
224
263
  expect((onError.mock.calls[0]![0] as Error).message).toBe("Boom");
264
+ expect(onError.mock.calls[0]![1].componentStack).toContain(
265
+ "ThrowOnRender",
266
+ );
225
267
  });
226
268
 
227
269
  test("logs the error so it still reaches the console and RUM", () => {
@@ -292,6 +334,122 @@ describe("ErrorBoundary", () => {
292
334
  expect(reloadMock).toHaveBeenCalledTimes(1);
293
335
  });
294
336
 
337
+ test("downloads the caught error and React component stack", () => {
338
+ render(
339
+ <ErrorBoundary>
340
+ <ThrowOnRender message="Trace chart render exploded" />
341
+ </ErrorBoundary>,
342
+ );
343
+
344
+ fireEvent.click(
345
+ screen.getByTestId("error-boundary-download-support-bundle"),
346
+ );
347
+
348
+ expect(downloadFileMock).toHaveBeenCalledTimes(1);
349
+ expect(reloadMock).not.toHaveBeenCalled();
350
+ expect(screen.queryByTestId("error-boundary-fallback")).not.toBeNull();
351
+
352
+ const download: Parameters<typeof downloadFile>[0] =
353
+ downloadFileMock.mock.calls[0]![0];
354
+ const bundle: ErrorSupportBundle = JSON.parse(
355
+ download.content as string,
356
+ ) as ErrorSupportBundle;
357
+
358
+ expect(download.filename).toMatch(
359
+ /^oneuptime-error-support-bundle-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.json$/,
360
+ );
361
+ expect(download.mimeType).toBe("application/json;charset=utf-8;");
362
+ expect(bundle.error.message).toBe("Trace chart render exploded");
363
+ expect(bundle.error.stack).toContain("ThrowOnRender");
364
+ expect(bundle.react.component).toBe("ThrowOnRender");
365
+ expect(bundle.react.componentStack).toContain("ThrowOnRender");
366
+ expect(Date.parse(bundle.errorCapturedAt)).toBeLessThanOrEqual(
367
+ Date.parse(bundle.generatedAt),
368
+ );
369
+ expect(
370
+ screen.getByTestId("error-boundary-support-bundle-status").textContent,
371
+ ).toContain("Support bundle downloaded");
372
+ });
373
+
374
+ test("keeps component diagnostics when a component throws NaN", () => {
375
+ const ThrowNaN: FunctionComponent = (): ReactElement => {
376
+ throw Number.NaN;
377
+ };
378
+
379
+ render(
380
+ <ErrorBoundary>
381
+ <ThrowNaN />
382
+ </ErrorBoundary>,
383
+ );
384
+
385
+ fireEvent.click(
386
+ screen.getByTestId("error-boundary-download-support-bundle"),
387
+ );
388
+
389
+ const bundle: ErrorSupportBundle = JSON.parse(
390
+ downloadFileMock.mock.calls[0]![0].content as string,
391
+ ) as ErrorSupportBundle;
392
+
393
+ expect(bundle.error.message).toBe("NaN");
394
+ expect(bundle.react.componentStack).toContain("ThrowNaN");
395
+ });
396
+
397
+ test("shows a recoverable message if the browser blocks the download", () => {
398
+ downloadFileMock.mockImplementation(() => {
399
+ throw new Error("Downloads disabled");
400
+ });
401
+
402
+ render(
403
+ <ErrorBoundary>
404
+ <ThrowOnRender />
405
+ </ErrorBoundary>,
406
+ );
407
+
408
+ fireEvent.click(
409
+ screen.getByTestId("error-boundary-download-support-bundle"),
410
+ );
411
+
412
+ expect(
413
+ screen.getByTestId("error-boundary-support-bundle-status").textContent,
414
+ ).toContain("could not be downloaded");
415
+ expect(screen.queryByTestId("error-boundary-fallback")).not.toBeNull();
416
+ expect(reloadMock).not.toHaveBeenCalled();
417
+ });
418
+
419
+ test("downloads fresh diagnostics after recovering and crashing again", () => {
420
+ let errorMessage: string = "First render failure";
421
+
422
+ const ThrowsCurrentError: FunctionComponent = (): ReactElement => {
423
+ throw new Error(errorMessage);
424
+ };
425
+
426
+ render(
427
+ <ErrorBoundary>
428
+ <ThrowsCurrentError />
429
+ </ErrorBoundary>,
430
+ );
431
+
432
+ fireEvent.click(
433
+ screen.getByTestId("error-boundary-download-support-bundle"),
434
+ );
435
+ const firstBundle: ErrorSupportBundle = JSON.parse(
436
+ downloadFileMock.mock.calls[0]![0].content as string,
437
+ ) as ErrorSupportBundle;
438
+
439
+ errorMessage = "Second render failure";
440
+ fireEvent.click(screen.getByTestId("error-boundary-try-again"));
441
+ fireEvent.click(
442
+ screen.getByTestId("error-boundary-download-support-bundle"),
443
+ );
444
+ const secondBundle: ErrorSupportBundle = JSON.parse(
445
+ downloadFileMock.mock.calls[1]![0].content as string,
446
+ ) as ErrorSupportBundle;
447
+
448
+ expect(firstBundle.error.message).toBe("First render failure");
449
+ expect(secondBundle.error.message).toBe("Second render failure");
450
+ expect(secondBundle.react.componentStack).toContain("ThrowsCurrentError");
451
+ });
452
+
295
453
  test("changing the resetKey clears the error (navigating away)", () => {
296
454
  const { rerender } = render(
297
455
  <ErrorBoundary resetKey="/dashboard/monitors/broken">