@oneuptime/common 13.0.3 → 13.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 (687) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -0
  2. package/Models/DatabaseModels/RumApplication.ts +1 -1
  3. package/Models/DatabaseModels/SecurityEventConnection.ts +457 -0
  4. package/Models/DatabaseModels/SecurityEventConnectionRun.ts +263 -0
  5. package/Models/DatabaseModels/TelemetryIngestionKey.ts +2 -2
  6. package/Server/API/AIInvestigationAPI.ts +484 -24
  7. package/Server/API/GoogleSecOpsConnectionAPI.ts +249 -8
  8. package/Server/API/SecurityEventConnectionAPI.ts +250 -0
  9. package/Server/API/TelemetryAPI.ts +402 -265
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.ts +99 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Middleware/TelemetryIngest.ts +101 -6
  13. package/Server/Services/ExceptionAggregationService.ts +7 -16
  14. package/Server/Services/GoogleSecOpsConnectionService.ts +15 -0
  15. package/Server/Services/Index.ts +4 -0
  16. package/Server/Services/InventoryItemService.ts +28 -0
  17. package/Server/Services/LogAggregationService.ts +17 -23
  18. package/Server/Services/MetricAggregationService.ts +4 -14
  19. package/Server/Services/SecurityEventConnectionRunService.ts +10 -0
  20. package/Server/Services/SecurityEventConnectionService.ts +538 -0
  21. package/Server/Services/TraceAggregationService.ts +77 -28
  22. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  23. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +13 -1
  24. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +15 -1
  25. package/Server/Utils/AI/SRE/InvestigationEvidence.ts +658 -0
  26. package/Server/Utils/AI/SRE/InvestigationReferences.ts +324 -0
  27. package/Server/Utils/AI/Toolbox/Index.ts +38 -0
  28. package/Server/Utils/AI/Toolbox/TimelineTools.ts +9 -2
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +80 -0
  30. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +5 -3
  31. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.ts +1108 -0
  32. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.ts +732 -0
  33. package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +495 -0
  34. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.ts +689 -0
  35. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.ts +767 -0
  36. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.ts +571 -0
  37. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.ts +827 -0
  38. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.ts +610 -0
  39. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.ts +549 -0
  40. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.ts +830 -0
  41. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.ts +684 -0
  42. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.ts +487 -0
  43. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.ts +778 -0
  44. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +1002 -0
  45. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +556 -0
  46. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +275 -0
  47. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +53 -0
  48. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.ts +709 -0
  49. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.ts +596 -0
  50. package/Server/Utils/SecurityEvent/Connectors/Types.ts +191 -0
  51. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +353 -65
  52. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +777 -0
  53. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +747 -148
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +48 -2
  55. package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +81 -0
  56. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +741 -40
  57. package/Server/Utils/StartServer.ts +10 -0
  58. package/Server/Utils/Telemetry/EntityRegistry.ts +8 -0
  59. package/Server/Utils/Telemetry/KubernetesResourceAttributes.ts +108 -0
  60. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +174 -93
  61. package/Server/Utils/Telemetry/ResourceFacetPlanner.ts +251 -0
  62. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +248 -413
  63. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +668 -0
  64. package/Server/Utils/Telemetry/TraceContextPropagation.ts +348 -0
  65. package/Server/Utils/Telemetry.ts +18 -0
  66. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +413 -0
  67. package/Tests/App/Dashboard/AIRootCauseFeedItem.test.tsx +285 -0
  68. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +242 -0
  69. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +601 -15
  70. package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +835 -0
  71. package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +41 -0
  72. package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +604 -0
  73. package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +738 -0
  74. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +153 -6
  75. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +165 -0
  76. package/Tests/App/Dashboard/EpisodeChangeState.test.tsx +1069 -0
  77. package/Tests/App/Dashboard/EpisodeMembersCard.test.tsx +772 -0
  78. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +2354 -0
  79. package/Tests/App/Dashboard/EventOverviewSkeleton.test.tsx +210 -0
  80. package/Tests/App/Dashboard/EventStatBar.test.tsx +392 -0
  81. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +247 -0
  82. package/Tests/App/Dashboard/ExceptionAIAssistance.test.tsx +606 -0
  83. package/Tests/App/Dashboard/ExceptionBreadcrumbTimeline.test.tsx +273 -0
  84. package/Tests/App/Dashboard/ExceptionDetailSideMenu.test.tsx +176 -0
  85. package/Tests/App/Dashboard/ExceptionOverviewCards.test.tsx +366 -0
  86. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +371 -0
  87. package/Tests/App/Dashboard/ExceptionStackFrameViewer.test.tsx +440 -0
  88. package/Tests/App/Dashboard/ExceptionSummary.test.tsx +381 -0
  89. package/Tests/App/Dashboard/ExceptionTriage.test.tsx +306 -0
  90. package/Tests/App/Dashboard/ExceptionsResourceFacets.test.tsx +604 -0
  91. package/Tests/App/Dashboard/ExceptionsViewerEntityChips.test.tsx +706 -0
  92. package/Tests/App/Dashboard/FeedOrdering.test.ts +32 -0
  93. package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +838 -0
  94. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +18 -0
  95. package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +297 -20
  96. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +618 -13
  97. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +254 -207
  98. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +303 -135
  99. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +1251 -0
  100. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +303 -0
  101. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +876 -8
  102. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +35 -0
  103. package/Tests/App/Dashboard/InvestigationReferenceLink.test.tsx +177 -0
  104. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +1295 -0
  105. package/Tests/App/Dashboard/KubernetesImageReferenceView.test.tsx +114 -0
  106. package/Tests/App/Dashboard/KubernetesLogsTabChips.test.tsx +285 -0
  107. package/Tests/App/Dashboard/LogsEntityChipNames.test.tsx +316 -0
  108. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +361 -0
  109. package/Tests/App/Dashboard/ProfileTableEntityNames.test.tsx +432 -0
  110. package/Tests/App/Dashboard/ProfileViewEntityName.test.tsx +250 -0
  111. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +10 -0
  112. package/Tests/App/Dashboard/ResourceLogsTabsBareLayout.test.tsx +700 -0
  113. package/Tests/App/Dashboard/ResourcePageEntityNameProps.test.tsx +803 -0
  114. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +11 -1
  115. package/Tests/App/Dashboard/ScheduledMaintenanceChangeState.test.tsx +1026 -0
  116. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +436 -0
  117. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +1195 -0
  118. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1022 -0
  119. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +905 -0
  120. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +247 -0
  121. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +375 -274
  122. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +410 -0
  123. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +774 -0
  124. package/Tests/App/Dashboard/TopErrorsPanelResourceNames.test.tsx +316 -0
  125. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +30 -0
  126. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +88 -24
  127. package/Tests/App/Dashboard/TraceExplorer.test.tsx +1590 -0
  128. package/Tests/App/Dashboard/TraceWaterfall.test.tsx +488 -0
  129. package/Tests/App/Dashboard/TracesAnalyticsSeriesLabels.test.tsx +367 -0
  130. package/Tests/App/Dashboard/TracesEntityNames.test.tsx +585 -0
  131. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +613 -0
  132. package/Tests/App/Dashboard/UseServiceNames.test.tsx +359 -0
  133. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +7 -0
  134. package/Tests/Models/InventoryItemNaming.test.ts +132 -16
  135. package/Tests/Models/SecurityDomainAccessControl.test.ts +61 -17
  136. package/Tests/Server/API/AIInvestigationAPI.test.ts +611 -2
  137. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +1098 -0
  138. package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +409 -0
  139. package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +856 -0
  140. package/Tests/Server/API/SessionReplayAPI.test.ts +528 -1
  141. package/Tests/Server/API/TelemetryFacetsAPI.test.ts +810 -0
  142. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +240 -0
  143. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +229 -0
  144. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +66 -0
  145. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  146. package/Tests/Server/Services/SecurityEventConnectionService.test.ts +1233 -0
  147. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +53 -0
  148. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +218 -0
  149. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +155 -1
  150. package/Tests/Server/Services/TraceAggregationExceptionScope.test.ts +142 -0
  151. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +85 -24
  152. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +33 -0
  153. package/Tests/Server/Utils/AI/InvestigationEvidence.test.ts +1395 -0
  154. package/Tests/Server/Utils/AI/InvestigationReferences.test.ts +616 -0
  155. package/Tests/Server/Utils/AI/TimelineTools.test.ts +119 -0
  156. package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +151 -0
  157. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +65 -0
  158. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorExceptionScope.test.ts +224 -0
  159. package/Tests/Server/Utils/LoggerCore.test.ts +597 -0
  160. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
  161. package/Tests/Server/Utils/Monitor/MonitorTemplateUtil.test.ts +1109 -0
  162. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
  163. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
  164. package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
  165. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
  166. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
  167. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
  168. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
  169. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
  170. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
  171. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
  172. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
  173. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
  174. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
  175. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
  176. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
  178. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
  179. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
  181. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
  182. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
  183. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
  184. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
  185. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
  186. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
  187. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
  188. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
  189. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
  190. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
  191. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
  192. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +711 -0
  193. package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
  194. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
  195. package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
  196. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +868 -0
  197. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
  198. package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
  199. package/Tests/Types/OnCallDutyPolicy/LayerUtilCorePaths.test.ts +1014 -0
  200. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +9 -6
  201. package/Tests/Types/Rules/RuleCriteriaFieldRegistry.test.ts +276 -0
  202. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
  203. package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
  204. package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
  205. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
  206. package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
  207. package/Tests/Types/Telemetry/LlmConventions.test.ts +495 -0
  208. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
  209. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
  210. package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
  211. package/Tests/UI/Components/Card.test.tsx +312 -1
  212. package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
  213. package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
  214. package/Tests/UI/Components/Checkbox.test.tsx +79 -0
  215. package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
  216. package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
  217. package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
  218. package/Tests/UI/Components/FacetSections.test.tsx +581 -0
  219. package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
  220. package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
  221. package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
  222. package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
  223. package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
  224. package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
  225. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
  226. package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
  227. package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
  228. package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
  229. package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
  230. package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
  231. package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
  232. package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
  233. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
  234. package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
  235. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
  236. package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
  237. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
  238. package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
  239. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
  240. package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
  241. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
  242. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
  243. package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
  244. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
  245. package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
  246. package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
  247. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
  248. package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
  249. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
  250. package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
  251. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
  252. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
  253. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
  254. package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
  255. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
  256. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
  257. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
  258. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
  259. package/Tests/UI/Utils/Clipboard.test.ts +154 -0
  260. package/Tests/UI/Utils/Navigation.test.ts +24 -0
  261. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
  262. package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
  263. package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
  264. package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
  265. package/Tests/Utils/Rum/SessionReplayHealth.test.ts +884 -0
  266. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
  267. package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
  268. package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
  269. package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
  270. package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
  271. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
  272. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
  273. package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
  274. package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
  275. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
  276. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
  277. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
  278. package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
  279. package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
  280. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
  281. package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
  282. package/Types/AI/AIChatTypes.ts +8 -0
  283. package/Types/AI/InvestigationEvidence.ts +96 -0
  284. package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
  285. package/Types/OnCallDutyPolicy/Layer.ts +12 -0
  286. package/Types/Permission.ts +2 -2
  287. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  288. package/Types/Rum/SessionReplay.ts +109 -0
  289. package/Types/Rum/SessionReplayApi.ts +31 -0
  290. package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
  291. package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
  292. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
  293. package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
  294. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
  295. package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
  296. package/Types/Telemetry/EntityType.ts +14 -0
  297. package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
  298. package/Types/Telemetry/LockedFilterDetail.ts +46 -0
  299. package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
  300. package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
  301. package/UI/Components/Card/Card.tsx +118 -83
  302. package/UI/Components/Checkbox/Checkbox.tsx +8 -5
  303. package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
  304. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
  305. package/UI/Components/Detail/Detail.tsx +137 -22
  306. package/UI/Components/ErrorBoundary.tsx +38 -0
  307. package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
  308. package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
  309. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
  310. package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
  311. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
  312. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
  313. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
  314. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
  315. package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
  316. package/UI/Components/LogsViewer/types.ts +6 -0
  317. package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
  318. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
  319. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
  320. package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
  321. package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
  322. package/UI/Components/ModelList/ModelList.tsx +33 -8
  323. package/UI/Components/Page/ModelPage.tsx +119 -54
  324. package/UI/Components/Table/Table.tsx +8 -0
  325. package/UI/Components/Table/TableRow.tsx +13 -5
  326. package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
  327. package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
  328. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
  329. package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
  330. package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
  331. package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
  332. package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
  333. package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
  334. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
  335. package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
  336. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
  337. package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
  338. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
  339. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
  340. package/UI/Components/TelemetryViewer/types.ts +19 -0
  341. package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
  342. package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
  343. package/UI/Utils/Clipboard.ts +68 -2
  344. package/UI/Utils/Navigation.ts +17 -2
  345. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
  346. package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
  347. package/Utils/AI/InvestigationReport.ts +1789 -0
  348. package/Utils/API.ts +90 -1
  349. package/Utils/Rum/SessionReplayHealth.ts +11 -0
  350. package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
  351. package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
  352. package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
  353. package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
  354. package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
  355. package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
  356. package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
  357. package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
  358. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
  359. package/Utils/Telemetry/CrossSignalScope.ts +22 -8
  360. package/Utils/Telemetry/EntityRelationship.ts +8 -0
  361. package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
  362. package/Utils/Telemetry/NetworkHost.ts +43 -0
  363. package/Utils/Telemetry/OriginAllowList.ts +139 -4
  364. package/Utils/Traces/CriticalPath.ts +10 -4
  365. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  366. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
  368. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
  370. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
  371. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
  372. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
  373. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
  374. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  375. package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
  376. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  377. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
  378. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
  379. package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
  380. package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
  381. package/build/dist/Server/API/TelemetryAPI.js +248 -170
  382. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  383. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
  384. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
  385. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  386. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  387. package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
  388. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  389. package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
  390. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  391. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
  392. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  393. package/build/dist/Server/Services/Index.js +4 -0
  394. package/build/dist/Server/Services/Index.js.map +1 -1
  395. package/build/dist/Server/Services/InventoryItemService.js +22 -0
  396. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  397. package/build/dist/Server/Services/LogAggregationService.js +8 -17
  398. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  399. package/build/dist/Server/Services/MetricAggregationService.js +4 -14
  400. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  401. package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
  402. package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
  403. package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
  404. package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
  405. package/build/dist/Server/Services/TraceAggregationService.js +51 -25
  406. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  407. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  408. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  409. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  410. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  411. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
  412. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  413. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
  414. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
  415. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
  416. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
  417. package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
  418. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  419. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
  420. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
  421. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
  422. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  425. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
  426. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
  427. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
  428. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
  429. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
  430. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
  431. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
  432. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
  433. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
  434. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
  435. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
  436. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
  437. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
  438. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
  439. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
  440. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
  441. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
  442. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
  443. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
  444. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
  445. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
  446. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
  447. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
  448. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
  449. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
  450. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
  451. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
  452. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
  453. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
  454. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
  455. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
  456. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
  457. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
  458. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
  459. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
  460. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
  461. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
  462. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
  463. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
  464. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
  465. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
  466. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  467. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
  468. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
  469. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
  470. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  471. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
  472. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
  473. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
  474. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
  475. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
  476. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  477. package/build/dist/Server/Utils/StartServer.js +10 -0
  478. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  479. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
  480. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  481. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
  482. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
  483. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
  484. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  485. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
  486. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
  487. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
  488. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  489. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
  490. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
  491. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
  492. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
  493. package/build/dist/Server/Utils/Telemetry.js +11 -5
  494. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  495. package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
  496. package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
  497. package/build/dist/Types/OnCallDutyPolicy/Layer.js +11 -0
  498. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  499. package/build/dist/Types/Permission.js +2 -2
  500. package/build/dist/Types/Permission.js.map +1 -1
  501. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +9 -0
  502. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  503. package/build/dist/Types/Rum/SessionReplay.js +95 -0
  504. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  505. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  506. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
  507. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
  508. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
  509. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
  510. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
  511. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
  512. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
  513. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
  514. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
  515. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
  516. package/build/dist/Types/Telemetry/EntityType.js +14 -0
  517. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  518. package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
  519. package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
  520. package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
  521. package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
  522. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
  523. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
  524. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
  525. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
  526. package/build/dist/UI/Components/Card/Card.js +34 -17
  527. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  528. package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
  529. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  530. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  531. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  532. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
  533. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  534. package/build/dist/UI/Components/Detail/Detail.js +63 -13
  535. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  536. package/build/dist/UI/Components/ErrorBoundary.js +34 -0
  537. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  538. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
  539. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
  540. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
  541. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  542. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
  543. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  544. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
  545. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
  546. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
  547. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  548. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
  549. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  550. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
  551. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  552. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
  553. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  554. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
  555. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  556. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  557. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
  558. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
  559. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
  560. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  561. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
  562. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  563. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
  564. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  565. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
  566. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  567. package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
  568. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  569. package/build/dist/UI/Components/Page/ModelPage.js +62 -34
  570. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  571. package/build/dist/UI/Components/Table/Table.js +3 -1
  572. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  573. package/build/dist/UI/Components/Table/TableRow.js +8 -1
  574. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  575. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
  576. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
  577. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
  578. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
  579. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  580. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
  582. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
  583. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
  584. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
  585. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
  586. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
  587. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
  588. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
  589. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
  590. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
  591. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
  592. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
  593. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
  594. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
  595. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
  596. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  597. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
  598. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
  599. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
  600. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  601. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
  602. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  603. package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
  604. package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
  605. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
  606. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
  607. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
  608. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
  609. package/build/dist/UI/Utils/Clipboard.js +59 -2
  610. package/build/dist/UI/Utils/Clipboard.js.map +1 -1
  611. package/build/dist/UI/Utils/Navigation.js +11 -2
  612. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  613. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
  614. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
  615. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
  616. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
  617. package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
  618. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
  619. package/build/dist/Utils/API.js +25 -0
  620. package/build/dist/Utils/API.js.map +1 -1
  621. package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
  622. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
  623. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
  624. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
  625. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
  626. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
  627. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
  628. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
  629. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
  630. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
  631. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
  632. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
  633. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
  634. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
  635. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
  636. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
  637. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
  638. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
  639. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
  640. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  641. package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
  642. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  643. package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
  644. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  645. package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
  646. package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
  647. package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
  648. package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
  649. package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
  650. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  651. package/build/dist/Utils/Traces/CriticalPath.js +9 -4
  652. package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
  653. package/package.json +1 -1
  654. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
  655. package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
  656. package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
  657. package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
  658. package/UI/Components/GanttChart/Index.tsx +0 -90
  659. package/UI/Components/GanttChart/Row/Index.tsx +0 -22
  660. package/UI/Components/GanttChart/Row/Row.tsx +0 -231
  661. package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
  662. package/UI/Components/GanttChart/Rows.tsx +0 -59
  663. package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
  664. package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
  665. package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
  666. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
  667. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
  668. package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
  669. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
  670. package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
  671. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
  672. package/build/dist/UI/Components/GanttChart/Index.js +0 -39
  673. package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
  674. package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
  675. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
  676. package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
  677. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
  678. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
  679. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
  680. package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
  681. package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
  682. package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
  683. package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
  684. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
  685. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
  686. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
  687. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
