@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
@@ -6,7 +6,7 @@
6
6
  * expr := term (('+' | '-') term)*
7
7
  * term := factor (('*' | '/') factor)*
8
8
  * factor := number | identifier | '(' expr ')' | '-' factor
9
- * number := digits ('.' digits)?
9
+ * number := digit+ ('.' digit*)?
10
10
  * identifier := [A-Za-z_][A-Za-z0-9_]*
11
11
  *
12
12
  * Evaluation returns `null` when the result is not a finite real number —
@@ -123,7 +123,7 @@ export function parse(input: string): ParseResult | ParseError {
123
123
  }
124
124
 
125
125
  const tokens: Array<Token> = tokenized;
126
- const state: { i: number; depth: number } = { i: 0, depth: 0 };
126
+ const state: { i: number } = { i: 0 };
127
127
  const identifiers: Set<string> = new Set();
128
128
 
129
129
  function peek(): Token | undefined {
@@ -134,23 +134,20 @@ export function parse(input: string): ParseResult | ParseError {
134
134
  return tokens[state.i++];
135
135
  }
136
136
 
137
- function parseExpr(): Node | ParseError {
138
- if (state.depth > MAX_DEPTH) {
137
+ function parseExpr(depth: number): Node | ParseError {
138
+ if (depth > MAX_DEPTH) {
139
139
  return { ok: false, error: "Expression nesting too deep" };
140
140
  }
141
- state.depth++;
142
- let left: Node | ParseError = parseTerm();
141
+ let left: Node | ParseError = parseTerm(depth);
143
142
  if ("ok" in left && left.ok === false) {
144
- state.depth--;
145
143
  return left;
146
144
  }
147
145
  while (true) {
148
146
  const next: Token | undefined = peek();
149
147
  if (next?.type === "op" && (next.value === "+" || next.value === "-")) {
150
148
  advance();
151
- const right: Node | ParseError = parseTerm();
149
+ const right: Node | ParseError = parseTerm(depth);
152
150
  if ("ok" in right && right.ok === false) {
153
- state.depth--;
154
151
  return right;
155
152
  }
156
153
  left = {
@@ -163,12 +160,11 @@ export function parse(input: string): ParseResult | ParseError {
163
160
  }
164
161
  break;
165
162
  }
166
- state.depth--;
167
163
  return left as Node;
168
164
  }
169
165
 
170
- function parseTerm(): Node | ParseError {
171
- let left: Node | ParseError = parseFactor();
166
+ function parseTerm(depth: number): Node | ParseError {
167
+ let left: Node | ParseError = parseFactor(depth);
172
168
  if ("ok" in left && left.ok === false) {
173
169
  return left;
174
170
  }
@@ -176,7 +172,7 @@ export function parse(input: string): ParseResult | ParseError {
176
172
  const next: Token | undefined = peek();
177
173
  if (next?.type === "op" && (next.value === "*" || next.value === "/")) {
178
174
  advance();
179
- const right: Node | ParseError = parseFactor();
175
+ const right: Node | ParseError = parseFactor(depth);
180
176
  if ("ok" in right && right.ok === false) {
181
177
  return right;
182
178
  }
@@ -193,7 +189,10 @@ export function parse(input: string): ParseResult | ParseError {
193
189
  return left as Node;
194
190
  }
195
191
 
196
- function parseFactor(): Node | ParseError {
192
+ function parseFactor(depth: number): Node | ParseError {
193
+ if (depth > MAX_DEPTH) {
194
+ return { ok: false, error: "Expression nesting too deep" };
195
+ }
197
196
  const tok: Token | undefined = peek();
198
197
  if (!tok) {
199
198
  return {
@@ -203,7 +202,7 @@ export function parse(input: string): ParseResult | ParseError {
203
202
  }
204
203
  if (tok.type === "op" && tok.value === "-") {
205
204
  advance();
206
- const operand: Node | ParseError = parseFactor();
205
+ const operand: Node | ParseError = parseFactor(depth + 1);
207
206
  if ("ok" in operand && operand.ok === false) {
208
207
  return operand;
209
208
  }
@@ -228,7 +227,7 @@ export function parse(input: string): ParseResult | ParseError {
228
227
  }
229
228
  if (tok.type === "lparen") {
230
229
  advance();
231
- const inner: Node | ParseError = parseExpr();
230
+ const inner: Node | ParseError = parseExpr(depth + 1);
232
231
  if ("ok" in inner && inner.ok === false) {
233
232
  return inner;
234
233
  }
@@ -249,7 +248,7 @@ export function parse(input: string): ParseResult | ParseError {
249
248
  };
250
249
  }
251
250
 
252
- const ast: Node | ParseError = parseExpr();
251
+ const ast: Node | ParseError = parseExpr(0);
253
252
  if ("ok" in ast && ast.ok === false) {
254
253
  return ast;
255
254
  }
@@ -281,6 +280,9 @@ export function evaluate(
281
280
  case "num":
282
281
  return Number.isFinite(node.value) ? node.value : null;
283
282
  case "ident": {
283
+ if (!Object.prototype.hasOwnProperty.call(bindings, node.name)) {
284
+ return null;
285
+ }
284
286
  const v: number | undefined = bindings[node.name];
285
287
  if (typeof v !== "number" || !Number.isFinite(v)) {
286
288
  return null;
@@ -42,6 +42,8 @@ class MonitorMetricTypeUtil {
42
42
  case MonitorMetricType.TlsHandshakeTime:
43
43
  case MonitorMetricType.TimeToFirstByte:
44
44
  case MonitorMetricType.DownloadTime:
45
+ case MonitorMetricType.PortDnsLookupTime:
46
+ case MonitorMetricType.PortTcpConnectTime:
45
47
  return AggregationType.Avg;
46
48
  case MonitorMetricType.SnmpInterfaceOperStatus:
47
49
  return AggregationType.Min;
@@ -101,6 +103,10 @@ class MonitorMetricTypeUtil {
101
103
  return MonitorMetricType.PacketLossPercent;
102
104
  case CheckOn.Jitter:
103
105
  return MonitorMetricType.Jitter;
106
+ case CheckOn.PortDnsLookupTime:
107
+ return MonitorMetricType.PortDnsLookupTime;
108
+ case CheckOn.PortTcpConnectTime:
109
+ return MonitorMetricType.PortTcpConnectTime;
104
110
  case CheckOn.ResponseStatusCode:
105
111
  return MonitorMetricType.ResponseStatusCode;
106
112
  case CheckOn.IsOnline:
@@ -205,6 +211,16 @@ class MonitorMetricTypeUtil {
205
211
  ];
206
212
  }
207
213
 
214
+ if (monitorType === MonitorType.Port) {
215
+ return [
216
+ MonitorMetricType.IsOnline,
217
+ // Kept as the backwards-compatible total connection duration.
218
+ MonitorMetricType.ResponseTime,
219
+ MonitorMetricType.PortDnsLookupTime,
220
+ MonitorMetricType.PortTcpConnectTime,
221
+ ];
222
+ }
223
+
208
224
  if (monitorType === MonitorType.NetworkDevice) {
209
225
  return [
210
226
  MonitorMetricType.IsOnline,
@@ -218,7 +234,6 @@ class MonitorMetricTypeUtil {
218
234
  }
219
235
 
220
236
  if (
221
- monitorType === MonitorType.Port ||
222
237
  monitorType === MonitorType.DNS ||
223
238
  monitorType === MonitorType.DNSSEC ||
224
239
  monitorType === MonitorType.Domain ||
@@ -344,7 +359,15 @@ class MonitorMetricTypeUtil {
344
359
 
345
360
  public static getTitleByMonitorMetricType(
346
361
  monitorMetricType: MonitorMetricType,
362
+ monitorType?: MonitorType | undefined,
347
363
  ): string {
364
+ if (
365
+ monitorType === MonitorType.Port &&
366
+ monitorMetricType === MonitorMetricType.ResponseTime
367
+ ) {
368
+ return "Total Connection Time (DNS + TCP)";
369
+ }
370
+
348
371
  switch (monitorMetricType) {
349
372
  case MonitorMetricType.ResponseTime:
350
373
  return "Response Time";
@@ -374,6 +397,10 @@ class MonitorMetricTypeUtil {
374
397
  return "Time to First Byte";
375
398
  case MonitorMetricType.DownloadTime:
376
399
  return "Download Time";
400
+ case MonitorMetricType.PortDnsLookupTime:
401
+ return "Port DNS Lookup Time";
402
+ case MonitorMetricType.PortTcpConnectTime:
403
+ return "Port TCP Connect Time";
377
404
  case MonitorMetricType.SnmpInterfaceOperStatus:
378
405
  return "Interface Status";
379
406
  case MonitorMetricType.SnmpInterfaceInBitsPerSecond:
@@ -439,13 +466,14 @@ class MonitorMetricTypeUtil {
439
466
 
440
467
  public static getLegendByMonitorMetricType(
441
468
  monitorMetricType: MonitorMetricType,
469
+ monitorType?: MonitorType | undefined,
442
470
  ): string {
443
471
  /*
444
472
  * Legend labels default to the title, which also matches the existing behavior
445
473
  * for every metric type. Keeping this as a separate function preserves the
446
474
  * option to diverge later (e.g. shorter legend labels for dense charts).
447
475
  */
448
- return this.getTitleByMonitorMetricType(monitorMetricType);
476
+ return this.getTitleByMonitorMetricType(monitorMetricType, monitorType);
449
477
  }
450
478
 
451
479
  public static getLegendUnitByMonitorMetricType(
@@ -475,6 +503,8 @@ class MonitorMetricTypeUtil {
475
503
  case MonitorMetricType.TlsHandshakeTime:
476
504
  case MonitorMetricType.TimeToFirstByte:
477
505
  case MonitorMetricType.DownloadTime:
506
+ case MonitorMetricType.PortDnsLookupTime:
507
+ case MonitorMetricType.PortTcpConnectTime:
478
508
  return "ms";
479
509
  case MonitorMetricType.SnmpInterfaceOperStatus:
480
510
  return "";
@@ -525,7 +555,15 @@ class MonitorMetricTypeUtil {
525
555
 
526
556
  public static getDescriptionByMonitorMetricType(
527
557
  monitorMetricType: MonitorMetricType,
558
+ monitorType?: MonitorType | undefined,
528
559
  ): string {
560
+ if (
561
+ monitorType === MonitorType.Port &&
562
+ monitorMetricType === MonitorMetricType.ResponseTime
563
+ ) {
564
+ return "Total time spent resolving the hostname (when needed) and establishing the TCP connection.";
565
+ }
566
+
529
567
  switch (monitorMetricType) {
530
568
  case MonitorMetricType.ResponseTime:
531
569
  return "Response time is the time taken for a server to respond to a request. It is the sum of the time spent waiting to establish the connection, the time spent waiting for the request to be processed, and the time spent waiting for the response to be sent.";
@@ -555,6 +593,10 @@ class MonitorMetricTypeUtil {
555
593
  return "Time from the end of connection setup until the first byte of the response arrived — the server-side processing time.";
556
594
  case MonitorMetricType.DownloadTime:
557
595
  return "Time spent receiving the response body after the first byte arrived.";
596
+ case MonitorMetricType.PortDnsLookupTime:
597
+ return "Time from starting a Port check until its first TCP connection attempt. It is absent when the destination is already an IP address.";
598
+ case MonitorMetricType.PortTcpConnectTime:
599
+ return "Time from the first TCP connection attempt until a connection succeeds, including any automatic IPv6/IPv4 fallback attempts.";
558
600
  case MonitorMetricType.SnmpInterfaceOperStatus:
559
601
  return "Operational status of each interface: 1 when up, 0 when down. Interfaces that are administratively disabled also report 0.";
560
602
  case MonitorMetricType.SnmpInterfaceInBitsPerSecond:
@@ -271,6 +271,57 @@ export default class StatusPageGroupNestingLayoutUtil {
271
271
  return data.ownResourceCount > 0 || data.subGroupCount === 0;
272
272
  }
273
273
 
274
+ /*
275
+ * Whether the overview page draws its resources-and-groups block at all.
276
+ *
277
+ * This was `statusPageResources.length > 0` written inline on the page, and
278
+ * it meant a status page carrying a fully built group hierarchy but no
279
+ * monitors yet rendered nothing at all: an operator who had just created
280
+ * their whole hierarchy saw an empty overview until the first monitor was
281
+ * attached to one of the groups. A group is content in its own right - the
282
+ * hierarchy is what a large status page is organised around, and it has to
283
+ * be visible while it is being filled in - so either resources or groups are
284
+ * enough to draw the block.
285
+ */
286
+ public static shouldRenderResourcesSection(data: {
287
+ statusPageResourceCount: number;
288
+ statusPageGroupCount: number;
289
+ }): boolean {
290
+ return data.statusPageResourceCount > 0 || data.statusPageGroupCount > 0;
291
+ }
292
+
293
+ /*
294
+ * The "all clear" empty state is the page's fallback for when there is
295
+ * nothing whatsoever to draw, so it has to agree with
296
+ * shouldRenderResourcesSection: a page with groups and no monitors renders
297
+ * its hierarchy, and an empty state stacked above that hierarchy would be
298
+ * telling the visitor the opposite of what is on the screen underneath it.
299
+ */
300
+ public static shouldRenderOverviewEmptyState(data: {
301
+ statusPageResourceCount: number;
302
+ statusPageGroupCount: number;
303
+ activeIncidentCount: number;
304
+ activeEpisodeCount: number;
305
+ activeScheduledMaintenanceCount: number;
306
+ activeAnnouncementCount: number;
307
+ }): boolean {
308
+ if (
309
+ this.shouldRenderResourcesSection({
310
+ statusPageResourceCount: data.statusPageResourceCount,
311
+ statusPageGroupCount: data.statusPageGroupCount,
312
+ })
313
+ ) {
314
+ return false;
315
+ }
316
+
317
+ return (
318
+ data.activeIncidentCount === 0 &&
319
+ data.activeEpisodeCount === 0 &&
320
+ data.activeScheduledMaintenanceCount === 0 &&
321
+ data.activeAnnouncementCount === 0
322
+ );
323
+ }
324
+
274
325
  /*
275
326
  * Whether a group draws the time axis under its own resource list.
276
327
  *
@@ -329,7 +380,25 @@ export default class StatusPageGroupNestingLayoutUtil {
329
380
  showCurrentStatus: boolean;
330
381
  isCurrentlyDown: boolean;
331
382
  uptimePercent: number | null;
383
+ /*
384
+ * How many resources the group's whole subtree contains. Zero means there
385
+ * is nothing under this group to report on.
386
+ */
387
+ resourceCountInSubtree: number;
332
388
  }): StatusPageGroupRollupKind {
389
+ /*
390
+ * A group with nothing under it has no reading to give - not an uptime
391
+ * percent and not a status. This did not use to come up, because a page
392
+ * with no resources rendered no groups at all; now that a hierarchy is
393
+ * drawn while it is still being filled in, it does. The rolled up status
394
+ * defaults to Operational when it finds nothing to look at, and a green
395
+ * "Operational" against a group that contains no monitors is a claim about
396
+ * availability the page has no basis for.
397
+ */
398
+ if (data.resourceCountInSubtree <= 0) {
399
+ return StatusPageGroupRollupKind.None;
400
+ }
401
+
333
402
  if (data.showUptimePercent && !data.isCurrentlyDown) {
334
403
  return data.uptimePercent === null
335
404
  ? StatusPageGroupRollupKind.None
@@ -6,6 +6,34 @@ export interface StatusPageGroupTreeNode {
6
6
  children: Array<StatusPageGroupTreeNode>;
7
7
  }
8
8
 
9
+ /*
10
+ * A parent -> children index over one list of groups, plus the id lookup that
11
+ * goes with it.
12
+ *
13
+ * Every helper here used to answer "who are this group's children?" by
14
+ * filtering the whole list, which made one walk of the tree cost O(groups) per
15
+ * node it touched. That is invisible at a dozen groups and is not invisible at
16
+ * fifteen hundred: a status page that size renders the same subtree walk once
17
+ * per group per render. Building the buckets once turns each of those lookups
18
+ * into a map read.
19
+ *
20
+ * Callers that walk the same list more than once (the status page overview
21
+ * renders a rollup per group; the uptime endpoint rolls up per group) should
22
+ * build one of these with buildIndex and hand it to every call. Callers that
23
+ * only ask one question can leave it out - each method builds its own, which
24
+ * still costs one pass over the list rather than one pass per node.
25
+ */
26
+ export interface StatusPageGroupTreeIndex {
27
+ /*
28
+ * Keyed by StatusPageGroupTreeUtil.getParentId, so `null` is the bucket of
29
+ * groups with no usable parent pointer. A plain string key would collide
30
+ * with groups that carry no id of their own, which read as "" everywhere
31
+ * else in this class.
32
+ */
33
+ childrenByParentId: Map<string | null, Array<StatusPageGroup>>;
34
+ byId: Map<string, StatusPageGroup>;
35
+ }
36
+
9
37
  /*
10
38
  * Status page groups form a tree: a group may be nested under another group
11
39
  * (Corporate Unit -> Region -> Market -> Site), and each level rolls up the
@@ -42,17 +70,69 @@ export default class StatusPageGroupTreeUtil {
42
70
  return parentId;
43
71
  }
44
72
 
73
+ /*
74
+ * One pass over the list. Siblings come out of each bucket in `order`, which
75
+ * is the same order filtering the whole list and sorting it produced -
76
+ * bucketing keeps the incoming order and the sort below is stable, so groups
77
+ * that share an order (or have none) keep their incoming position.
78
+ */
79
+ public static buildIndex(data: {
80
+ statusPageGroups: Array<StatusPageGroup>;
81
+ }): StatusPageGroupTreeIndex {
82
+ const childrenByParentId: Map<
83
+ string | null,
84
+ Array<StatusPageGroup>
85
+ > = new Map<string | null, Array<StatusPageGroup>>();
86
+
87
+ const byId: Map<string, StatusPageGroup> = new Map<
88
+ string,
89
+ StatusPageGroup
90
+ >();
91
+
92
+ for (const group of data.statusPageGroups) {
93
+ const groupId: string | undefined = group._id?.toString();
94
+
95
+ if (groupId) {
96
+ byId.set(groupId, group);
97
+ }
98
+
99
+ const parentId: string | null = this.getParentId(group);
100
+ const siblings: Array<StatusPageGroup> | undefined =
101
+ childrenByParentId.get(parentId);
102
+
103
+ if (siblings) {
104
+ siblings.push(group);
105
+ } else {
106
+ childrenByParentId.set(parentId, [group]);
107
+ }
108
+ }
109
+
110
+ for (const siblings of childrenByParentId.values()) {
111
+ this.sortByOrderInPlace(siblings);
112
+ }
113
+
114
+ return {
115
+ childrenByParentId: childrenByParentId,
116
+ byId: byId,
117
+ };
118
+ }
119
+
45
120
  public static getChildGroups(data: {
46
121
  statusPageGroupId: string | null;
47
122
  statusPageGroups: Array<StatusPageGroup>;
123
+ index?: StatusPageGroupTreeIndex | undefined;
48
124
  }): Array<StatusPageGroup> {
49
- const children: Array<StatusPageGroup> = data.statusPageGroups.filter(
50
- (group: StatusPageGroup) => {
51
- return this.getParentId(group) === data.statusPageGroupId;
52
- },
53
- );
54
-
55
- return this.sortByOrder(children);
125
+ /*
126
+ * A copy rather than the index's own bucket: this is public, and handing a
127
+ * caller the array the index is built on invites a mutation that would
128
+ * quietly corrupt every later lookup.
129
+ */
130
+ return [
131
+ ...this.getChildGroupsFromIndex({
132
+ statusPageGroupId: data.statusPageGroupId,
133
+ index: this.resolveIndex(data),
134
+ }),
135
+ ];
56
136
  }
57
137
 
58
138
  /*
@@ -61,13 +141,19 @@ export default class StatusPageGroupTreeUtil {
61
141
  */
62
142
  public static getRootGroups(data: {
63
143
  statusPageGroups: Array<StatusPageGroup>;
144
+ index?: StatusPageGroupTreeIndex | undefined;
64
145
  }): Array<StatusPageGroup> {
65
- const idsInList: Set<string> = this.getIdSet(data.statusPageGroups);
146
+ const index: StatusPageGroupTreeIndex = this.resolveIndex(data);
66
147
 
148
+ /*
149
+ * A scan of the whole list rather than a concatenation of the orphan
150
+ * buckets, so roots that share an order (or have none) keep the relative
151
+ * position they arrived in.
152
+ */
67
153
  const roots: Array<StatusPageGroup> = data.statusPageGroups.filter(
68
154
  (group: StatusPageGroup) => {
69
155
  const parentId: string | null = this.getParentId(group);
70
- return !parentId || !idsInList.has(parentId);
156
+ return !parentId || !index.byId.has(parentId);
71
157
  },
72
158
  );
73
159
 
@@ -81,7 +167,9 @@ export default class StatusPageGroupTreeUtil {
81
167
  */
82
168
  public static buildTree(data: {
83
169
  statusPageGroups: Array<StatusPageGroup>;
170
+ index?: StatusPageGroupTreeIndex | undefined;
84
171
  }): Array<StatusPageGroupTreeNode> {
172
+ const index: StatusPageGroupTreeIndex = this.resolveIndex(data);
85
173
  const visited: Set<string> = new Set<string>();
86
174
 
87
175
  const buildNode: (
@@ -94,16 +182,17 @@ export default class StatusPageGroupTreeUtil {
94
182
  const groupId: string = group._id?.toString() || "";
95
183
  visited.add(groupId);
96
184
 
97
- const children: Array<StatusPageGroupTreeNode> = this.getChildGroups({
98
- statusPageGroupId: groupId,
99
- statusPageGroups: data.statusPageGroups,
100
- })
101
- .filter((child: StatusPageGroup) => {
102
- return !visited.has(child._id?.toString() || "");
185
+ const children: Array<StatusPageGroupTreeNode> =
186
+ this.getChildGroupsFromIndex({
187
+ statusPageGroupId: groupId,
188
+ index: index,
103
189
  })
104
- .map((child: StatusPageGroup) => {
105
- return buildNode(child, depth + 1);
106
- });
190
+ .filter((child: StatusPageGroup) => {
191
+ return !visited.has(child._id?.toString() || "");
192
+ })
193
+ .map((child: StatusPageGroup) => {
194
+ return buildNode(child, depth + 1);
195
+ });
107
196
 
108
197
  return {
109
198
  group: group,
@@ -114,6 +203,7 @@ export default class StatusPageGroupTreeUtil {
114
203
 
115
204
  const tree: Array<StatusPageGroupTreeNode> = this.getRootGroups({
116
205
  statusPageGroups: data.statusPageGroups,
206
+ index: index,
117
207
  }).map((root: StatusPageGroup) => {
118
208
  return buildNode(root, 0);
119
209
  });
@@ -137,15 +227,18 @@ export default class StatusPageGroupTreeUtil {
137
227
  public static getDescendantGroups(data: {
138
228
  statusPageGroup: StatusPageGroup;
139
229
  statusPageGroups: Array<StatusPageGroup>;
230
+ index?: StatusPageGroupTreeIndex | undefined;
140
231
  }): Array<StatusPageGroup> {
232
+ const index: StatusPageGroupTreeIndex = this.resolveIndex(data);
233
+
141
234
  const descendants: Array<StatusPageGroup> = [];
142
235
  const visited: Set<string> = new Set<string>([
143
236
  data.statusPageGroup._id?.toString() || "",
144
237
  ]);
145
238
 
146
- let frontier: Array<StatusPageGroup> = this.getChildGroups({
239
+ let frontier: Array<StatusPageGroup> = this.getChildGroupsFromIndex({
147
240
  statusPageGroupId: data.statusPageGroup._id?.toString() || "",
148
- statusPageGroups: data.statusPageGroups,
241
+ index: index,
149
242
  });
150
243
 
151
244
  while (frontier.length > 0) {
@@ -162,9 +255,9 @@ export default class StatusPageGroupTreeUtil {
162
255
  descendants.push(group);
163
256
 
164
257
  nextFrontier.push(
165
- ...this.getChildGroups({
258
+ ...this.getChildGroupsFromIndex({
166
259
  statusPageGroupId: groupId,
167
- statusPageGroups: data.statusPageGroups,
260
+ index: index,
168
261
  }),
169
262
  );
170
263
  }
@@ -182,12 +275,14 @@ export default class StatusPageGroupTreeUtil {
182
275
  public static getGroupAndDescendants(data: {
183
276
  statusPageGroup: StatusPageGroup;
184
277
  statusPageGroups: Array<StatusPageGroup>;
278
+ index?: StatusPageGroupTreeIndex | undefined;
185
279
  }): Array<StatusPageGroup> {
186
280
  return [
187
281
  data.statusPageGroup,
188
282
  ...this.getDescendantGroups({
189
283
  statusPageGroup: data.statusPageGroup,
190
284
  statusPageGroups: data.statusPageGroups,
285
+ index: data.index,
191
286
  }),
192
287
  ];
193
288
  }
@@ -198,10 +293,9 @@ export default class StatusPageGroupTreeUtil {
198
293
  public static getAncestorGroups(data: {
199
294
  statusPageGroup: StatusPageGroup;
200
295
  statusPageGroups: Array<StatusPageGroup>;
296
+ index?: StatusPageGroupTreeIndex | undefined;
201
297
  }): Array<StatusPageGroup> {
202
- const byId: Map<string, StatusPageGroup> = this.getGroupsById(
203
- data.statusPageGroups,
204
- );
298
+ const byId: Map<string, StatusPageGroup> = this.resolveIndex(data).byId;
205
299
 
206
300
  const ancestors: Array<StatusPageGroup> = [];
207
301
  const visited: Set<string> = new Set<string>([
@@ -231,10 +325,12 @@ export default class StatusPageGroupTreeUtil {
231
325
  public static getDepth(data: {
232
326
  statusPageGroup: StatusPageGroup;
233
327
  statusPageGroups: Array<StatusPageGroup>;
328
+ index?: StatusPageGroupTreeIndex | undefined;
234
329
  }): number {
235
330
  return this.getAncestorGroups({
236
331
  statusPageGroup: data.statusPageGroup,
237
332
  statusPageGroups: data.statusPageGroups,
333
+ index: data.index,
238
334
  }).length;
239
335
  }
240
336
 
@@ -242,48 +338,39 @@ export default class StatusPageGroupTreeUtil {
242
338
  statusPageGroup: StatusPageGroup;
243
339
  possibleAncestorId: string;
244
340
  statusPageGroups: Array<StatusPageGroup>;
341
+ index?: StatusPageGroupTreeIndex | undefined;
245
342
  }): boolean {
246
343
  return this.getAncestorGroups({
247
344
  statusPageGroup: data.statusPageGroup,
248
345
  statusPageGroups: data.statusPageGroups,
346
+ index: data.index,
249
347
  }).some((ancestor: StatusPageGroup) => {
250
348
  return ancestor._id?.toString() === data.possibleAncestorId;
251
349
  });
252
350
  }
253
351
 
254
- private static getGroupsById(
255
- statusPageGroups: Array<StatusPageGroup>,
256
- ): Map<string, StatusPageGroup> {
257
- const byId: Map<string, StatusPageGroup> = new Map<
258
- string,
259
- StatusPageGroup
260
- >();
261
-
262
- for (const group of statusPageGroups) {
263
- const groupId: string | undefined = group._id?.toString();
264
-
265
- if (groupId) {
266
- byId.set(groupId, group);
267
- }
268
- }
269
-
270
- return byId;
352
+ private static resolveIndex(data: {
353
+ statusPageGroups: Array<StatusPageGroup>;
354
+ index?: StatusPageGroupTreeIndex | undefined;
355
+ }): StatusPageGroupTreeIndex {
356
+ return (
357
+ data.index ||
358
+ this.buildIndex({
359
+ statusPageGroups: data.statusPageGroups,
360
+ })
361
+ );
271
362
  }
272
363
 
273
- private static getIdSet(
274
- statusPageGroups: Array<StatusPageGroup>,
275
- ): Set<string> {
276
- const ids: Set<string> = new Set<string>();
277
-
278
- for (const group of statusPageGroups) {
279
- const groupId: string | undefined = group._id?.toString();
280
-
281
- if (groupId) {
282
- ids.add(groupId);
283
- }
284
- }
285
-
286
- return ids;
364
+ /*
365
+ * The index's own bucket, not a copy. Internal callers below only ever read
366
+ * it or spread it, and a tree walk that copies every sibling list at every
367
+ * node is most of what this index exists to avoid.
368
+ */
369
+ private static getChildGroupsFromIndex(data: {
370
+ statusPageGroupId: string | null;
371
+ index: StatusPageGroupTreeIndex;
372
+ }): Array<StatusPageGroup> {
373
+ return data.index.childrenByParentId.get(data.statusPageGroupId) || [];
287
374
  }
288
375
 
289
376
  /*
@@ -293,15 +380,19 @@ export default class StatusPageGroupTreeUtil {
293
380
  private static sortByOrder(
294
381
  statusPageGroups: Array<StatusPageGroup>,
295
382
  ): Array<StatusPageGroup> {
296
- return [...statusPageGroups].sort(
297
- (a: StatusPageGroup, b: StatusPageGroup) => {
298
- const orderA: number =
299
- typeof a.order === "number" ? a.order : Number.MAX_SAFE_INTEGER;
300
- const orderB: number =
301
- typeof b.order === "number" ? b.order : Number.MAX_SAFE_INTEGER;
302
-
303
- return orderA - orderB;
304
- },
305
- );
383
+ return this.sortByOrderInPlace([...statusPageGroups]);
384
+ }
385
+
386
+ private static sortByOrderInPlace(
387
+ statusPageGroups: Array<StatusPageGroup>,
388
+ ): Array<StatusPageGroup> {
389
+ return statusPageGroups.sort((a: StatusPageGroup, b: StatusPageGroup) => {
390
+ const orderA: number =
391
+ typeof a.order === "number" ? a.order : Number.MAX_SAFE_INTEGER;
392
+ const orderB: number =
393
+ typeof b.order === "number" ? b.order : Number.MAX_SAFE_INTEGER;
394
+
395
+ return orderA - orderB;
396
+ });
306
397
  }
307
398
  }