@@ -7,7 +7,15 @@ import {
7
7
  jest,
8
8
  test,
9
9
  } from "@jest/globals";
10
- import { cleanup, render, screen, waitFor } from "@testing-library/react";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ waitFor,
17
+ within,
18
+ } from "@testing-library/react";
11
19
  import React from "react";
12
20
  import { Location } from "react-router-dom";
13
21
  import getJestMockFunction, { MockFunction } from "../../MockType";
@@ -20,7 +28,7 @@ import getJestMockFunction, { MockFunction } from "../../MockType";
20
28
  * getElement threw BadDataException("Episode Severity not found") from inside
21
29
  * Detail's own render, so the whole route was replaced by the app level
22
30
  * "Something went wrong" boundary. The only recovery was deleting the episode,
23
- * which detached every member incident.
31
+ * which detached every member alert.
24
32
  *
25
33
  * These tests drive the real page against a fake server and assert what a
26
34
  * person actually sees: the Episode Severity row, holding a placeholder.
@@ -33,16 +41,30 @@ import getJestMockFunction, { MockFunction } from "../../MockType";
33
41
  * guard must NOT be what makes these pass: every case asserts the page never
34
42
  * logged "threw and was ignored", which is how a regression at the page level
35
43
  * stays visible instead of being quietly absorbed by the framework.
44
+ *
45
+ * The overview itself was later redesigned (header, stat bar, member list,
46
+ * compact details column), so the last describe blocks pin that layout's
47
+ * behaviour against the same fake server.
36
48
  */
37
49
 
38
50
  const EPISODE_ID: string = "11111111-1111-4111-8111-111111111111";
39
51
  const PROJECT_ID: string = "22222222-2222-4222-8222-222222222222";
52
+ const ACK_STATE_ID: string = "66666666-6666-4666-8666-666666666666";
53
+ const RESOLVED_STATE_ID: string = "77777777-7777-4777-8777-777777777777";
40
54
 
41
55
  const getItemMock: MockFunction = getJestMockFunction();
42
56
  const getListMock: MockFunction = getJestMockFunction();
43
57
  const getCommonHeadersMock: MockFunction = getJestMockFunction();
44
58
  const loggerErrorMock: MockFunction = getJestMockFunction();
45
59
 
60
+ // The last props the stubbed header was rendered with.
61
+ const changeStateProps: { current: Record<string, any> | null } = {
62
+ current: null,
63
+ };
64
+
65
+ // Every set of props the stubbed header was rendered with, in order.
66
+ const changeStateHistory: Array<Record<string, any>> = [];
67
+
46
68
  /*
47
69
  * The arrow wrappers are load bearing: jest.mock is hoisted above the compiled
48
70
  * requires, so the mock consts above are still unassigned when the factory
@@ -88,14 +110,17 @@ jest.mock("../../../UI/Utils/Logger", () => {
88
110
  /*
89
111
  * The page's two heavy children each drive their own API traffic and neither
90
112
  * renders a detail field. Stubbing them keeps this suite pointed at the
91
- * Episode Details card, which is where #3374 lives.
113
+ * Episode Details card, which is where #3374 lives. The header stub records
114
+ * its props so the layout tests can drive a state change.
92
115
  */
93
116
  jest.mock(
94
117
  "../../../../App/FeatureSet/Dashboard/src/Components/AlertEpisode/ChangeState",
95
118
  () => {
96
119
  return {
97
120
  __esModule: true,
98
- default: () => {
121
+ default: (props: Record<string, any>) => {
122
+ changeStateProps.current = props;
123
+ changeStateHistory.push(props);
99
124
  return null;
100
125
  },
101
126
  };
@@ -116,10 +141,13 @@ jest.mock(
116
141
 
117
142
  import AlertEpisodeView from "../../../../App/FeatureSet/Dashboard/src/Pages/Alerts/EpisodeView/Index";
118
143
  import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
144
+ import Alert from "../../../Models/DatabaseModels/Alert";
119
145
  import AlertEpisode from "../../../Models/DatabaseModels/AlertEpisode";
146
+ import AlertEpisodeStateTimeline from "../../../Models/DatabaseModels/AlertEpisodeStateTimeline";
120
147
  import AlertSeverity from "../../../Models/DatabaseModels/AlertSeverity";
121
148
  import AlertState from "../../../Models/DatabaseModels/AlertState";
122
149
  import Route from "../../../Types/API/Route";
150
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
123
151
  import Color from "../../../Types/Color";
124
152
  import ObjectID from "../../../Types/ObjectID";
125
153
  import Permission from "../../../Types/Permission";
@@ -214,6 +242,22 @@ const buildEpisode: BuildEpisodeFunction = (
214
242
  return episode;
215
243
  };
216
244
 
245
+ type EmptyListFunction = () => {
246
+ data: Array<unknown>;
247
+ count: number;
248
+ skip: number;
249
+ limit: number;
250
+ };
251
+
252
+ const emptyList: EmptyListFunction = () => {
253
+ return {
254
+ data: [],
255
+ count: 0,
256
+ skip: 0,
257
+ limit: 0,
258
+ };
259
+ };
260
+
217
261
  type RenderPageFunction = (spec: EpisodeSpec) => Promise<void>;
218
262
 
219
263
  const renderPage: RenderPageFunction = async (
@@ -232,6 +276,7 @@ const renderPage: RenderPageFunction = async (
232
276
  * Detail renders each field as <container><FieldLabel/><value/></container>,
233
277
  * and FieldLabel's root is the only div.space-y-1 in that subtree. Walking up
234
278
  * from the label and taking the sibling is what a person reading the row does.
279
+ * The compact style the overview uses keeps exactly that shape.
235
280
  */
236
281
  type FieldValueFunction = (title: string) => string;
237
282
 
@@ -270,6 +315,16 @@ const pillFor: PillForFunction = (title: string): HTMLElement => {
270
315
  return pill;
271
316
  };
272
317
 
318
+ // A stat bar cell: label span -> label row -> cell; the value is the 2nd child.
319
+ type StatValueFunction = (label: string) => string;
320
+
321
+ const statValue: StatValueFunction = (label: string): string => {
322
+ const cell: HTMLElement | null | undefined =
323
+ screen.getByText(label).parentElement?.parentElement;
324
+
325
+ return (cell?.children[1]?.textContent || "").trim();
326
+ };
327
+
273
328
  type ExpectNoCrashFunction = () => void;
274
329
 
275
330
  /*
@@ -291,18 +346,25 @@ const expectNoCrash: ExpectNoCrashFunction = (): void => {
291
346
  expect(swallowed).toEqual([]);
292
347
  };
293
348
 
349
+ type CallsForModelFunction = (modelType: unknown) => Array<Array<any>>;
350
+
351
+ const listCallsFor: CallsForModelFunction = (
352
+ modelType: unknown,
353
+ ): Array<Array<any>> => {
354
+ return getListMock.mock.calls.filter((call: Array<any>): boolean => {
355
+ return call[0]?.modelType === modelType;
356
+ });
357
+ };
358
+
294
359
  beforeEach((): void => {
295
360
  getItemMock.mockReset();
296
361
  getListMock.mockReset();
297
362
  getCommonHeadersMock.mockReset();
298
363
  loggerErrorMock.mockReset();
364
+ changeStateProps.current = null;
365
+ changeStateHistory.length = 0;
299
366
 
300
- getListMock.mockResolvedValue({
301
- data: [],
302
- count: 0,
303
- skip: 0,
304
- limit: 0,
305
- } as never);
367
+ getListMock.mockResolvedValue(emptyList() as never);
306
368
  getCommonHeadersMock.mockReturnValue({} as never);
307
369
 
308
370
  const path: string = `/dashboard/${PROJECT_ID}/alerts/episodes/${EPISODE_ID}`;
@@ -357,14 +419,13 @@ describe("Alert Episode detail page: severity (issue #3374)", () => {
357
419
  test("still renders the rest of the episode when severity is missing", async () => {
358
420
  await renderPage({ state: buildState("Created", new Color("#4b5563")) });
359
421
 
360
- expect(screen.getByText("Episode Title")).toBeInTheDocument();
361
- expect(fieldValue("Episode Title")).toEqual("episode without severity");
362
422
  expect(fieldValue("Episode Number")).toContain("#7");
363
423
  expect(fieldValue("Alert Count")).toEqual("0");
364
- // An episode with no grouping rule is a manual one, not a crash.
365
- expect(fieldValue("Grouping Rule")).toEqual("Manual Episode");
366
424
  // No createdByUser on an API-created episode.
367
425
  expect(fieldValue("Created By")).toEqual("System");
426
+ // An episode with no grouping rule is a manual one, not a crash.
427
+ expect(fieldValue("Grouping Rule")).toEqual("Manual Episode");
428
+ expect(fieldValue("Episode ID")).toContain(EPISODE_ID);
368
429
  expectNoCrash();
369
430
  });
370
431
 
@@ -459,7 +520,532 @@ describe("Alert Episode detail page: both relations missing", () => {
459
520
 
460
521
  expect(fieldValue("Episode Severity")).toEqual("-");
461
522
  expect(fieldValue("Current State")).toEqual("-");
462
- expect(fieldValue("Episode Title")).toEqual("episode without severity");
523
+ expect(fieldValue("Episode Number")).toContain("#7");
524
+ expectNoCrash();
525
+ });
526
+ });
527
+
528
+ describe("Alert Episode overview: details column", () => {
529
+ test("uses the compact single-column style with the episode ID last", async () => {
530
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
531
+
532
+ const numberRow: HTMLElement | null | undefined = screen
533
+ .getByText("Episode Number")
534
+ .closest("div.space-y-1")?.parentElement;
535
+ const grid: HTMLElement | null | undefined = numberRow?.parentElement;
536
+
537
+ expect(grid).toHaveClass("grid", "grid-cols-1", "divide-y");
538
+ expect(grid).toHaveClass("sm:grid-cols-1");
539
+ expect(numberRow).toHaveClass("py-3");
540
+
541
+ const rowTitles: Array<string> = Array.from(grid!.children).map(
542
+ (row: Element): string => {
543
+ return (row.querySelector("label")?.textContent || "").trim();
544
+ },
545
+ );
546
+
547
+ expect(rowTitles).toEqual([
548
+ "Episode Number",
549
+ "Current State",
550
+ "Episode Severity",
551
+ "Alert Count",
552
+ "Grouping Rule",
553
+ "Created By",
554
+ "On-Call Duty Policies",
555
+ "Created At",
556
+ "Labels",
557
+ "Episode ID",
558
+ ]);
559
+ expectNoCrash();
560
+ });
561
+
562
+ test("no longer repeats the title the header already shows", async () => {
563
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
564
+
565
+ expect(screen.queryByText("Episode Title")).toBeNull();
566
+ expect(screen.queryByText("Last Alert Added At")).toBeNull();
567
+ // The number is plain text now, not a hand-rolled SVG chip.
568
+ const numberRow: HTMLElement | null | undefined = screen
569
+ .getByText("Episode Number")
570
+ .closest("div.space-y-1")?.parentElement;
571
+
572
+ expect(numberRow?.querySelector("svg")).toBeNull();
573
+ });
574
+ /*
575
+ * The details card sits in a ~300px column. Its old side-by-side header put
576
+ * "Edit Alert Episode" beside the title and squeezed the title and description
577
+ * into a column a word or two wide.
578
+ */
579
+ test("stacks the details card header and keeps a short, gated Edit button", async () => {
580
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
581
+
582
+ const header: HTMLElement = await waitFor(() => {
583
+ return screen.getByTestId("card-header");
584
+ });
585
+
586
+ expect(header).toHaveAttribute("data-header-layout", "stacked");
587
+ expect(within(header).getByText("Episode Details")).toBeInTheDocument();
588
+ expect(
589
+ within(header).getByText("Key facts about this episode."),
590
+ ).toBeInTheDocument();
591
+
592
+ const actions: HTMLElement = await waitFor(() => {
593
+ return screen.getByTestId("card-header-actions");
594
+ });
595
+ const edit: HTMLElement = within(actions).getByRole("button", {
596
+ name: "Edit",
597
+ });
598
+
599
+ expect(edit).not.toBeDisabled();
600
+ expect(screen.queryByText("Edit Alert Episode")).toBeNull();
601
+ expect(
602
+ screen.queryByText("Here are more details for this episode."),
603
+ ).toBeNull();
604
+ expectNoCrash();
605
+ });
606
+ });
607
+
608
+ describe("Alert Episode overview: loading and layout", () => {
609
+ test("shows the overview skeleton and fires every page request at once", async () => {
610
+ getListMock.mockReturnValue(new Promise<never>(() => {}) as never);
611
+ getItemMock.mockReturnValue(new Promise<never>(() => {}) as never);
612
+
613
+ render(<AlertEpisodeView {...pageProps} />);
614
+
615
+ const status: HTMLElement = screen.getByRole("status");
616
+
617
+ expect(status).toHaveTextContent("Loading episode");
618
+ expect(
619
+ screen
620
+ .getByTestId("event-overview-skeleton-stats")
621
+ .querySelectorAll(".bg-white"),
622
+ ).toHaveLength(4);
623
+
624
+ // Nothing below the skeleton mounts (and fetches) until the page is ready.
625
+ expect(screen.queryByText("Episode Severity")).toBeNull();
626
+ expect(changeStateProps.current).toBeNull();
627
+
628
+ // All four requests are in flight together rather than one after another.
629
+ expect(listCallsFor(Alert)).toHaveLength(1);
630
+ expect(listCallsFor(AlertEpisodeStateTimeline)).toHaveLength(1);
631
+ expect(listCallsFor(AlertState)).toHaveLength(1);
632
+ expect(getItemMock).toHaveBeenCalledTimes(1);
633
+ });
634
+
635
+ test("still reads the first member's telemetry snapshot, earliest created", async () => {
636
+ await renderPage({});
637
+
638
+ const firstMemberCall: Array<any> = listCallsFor(Alert).find(
639
+ (call: Array<any>): boolean => {
640
+ return call[0].limit === 1;
641
+ },
642
+ )!;
643
+
644
+ expect(firstMemberCall[0].query.alertEpisodeId.toString()).toBe(EPISODE_ID);
645
+ expect(firstMemberCall[0].select).toEqual({
646
+ _id: true,
647
+ telemetryQuery: true,
648
+ seriesLabels: true,
649
+ });
650
+ // Alerts have no declaredAt: the earliest created alert comes first.
651
+ expect(firstMemberCall[0].sort).toEqual({
652
+ createdAt: SortOrder.Ascending,
653
+ });
654
+ });
655
+
656
+ test("renders the header, a four-cell stat bar and the member list", async () => {
657
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
658
+
659
+ expect(changeStateProps.current?.["episodeId"].toString()).toBe(EPISODE_ID);
660
+
661
+ const statBar: HTMLElement = screen.getByRole("group", {
662
+ name: "Episode timing",
663
+ });
664
+
665
+ expect(within(statBar).getByText("Acknowledged in")).toBeInTheDocument();
666
+ expect(within(statBar).getByText("Resolved in")).toBeInTheDocument();
667
+ expect(within(statBar).getByText("Duration")).toBeInTheDocument();
668
+ expect(within(statBar).getByText("Alerts")).toBeInTheDocument();
669
+ expect(statValue("Alerts")).toBe("0");
670
+
671
+ await waitFor((): void => {
672
+ expect(
673
+ screen.getByText("No alerts in this episode yet"),
674
+ ).toBeInTheDocument();
675
+ });
676
+
677
+ const memberCall: Array<any> = listCallsFor(Alert).find(
678
+ (call: Array<any>): boolean => {
679
+ return call[0].limit === 8;
680
+ },
681
+ )!;
682
+
683
+ expect(memberCall[0].query.alertEpisodeId.toString()).toBe(EPISODE_ID);
684
+ expect(memberCall[0].sort).toEqual({ createdAt: SortOrder.Descending });
685
+ expectNoCrash();
686
+ });
687
+
688
+ test("measures the stat bar from createdAt with the project's state names", async () => {
689
+ const startedAt: Date = new Date("2026-09-14T18:00:00.000Z");
690
+
691
+ const ackState: AlertState = new AlertState();
692
+ ackState.id = new ObjectID(ACK_STATE_ID);
693
+ ackState.name = "Triaged";
694
+ ackState.isAcknowledgedState = true;
695
+
696
+ const resolvedState: AlertState = new AlertState();
697
+ resolvedState.id = new ObjectID(RESOLVED_STATE_ID);
698
+ resolvedState.name = "Fixed";
699
+ resolvedState.isResolvedState = true;
700
+
701
+ const ackTimeline: AlertEpisodeStateTimeline =
702
+ new AlertEpisodeStateTimeline();
703
+ ackTimeline.alertStateId = new ObjectID(ACK_STATE_ID);
704
+ // Written long after creation, so a first-timeline start would be wrong.
705
+ ackTimeline.startsAt = new Date(startedAt.getTime() + 42 * 60 * 1000);
706
+
707
+ const resolvedTimeline: AlertEpisodeStateTimeline =
708
+ new AlertEpisodeStateTimeline();
709
+ resolvedTimeline.alertStateId = new ObjectID(RESOLVED_STATE_ID);
710
+ resolvedTimeline.startsAt = new Date(startedAt.getTime() + 125 * 60 * 1000);
711
+
712
+ getListMock.mockImplementation((...args: Array<any>) => {
713
+ const modelType: unknown = args[0]?.modelType;
714
+
715
+ if (modelType === AlertState) {
716
+ return Promise.resolve({
717
+ data: [ackState, resolvedState],
718
+ count: 2,
719
+ skip: 0,
720
+ limit: 2,
721
+ });
722
+ }
723
+
724
+ if (modelType === AlertEpisodeStateTimeline) {
725
+ return Promise.resolve({
726
+ data: [resolvedTimeline, ackTimeline],
727
+ count: 2,
728
+ skip: 0,
729
+ limit: 2,
730
+ });
731
+ }
732
+
733
+ return Promise.resolve(emptyList());
734
+ });
735
+
736
+ const episode: AlertEpisode = buildEpisode({});
737
+ episode.createdAt = startedAt;
738
+ episode.alertCount = 3;
739
+
740
+ getItemMock.mockResolvedValue(episode as never);
741
+
742
+ render(<AlertEpisodeView {...pageProps} />);
743
+
744
+ await waitFor((): void => {
745
+ expect(screen.getByText("Triaged in")).toBeInTheDocument();
746
+ });
747
+
748
+ expect(statValue("Triaged in")).toBe("42 minutes");
749
+ expect(statValue("Fixed in")).toBe("2 hours, 5 minutes");
750
+ // Resolved now, so the duration is closed: created -> resolved.
751
+ expect(statValue("Duration")).toBe("2 hours, 5 minutes");
752
+ expect(statValue("Alerts")).toBe("3");
753
+ });
754
+
755
+ test("a state change refreshes the page in place instead of unmounting it", async () => {
756
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
757
+
758
+ await waitFor((): void => {
759
+ expect(
760
+ screen.getByText("No alerts in this episode yet"),
761
+ ).toBeInTheDocument();
762
+ });
763
+
764
+ const timelineCallsBefore: number = listCallsFor(
765
+ AlertEpisodeStateTimeline,
766
+ ).length;
767
+ const memberCallsBefore: number = listCallsFor(Alert).filter(
768
+ (call: Array<any>): boolean => {
769
+ return call[0].limit === 8;
770
+ },
771
+ ).length;
772
+ const detailFetchesBefore: number = getItemMock.mock.calls.length;
773
+
774
+ let pending: Promise<void> | undefined;
775
+
776
+ act(() => {
777
+ pending = changeStateProps.current!["onActionComplete"]();
778
+ });
779
+
780
+ // No skeleton, no loader: the stat bar and header stay on screen.
781
+ expect(screen.queryByText("Loading episode")).toBeNull();
782
+ expect(
783
+ screen.getByRole("group", { name: "Episode timing" }),
784
+ ).toBeInTheDocument();
785
+
786
+ await act(async () => {
787
+ await pending;
788
+ });
789
+
790
+ await waitFor((): void => {
791
+ expect(listCallsFor(AlertEpisodeStateTimeline).length).toBe(
792
+ timelineCallsBefore + 1,
793
+ );
794
+ // The member list reloads too: member states moved with the episode.
795
+ expect(
796
+ listCallsFor(Alert).filter((call: Array<any>): boolean => {
797
+ return call[0].limit === 8;
798
+ }).length,
799
+ ).toBe(memberCallsBefore + 1);
800
+ // Page timing getItem plus the details card reload.
801
+ expect(getItemMock.mock.calls.length).toBeGreaterThanOrEqual(
802
+ detailFetchesBefore + 2,
803
+ );
804
+ });
805
+
806
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
463
807
  expectNoCrash();
464
808
  });
809
+
810
+ test("a failed first load offers a retry that recovers", async () => {
811
+ getItemMock.mockResolvedValue(buildEpisode({}) as never);
812
+ getListMock.mockRejectedValueOnce(new Error("Episode service down"));
813
+
814
+ render(<AlertEpisodeView {...pageProps} />);
815
+
816
+ await waitFor((): void => {
817
+ expect(screen.getByText("Episode service down")).toBeInTheDocument();
818
+ });
819
+
820
+ fireEvent.click(screen.getByTestId("refresh-button"));
821
+
822
+ await waitFor((): void => {
823
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
824
+ });
825
+
826
+ expect(screen.queryByText("Episode service down")).toBeNull();
827
+ });
828
+
829
+ test("a failed refresh keeps the page and says so inline", async () => {
830
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
831
+
832
+ getListMock.mockRejectedValueOnce(new Error("Timeline unavailable"));
833
+
834
+ await act(async () => {
835
+ await changeStateProps.current!["onActionComplete"]();
836
+ });
837
+
838
+ await waitFor((): void => {
839
+ expect(screen.getByRole("alert")).toHaveTextContent(
840
+ "Couldn't refresh episode timings: Timeline unavailable",
841
+ );
842
+ });
843
+
844
+ expect(
845
+ screen.getByRole("group", { name: "Episode timing" }),
846
+ ).toBeInTheDocument();
847
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
848
+ });
849
+ });
850
+
851
+ describe("Alert Episode overview: moving to another episode on the same route", () => {
852
+ /*
853
+ * The overview is an index route inside the episode layout, so moving to
854
+ * another episode re-renders this page with a new id instead of remounting
855
+ * it. The first render for the new id used to mount the header, the member
856
+ * list and the details card for the new id over the previous episode's
857
+ * numbers (each one fetching) before an effect flipped back to the skeleton.
858
+ */
859
+ const OTHER_EPISODE_ID: string = "99999999-9999-4999-8999-999999999999";
860
+
861
+ type NavigateToFunction = (episodeId: string) => void;
862
+
863
+ const navigateTo: NavigateToFunction = (episodeId: string): void => {
864
+ const path: string = `/dashboard/${PROJECT_ID}/alerts/episodes/${episodeId}`;
865
+
866
+ window.history.pushState({}, "", path);
867
+ Navigation.setLocation({
868
+ pathname: path,
869
+ search: "",
870
+ hash: "",
871
+ state: null,
872
+ key: "other",
873
+ } as Location);
874
+ };
875
+
876
+ // The page's own timing read asks for resolvedAt; the details card does not.
877
+ type IsPageReadFunction = (call: Array<any>) => boolean;
878
+
879
+ const isPageRead: IsPageReadFunction = (call: Array<any>): boolean => {
880
+ return call[0]?.select?.resolvedAt === true;
881
+ };
882
+
883
+ type ReadsForFunction = (
884
+ episodeId: string,
885
+ predicate: IsPageReadFunction,
886
+ ) => number;
887
+
888
+ const readsFor: ReadsForFunction = (
889
+ episodeId: string,
890
+ predicate: IsPageReadFunction,
891
+ ): number => {
892
+ return getItemMock.mock.calls.filter((call: Array<any>): boolean => {
893
+ return call[0]?.id?.toString() === episodeId && predicate(call);
894
+ }).length;
895
+ };
896
+
897
+ type ServeEpisodesFunction = (otherPageRead: Promise<AlertEpisode>) => void;
898
+
899
+ const serveEpisodes: ServeEpisodesFunction = (
900
+ otherPageRead: Promise<AlertEpisode>,
901
+ ): void => {
902
+ getItemMock.mockImplementation((...args: Array<any>) => {
903
+ const request: Record<string, any> = args[0];
904
+
905
+ if (request["id"]?.toString() === OTHER_EPISODE_ID && isPageRead(args)) {
906
+ return otherPageRead;
907
+ }
908
+
909
+ const episode: AlertEpisode = buildEpisode({
910
+ state: buildState("Created", new Color("#4b5563")),
911
+ });
912
+
913
+ if (request["id"]?.toString() === OTHER_EPISODE_ID) {
914
+ episode.id = new ObjectID(OTHER_EPISODE_ID);
915
+ episode.alertCount = 5;
916
+ }
917
+
918
+ return Promise.resolve(episode);
919
+ });
920
+ };
921
+
922
+ test("shows the skeleton first and renders nothing for the new id before its numbers land", async () => {
923
+ let resolveOther: (episode: AlertEpisode) => void = (): void => {};
924
+ const otherPageRead: Promise<AlertEpisode> = new Promise<AlertEpisode>(
925
+ (resolve: (episode: AlertEpisode) => void) => {
926
+ resolveOther = resolve;
927
+ },
928
+ );
929
+
930
+ serveEpisodes(otherPageRead);
931
+
932
+ const view: ReturnType<typeof render> = render(
933
+ <AlertEpisodeView {...pageProps} />,
934
+ );
935
+
936
+ await waitFor((): void => {
937
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
938
+ });
939
+
940
+ const headerRendersBefore: number = changeStateHistory.length;
941
+
942
+ navigateTo(OTHER_EPISODE_ID);
943
+ view.rerender(<AlertEpisodeView {...pageProps} />);
944
+
945
+ expect(screen.getByRole("status")).toHaveTextContent("Loading episode");
946
+ expect(screen.queryByText("Episode Severity")).toBeNull();
947
+
948
+ await waitFor((): void => {
949
+ expect(readsFor(OTHER_EPISODE_ID, isPageRead)).toBe(1);
950
+ });
951
+
952
+ // Neither the header nor the details card rendered for the new id yet.
953
+ expect(
954
+ changeStateHistory
955
+ .slice(headerRendersBefore)
956
+ .filter((props: Record<string, any>): boolean => {
957
+ return props["episodeId"]?.toString() === OTHER_EPISODE_ID;
958
+ }),
959
+ ).toEqual([]);
960
+ expect(
961
+ readsFor(OTHER_EPISODE_ID, (call: Array<any>): boolean => {
962
+ return !isPageRead(call);
963
+ }),
964
+ ).toBe(0);
965
+
966
+ const otherEpisode: AlertEpisode = buildEpisode({});
967
+ otherEpisode.id = new ObjectID(OTHER_EPISODE_ID);
968
+ otherEpisode.alertCount = 5;
969
+
970
+ await act(async () => {
971
+ resolveOther(otherEpisode);
972
+ });
973
+
974
+ await waitFor((): void => {
975
+ expect(statValue("Alerts")).toBe("5");
976
+ });
977
+
978
+ expect(changeStateProps.current?.["episodeId"].toString()).toBe(
979
+ OTHER_EPISODE_ID,
980
+ );
981
+
982
+ await waitFor((): void => {
983
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
984
+ });
985
+
986
+ // The details card mounted once for the new episode: one read, not two.
987
+ expect(
988
+ readsFor(OTHER_EPISODE_ID, (call: Array<any>): boolean => {
989
+ return !isPageRead(call);
990
+ }),
991
+ ).toBe(1);
992
+ expectNoCrash();
993
+ });
994
+
995
+ test("an action on the previous episode that finishes after the switch cannot cancel the next one's load", async () => {
996
+ let resolveOther: (episode: AlertEpisode) => void = (): void => {};
997
+ const otherPageRead: Promise<AlertEpisode> = new Promise<AlertEpisode>(
998
+ (resolve: (episode: AlertEpisode) => void) => {
999
+ resolveOther = resolve;
1000
+ },
1001
+ );
1002
+
1003
+ serveEpisodes(otherPageRead);
1004
+
1005
+ const view: ReturnType<typeof render> = render(
1006
+ <AlertEpisodeView {...pageProps} />,
1007
+ );
1008
+
1009
+ await waitFor((): void => {
1010
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
1011
+ });
1012
+
1013
+ // Handed to the previous episode's header, which can still call it.
1014
+ const previousOnActionComplete: () => Promise<void> =
1015
+ changeStateProps.current!["onActionComplete"];
1016
+
1017
+ navigateTo(OTHER_EPISODE_ID);
1018
+ view.rerender(<AlertEpisodeView {...pageProps} />);
1019
+
1020
+ await waitFor((): void => {
1021
+ expect(readsFor(OTHER_EPISODE_ID, isPageRead)).toBe(1);
1022
+ });
1023
+
1024
+ const timelineReadsBefore: number = listCallsFor(
1025
+ AlertEpisodeStateTimeline,
1026
+ ).length;
1027
+ const previousPageReadsBefore: number = readsFor(EPISODE_ID, isPageRead);
1028
+
1029
+ await act(async () => {
1030
+ await previousOnActionComplete();
1031
+ });
1032
+
1033
+ // Nothing was read again for the episode the reader already left.
1034
+ expect(listCallsFor(AlertEpisodeStateTimeline).length).toBe(
1035
+ timelineReadsBefore,
1036
+ );
1037
+ expect(readsFor(EPISODE_ID, isPageRead)).toBe(previousPageReadsBefore);
1038
+
1039
+ const otherEpisode: AlertEpisode = buildEpisode({});
1040
+ otherEpisode.id = new ObjectID(OTHER_EPISODE_ID);
1041
+ otherEpisode.alertCount = 5;
1042
+
1043
+ await act(async () => {
1044
+ resolveOther(otherEpisode);
1045
+ });
1046
+
1047
+ await waitFor((): void => {
1048
+ expect(statValue("Alerts")).toBe("5");
1049
+ });
1050
+ });
465
1051
  });