@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";
@@ -32,16 +40,35 @@ import getJestMockFunction, { MockFunction } from "../../MockType";
32
40
  * guard must NOT be what makes these pass: every case asserts the page never
33
41
  * logged "threw and was ignored", which is how a regression at the page level
34
42
  * stays visible instead of being quietly absorbed by the framework.
43
+ *
44
+ * The overview itself was later redesigned (header, stat bar, member list,
45
+ * compact details column), so the last describe blocks pin that layout's
46
+ * behaviour against the same fake server.
35
47
  */
36
48
 
37
49
  const EPISODE_ID: string = "11111111-1111-4111-8111-111111111111";
38
50
  const PROJECT_ID: string = "22222222-2222-4222-8222-222222222222";
51
+ const ACK_STATE_ID: string = "66666666-6666-4666-8666-666666666666";
52
+ const RESOLVED_STATE_ID: string = "77777777-7777-4777-8777-777777777777";
39
53
 
40
54
  const getItemMock: MockFunction = getJestMockFunction();
41
55
  const getListMock: MockFunction = getJestMockFunction();
42
56
  const getCommonHeadersMock: MockFunction = getJestMockFunction();
43
57
  const loggerErrorMock: MockFunction = getJestMockFunction();
44
58
 
59
+ // The last props the stubbed header was rendered with.
60
+ const changeStateProps: { current: Record<string, any> | null } = {
61
+ current: null,
62
+ };
63
+
64
+ // Every set of props the stubbed header was rendered with, in order.
65
+ const changeStateHistory: Array<Record<string, any>> = [];
66
+
67
+ // The last props the stubbed roles card was rendered with.
68
+ const rolesProps: { current: Record<string, any> | null } = {
69
+ current: null,
70
+ };
71
+
45
72
  /*
46
73
  * The arrow wrappers are load bearing: jest.mock is hoisted above the compiled
47
74
  * requires, so the mock consts above are still unassigned when the factory
@@ -87,14 +114,17 @@ jest.mock("../../../UI/Utils/Logger", () => {
87
114
  /*
88
115
  * The page's three heavy children each drive their own API traffic and none of
89
116
  * them renders a detail field. Stubbing them keeps this suite pointed at the
90
- * Episode Details card, which is where #3374 lives.
117
+ * Episode Details card, which is where #3374 lives. The header stub records
118
+ * its props so the layout tests can drive a state change.
91
119
  */
92
120
  jest.mock(
93
121
  "../../../../App/FeatureSet/Dashboard/src/Components/IncidentEpisode/ChangeState",
94
122
  () => {
95
123
  return {
96
124
  __esModule: true,
97
- default: () => {
125
+ default: (props: Record<string, any>) => {
126
+ changeStateProps.current = props;
127
+ changeStateHistory.push(props);
98
128
  return null;
99
129
  },
100
130
  };
@@ -106,7 +136,8 @@ jest.mock(
106
136
  () => {
107
137
  return {
108
138
  __esModule: true,
109
- default: () => {
139
+ default: (props: Record<string, any>) => {
140
+ rolesProps.current = props;
110
141
  return null;
111
142
  },
112
143
  };
@@ -127,10 +158,13 @@ jest.mock(
127
158
 
128
159
  import IncidentEpisodeView from "../../../../App/FeatureSet/Dashboard/src/Pages/Incidents/EpisodeView/Index";
129
160
  import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
161
+ import Incident from "../../../Models/DatabaseModels/Incident";
130
162
  import IncidentEpisode from "../../../Models/DatabaseModels/IncidentEpisode";
163
+ import IncidentEpisodeStateTimeline from "../../../Models/DatabaseModels/IncidentEpisodeStateTimeline";
131
164
  import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
132
165
  import IncidentState from "../../../Models/DatabaseModels/IncidentState";
133
166
  import Route from "../../../Types/API/Route";
167
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
134
168
  import Color from "../../../Types/Color";
135
169
  import ObjectID from "../../../Types/ObjectID";
136
170
  import Permission from "../../../Types/Permission";
@@ -225,6 +259,22 @@ const buildEpisode: BuildEpisodeFunction = (
225
259
  return episode;
226
260
  };
227
261
 
262
+ type EmptyListFunction = () => {
263
+ data: Array<unknown>;
264
+ count: number;
265
+ skip: number;
266
+ limit: number;
267
+ };
268
+
269
+ const emptyList: EmptyListFunction = () => {
270
+ return {
271
+ data: [],
272
+ count: 0,
273
+ skip: 0,
274
+ limit: 0,
275
+ };
276
+ };
277
+
228
278
  type RenderPageFunction = (spec: EpisodeSpec) => Promise<void>;
229
279
 
230
280
  const renderPage: RenderPageFunction = async (
@@ -243,6 +293,7 @@ const renderPage: RenderPageFunction = async (
243
293
  * Detail renders each field as <container><FieldLabel/><value/></container>,
244
294
  * and FieldLabel's root is the only div.space-y-1 in that subtree. Walking up
245
295
  * from the label and taking the sibling is what a person reading the row does.
296
+ * The compact style the overview uses keeps exactly that shape.
246
297
  */
247
298
  type FieldValueFunction = (title: string) => string;
248
299
 
@@ -281,6 +332,16 @@ const pillFor: PillForFunction = (title: string): HTMLElement => {
281
332
  return pill;
282
333
  };
283
334
 
335
+ // A stat bar cell: label span -> label row -> cell; the value is the 2nd child.
336
+ type StatValueFunction = (label: string) => string;
337
+
338
+ const statValue: StatValueFunction = (label: string): string => {
339
+ const cell: HTMLElement | null | undefined =
340
+ screen.getByText(label).parentElement?.parentElement;
341
+
342
+ return (cell?.children[1]?.textContent || "").trim();
343
+ };
344
+
284
345
  type ExpectNoCrashFunction = () => void;
285
346
 
286
347
  /*
@@ -302,18 +363,26 @@ const expectNoCrash: ExpectNoCrashFunction = (): void => {
302
363
  expect(swallowed).toEqual([]);
303
364
  };
304
365
 
366
+ type CallsForModelFunction = (modelType: unknown) => Array<Array<any>>;
367
+
368
+ const listCallsFor: CallsForModelFunction = (
369
+ modelType: unknown,
370
+ ): Array<Array<any>> => {
371
+ return getListMock.mock.calls.filter((call: Array<any>): boolean => {
372
+ return call[0]?.modelType === modelType;
373
+ });
374
+ };
375
+
305
376
  beforeEach((): void => {
306
377
  getItemMock.mockReset();
307
378
  getListMock.mockReset();
308
379
  getCommonHeadersMock.mockReset();
309
380
  loggerErrorMock.mockReset();
381
+ changeStateProps.current = null;
382
+ changeStateHistory.length = 0;
383
+ rolesProps.current = null;
310
384
 
311
- getListMock.mockResolvedValue({
312
- data: [],
313
- count: 0,
314
- skip: 0,
315
- limit: 0,
316
- } as never);
385
+ getListMock.mockResolvedValue(emptyList() as never);
317
386
  getCommonHeadersMock.mockReturnValue({} as never);
318
387
 
319
388
  const path: string = `/dashboard/${PROJECT_ID}/incidents/episodes/${EPISODE_ID}`;
@@ -368,12 +437,13 @@ describe("Incident Episode detail page: severity (issue #3374)", () => {
368
437
  test("still renders the rest of the episode when severity is missing", async () => {
369
438
  await renderPage({ state: buildState("Created", new Color("#4b5563")) });
370
439
 
371
- expect(screen.getByText("Episode Title")).toBeInTheDocument();
372
- expect(fieldValue("Episode Title")).toEqual("episode without severity");
373
440
  expect(fieldValue("Episode Number")).toContain("#7");
374
441
  expect(fieldValue("Incident Count")).toEqual("0");
375
442
  // No createdByUser on an API-created episode.
376
443
  expect(fieldValue("Created By")).toEqual("System");
444
+ // An episode with no grouping rule is a manual one, not a crash.
445
+ expect(fieldValue("Grouping Rule")).toEqual("Manual Episode");
446
+ expect(fieldValue("Episode ID")).toContain(EPISODE_ID);
377
447
  expectNoCrash();
378
448
  });
379
449
 
@@ -468,7 +538,542 @@ describe("Incident Episode detail page: both relations missing", () => {
468
538
 
469
539
  expect(fieldValue("Episode Severity")).toEqual("-");
470
540
  expect(fieldValue("Current State")).toEqual("-");
471
- expect(fieldValue("Episode Title")).toEqual("episode without severity");
541
+ expect(fieldValue("Episode Number")).toContain("#7");
542
+ expectNoCrash();
543
+ });
544
+ });
545
+
546
+ describe("Incident Episode overview: details column", () => {
547
+ test("uses the compact single-column style with the episode ID last", async () => {
548
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
549
+
550
+ const numberRow: HTMLElement | null | undefined = screen
551
+ .getByText("Episode Number")
552
+ .closest("div.space-y-1")?.parentElement;
553
+ const grid: HTMLElement | null | undefined = numberRow?.parentElement;
554
+
555
+ expect(grid).toHaveClass("grid", "grid-cols-1", "divide-y");
556
+ expect(grid).toHaveClass("sm:grid-cols-1");
557
+ expect(numberRow).toHaveClass("py-3");
558
+
559
+ const rowTitles: Array<string> = Array.from(grid!.children).map(
560
+ (row: Element): string => {
561
+ return (row.querySelector("label")?.textContent || "").trim();
562
+ },
563
+ );
564
+
565
+ expect(rowTitles).toEqual([
566
+ "Episode Number",
567
+ "Current State",
568
+ "Episode Severity",
569
+ "Incident Count",
570
+ "Grouping Rule",
571
+ "Created By",
572
+ "On-Call Duty Policies",
573
+ "Created At",
574
+ "Labels",
575
+ "Episode ID",
576
+ ]);
577
+ expectNoCrash();
578
+ });
579
+
580
+ test("no longer repeats the title the header already shows", async () => {
581
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
582
+
583
+ expect(screen.queryByText("Episode Title")).toBeNull();
584
+ expect(screen.queryByText("Last Incident Added At")).toBeNull();
585
+ // The number is plain text now, not a hand-rolled SVG chip.
586
+ const numberRow: HTMLElement | null | undefined = screen
587
+ .getByText("Episode Number")
588
+ .closest("div.space-y-1")?.parentElement;
589
+
590
+ expect(numberRow?.querySelector("svg")).toBeNull();
591
+ });
592
+ /*
593
+ * The details card sits in a ~300px column. Its old side-by-side header put
594
+ * "Edit Incident Episode" beside the title and squeezed the title and description
595
+ * into a column a word or two wide.
596
+ */
597
+ test("stacks the details card header and keeps a short, gated Edit button", async () => {
598
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
599
+
600
+ const header: HTMLElement = await waitFor(() => {
601
+ return screen.getByTestId("card-header");
602
+ });
603
+
604
+ expect(header).toHaveAttribute("data-header-layout", "stacked");
605
+ expect(within(header).getByText("Episode Details")).toBeInTheDocument();
606
+ expect(
607
+ within(header).getByText("Key facts about this episode."),
608
+ ).toBeInTheDocument();
609
+
610
+ const actions: HTMLElement = await waitFor(() => {
611
+ return screen.getByTestId("card-header-actions");
612
+ });
613
+ const edit: HTMLElement = within(actions).getByRole("button", {
614
+ name: "Edit",
615
+ });
616
+
617
+ expect(edit).not.toBeDisabled();
618
+ expect(screen.queryByText("Edit Incident Episode")).toBeNull();
619
+ expect(
620
+ screen.queryByText("Here are more details for this episode."),
621
+ ).toBeNull();
622
+ // The roles card shares the column and stacks the same way.
623
+ expect(rolesProps.current?.["headerLayout"]).toBe("stacked");
624
+ expectNoCrash();
625
+ });
626
+ });
627
+
628
+ describe("Incident Episode overview: loading and layout", () => {
629
+ test("shows the overview skeleton and fires every page request at once", async () => {
630
+ getListMock.mockReturnValue(new Promise<never>(() => {}) as never);
631
+ getItemMock.mockReturnValue(new Promise<never>(() => {}) as never);
632
+
633
+ render(<IncidentEpisodeView {...pageProps} />);
634
+
635
+ const status: HTMLElement = screen.getByRole("status");
636
+
637
+ expect(status).toHaveTextContent("Loading episode");
638
+ expect(
639
+ screen
640
+ .getByTestId("event-overview-skeleton-stats")
641
+ .querySelectorAll(".bg-white"),
642
+ ).toHaveLength(4);
643
+
644
+ // Nothing below the skeleton mounts (and fetches) until the page is ready.
645
+ expect(screen.queryByText("Episode Severity")).toBeNull();
646
+ expect(changeStateProps.current).toBeNull();
647
+
648
+ // All four requests are in flight together rather than one after another.
649
+ expect(listCallsFor(Incident)).toHaveLength(1);
650
+ expect(listCallsFor(IncidentEpisodeStateTimeline)).toHaveLength(1);
651
+ expect(listCallsFor(IncidentState)).toHaveLength(1);
652
+ expect(getItemMock).toHaveBeenCalledTimes(1);
653
+ });
654
+
655
+ test("still reads the first member's telemetry snapshot, earliest declared", async () => {
656
+ await renderPage({});
657
+
658
+ const firstMemberCall: Array<any> = listCallsFor(Incident).find(
659
+ (call: Array<any>): boolean => {
660
+ return call[0].limit === 1;
661
+ },
662
+ )!;
663
+
664
+ expect(firstMemberCall[0].query.incidentEpisodeId.toString()).toBe(
665
+ EPISODE_ID,
666
+ );
667
+ expect(firstMemberCall[0].select).toEqual({
668
+ _id: true,
669
+ telemetryQuery: true,
670
+ seriesLabels: true,
671
+ });
672
+ expect(firstMemberCall[0].sort).toEqual({
673
+ declaredAt: SortOrder.Ascending,
674
+ });
675
+ });
676
+
677
+ test("renders the header, a four-cell stat bar and the member list", async () => {
678
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
679
+
680
+ expect(changeStateProps.current?.["episodeId"].toString()).toBe(EPISODE_ID);
681
+
682
+ const statBar: HTMLElement = screen.getByRole("group", {
683
+ name: "Episode timing",
684
+ });
685
+
686
+ expect(within(statBar).getByText("Acknowledged in")).toBeInTheDocument();
687
+ expect(within(statBar).getByText("Resolved in")).toBeInTheDocument();
688
+ expect(within(statBar).getByText("Duration")).toBeInTheDocument();
689
+ expect(within(statBar).getByText("Incidents")).toBeInTheDocument();
690
+ expect(statValue("Incidents")).toBe("0");
691
+
692
+ await waitFor((): void => {
693
+ expect(
694
+ screen.getByText("No incidents in this episode yet"),
695
+ ).toBeInTheDocument();
696
+ });
697
+
698
+ const memberCall: Array<any> = listCallsFor(Incident).find(
699
+ (call: Array<any>): boolean => {
700
+ return call[0].limit === 8;
701
+ },
702
+ )!;
703
+
704
+ expect(memberCall[0].query.incidentEpisodeId.toString()).toBe(EPISODE_ID);
705
+ expect(memberCall[0].sort).toEqual({ declaredAt: SortOrder.Descending });
706
+ expectNoCrash();
707
+ });
708
+
709
+ test("measures the stat bar from declaredAt with the project's state names", async () => {
710
+ const declaredAt: Date = new Date("2026-09-14T18:00:00.000Z");
711
+
712
+ const ackState: IncidentState = new IncidentState();
713
+ ackState.id = new ObjectID(ACK_STATE_ID);
714
+ ackState.name = "Triaged";
715
+ ackState.isAcknowledgedState = true;
716
+
717
+ const resolvedState: IncidentState = new IncidentState();
718
+ resolvedState.id = new ObjectID(RESOLVED_STATE_ID);
719
+ resolvedState.name = "Fixed";
720
+ resolvedState.isResolvedState = true;
721
+
722
+ const ackTimeline: IncidentEpisodeStateTimeline =
723
+ new IncidentEpisodeStateTimeline();
724
+ ackTimeline.incidentStateId = new ObjectID(ACK_STATE_ID);
725
+ // Written long after creation, so a first-timeline start would be wrong.
726
+ ackTimeline.startsAt = new Date(declaredAt.getTime() + 42 * 60 * 1000);
727
+
728
+ const resolvedTimeline: IncidentEpisodeStateTimeline =
729
+ new IncidentEpisodeStateTimeline();
730
+ resolvedTimeline.incidentStateId = new ObjectID(RESOLVED_STATE_ID);
731
+ resolvedTimeline.startsAt = new Date(
732
+ declaredAt.getTime() + 125 * 60 * 1000,
733
+ );
734
+
735
+ getListMock.mockImplementation((...args: Array<any>) => {
736
+ const modelType: unknown = args[0]?.modelType;
737
+
738
+ if (modelType === IncidentState) {
739
+ return Promise.resolve({
740
+ data: [ackState, resolvedState],
741
+ count: 2,
742
+ skip: 0,
743
+ limit: 2,
744
+ });
745
+ }
746
+
747
+ if (modelType === IncidentEpisodeStateTimeline) {
748
+ return Promise.resolve({
749
+ data: [resolvedTimeline, ackTimeline],
750
+ count: 2,
751
+ skip: 0,
752
+ limit: 2,
753
+ });
754
+ }
755
+
756
+ return Promise.resolve(emptyList());
757
+ });
758
+
759
+ const episode: IncidentEpisode = buildEpisode({});
760
+ episode.declaredAt = declaredAt;
761
+ episode.createdAt = new Date(declaredAt.getTime() - 3 * 24 * 3600 * 1000);
762
+ episode.incidentCount = 3;
763
+
764
+ getItemMock.mockResolvedValue(episode as never);
765
+
766
+ render(<IncidentEpisodeView {...pageProps} />);
767
+
768
+ await waitFor((): void => {
769
+ expect(screen.getByText("Triaged in")).toBeInTheDocument();
770
+ });
771
+
772
+ expect(statValue("Triaged in")).toBe("42 minutes");
773
+ expect(statValue("Fixed in")).toBe("2 hours, 5 minutes");
774
+ // Resolved now, so the duration is closed: declared -> resolved.
775
+ expect(statValue("Duration")).toBe("2 hours, 5 minutes");
776
+ expect(statValue("Incidents")).toBe("3");
777
+ });
778
+
779
+ test("a state change refreshes the page in place instead of unmounting it", async () => {
780
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
781
+
782
+ await waitFor((): void => {
783
+ expect(
784
+ screen.getByText("No incidents in this episode yet"),
785
+ ).toBeInTheDocument();
786
+ });
787
+
788
+ const timelineCallsBefore: number = listCallsFor(
789
+ IncidentEpisodeStateTimeline,
790
+ ).length;
791
+ const memberCallsBefore: number = listCallsFor(Incident).filter(
792
+ (call: Array<any>): boolean => {
793
+ return call[0].limit === 8;
794
+ },
795
+ ).length;
796
+ const detailFetchesBefore: number = getItemMock.mock.calls.length;
797
+
798
+ let pending: Promise<void> | undefined;
799
+
800
+ act(() => {
801
+ pending = changeStateProps.current!["onActionComplete"]();
802
+ });
803
+
804
+ // No skeleton, no loader: the stat bar and header stay on screen.
805
+ expect(screen.queryByText("Loading episode")).toBeNull();
806
+ expect(
807
+ screen.getByRole("group", { name: "Episode timing" }),
808
+ ).toBeInTheDocument();
809
+
810
+ await act(async () => {
811
+ await pending;
812
+ });
813
+
814
+ await waitFor((): void => {
815
+ expect(listCallsFor(IncidentEpisodeStateTimeline).length).toBe(
816
+ timelineCallsBefore + 1,
817
+ );
818
+ // The member list reloads too: member states moved with the episode.
819
+ expect(
820
+ listCallsFor(Incident).filter((call: Array<any>): boolean => {
821
+ return call[0].limit === 8;
822
+ }).length,
823
+ ).toBe(memberCallsBefore + 1);
824
+ // Page timing getItem plus the details card reload.
825
+ expect(getItemMock.mock.calls.length).toBeGreaterThanOrEqual(
826
+ detailFetchesBefore + 2,
827
+ );
828
+ });
829
+
830
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
831
+ expectNoCrash();
832
+ });
833
+
834
+ test("a failed first load offers a retry that recovers", async () => {
835
+ getItemMock.mockResolvedValue(buildEpisode({}) as never);
836
+ getListMock.mockRejectedValueOnce(new Error("Episode service down"));
837
+
838
+ render(<IncidentEpisodeView {...pageProps} />);
839
+
840
+ await waitFor((): void => {
841
+ expect(screen.getByText("Episode service down")).toBeInTheDocument();
842
+ });
843
+
844
+ fireEvent.click(screen.getByTestId("refresh-button"));
845
+
846
+ await waitFor((): void => {
847
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
848
+ });
849
+
850
+ expect(screen.queryByText("Episode service down")).toBeNull();
851
+ });
852
+
853
+ test("a failed refresh keeps the page and says so inline", async () => {
854
+ await renderPage({ state: buildState("Created", new Color("#4b5563")) });
855
+
856
+ getListMock.mockRejectedValueOnce(new Error("Timeline unavailable"));
857
+
858
+ await act(async () => {
859
+ await changeStateProps.current!["onActionComplete"]();
860
+ });
861
+
862
+ await waitFor((): void => {
863
+ expect(screen.getByRole("alert")).toHaveTextContent(
864
+ "Couldn't refresh episode timings: Timeline unavailable",
865
+ );
866
+ });
867
+
868
+ expect(
869
+ screen.getByRole("group", { name: "Episode timing" }),
870
+ ).toBeInTheDocument();
871
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
872
+ });
873
+ });
874
+
875
+ describe("Incident Episode overview: moving to another episode on the same route", () => {
876
+ /*
877
+ * The overview is an index route inside the episode layout, so moving to
878
+ * another episode re-renders this page with a new id instead of remounting
879
+ * it. The first render for the new id used to mount the header, the member
880
+ * list and the details card for the new id over the previous episode's
881
+ * numbers (each one fetching) before an effect flipped back to the skeleton.
882
+ */
883
+ const OTHER_EPISODE_ID: string = "99999999-9999-4999-8999-999999999999";
884
+
885
+ type NavigateToFunction = (episodeId: string) => void;
886
+
887
+ const navigateTo: NavigateToFunction = (episodeId: string): void => {
888
+ const path: string = `/dashboard/${PROJECT_ID}/incidents/episodes/${episodeId}`;
889
+
890
+ window.history.pushState({}, "", path);
891
+ Navigation.setLocation({
892
+ pathname: path,
893
+ search: "",
894
+ hash: "",
895
+ state: null,
896
+ key: "other",
897
+ } as Location);
898
+ };
899
+
900
+ // The page's own timing read asks for resolvedAt; the details card does not.
901
+ type IsPageReadFunction = (call: Array<any>) => boolean;
902
+
903
+ const isPageRead: IsPageReadFunction = (call: Array<any>): boolean => {
904
+ return call[0]?.select?.resolvedAt === true;
905
+ };
906
+
907
+ type ReadsForFunction = (
908
+ episodeId: string,
909
+ predicate: IsPageReadFunction,
910
+ ) => number;
911
+
912
+ const readsFor: ReadsForFunction = (
913
+ episodeId: string,
914
+ predicate: IsPageReadFunction,
915
+ ): number => {
916
+ return getItemMock.mock.calls.filter((call: Array<any>): boolean => {
917
+ return call[0]?.id?.toString() === episodeId && predicate(call);
918
+ }).length;
919
+ };
920
+
921
+ type ServeEpisodesFunction = (
922
+ otherPageRead: Promise<IncidentEpisode>,
923
+ ) => void;
924
+
925
+ const serveEpisodes: ServeEpisodesFunction = (
926
+ otherPageRead: Promise<IncidentEpisode>,
927
+ ): void => {
928
+ getItemMock.mockImplementation((...args: Array<any>) => {
929
+ const request: Record<string, any> = args[0];
930
+
931
+ if (request["id"]?.toString() === OTHER_EPISODE_ID && isPageRead(args)) {
932
+ return otherPageRead;
933
+ }
934
+
935
+ const episode: IncidentEpisode = buildEpisode({
936
+ state: buildState("Created", new Color("#4b5563")),
937
+ });
938
+
939
+ if (request["id"]?.toString() === OTHER_EPISODE_ID) {
940
+ episode.id = new ObjectID(OTHER_EPISODE_ID);
941
+ episode.incidentCount = 5;
942
+ }
943
+
944
+ return Promise.resolve(episode);
945
+ });
946
+ };
947
+
948
+ test("shows the skeleton first and renders nothing for the new id before its numbers land", async () => {
949
+ let resolveOther: (episode: IncidentEpisode) => void = (): void => {};
950
+ const otherPageRead: Promise<IncidentEpisode> =
951
+ new Promise<IncidentEpisode>(
952
+ (resolve: (episode: IncidentEpisode) => void) => {
953
+ resolveOther = resolve;
954
+ },
955
+ );
956
+
957
+ serveEpisodes(otherPageRead);
958
+
959
+ const view: ReturnType<typeof render> = render(
960
+ <IncidentEpisodeView {...pageProps} />,
961
+ );
962
+
963
+ await waitFor((): void => {
964
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
965
+ });
966
+
967
+ const headerRendersBefore: number = changeStateHistory.length;
968
+
969
+ navigateTo(OTHER_EPISODE_ID);
970
+ view.rerender(<IncidentEpisodeView {...pageProps} />);
971
+
972
+ expect(screen.getByRole("status")).toHaveTextContent("Loading episode");
973
+ expect(screen.queryByText("Episode Severity")).toBeNull();
974
+
975
+ await waitFor((): void => {
976
+ expect(readsFor(OTHER_EPISODE_ID, isPageRead)).toBe(1);
977
+ });
978
+
979
+ // Neither the header nor the details card rendered for the new id yet.
980
+ expect(
981
+ changeStateHistory
982
+ .slice(headerRendersBefore)
983
+ .filter((props: Record<string, any>): boolean => {
984
+ return props["episodeId"]?.toString() === OTHER_EPISODE_ID;
985
+ }),
986
+ ).toEqual([]);
987
+ expect(
988
+ readsFor(OTHER_EPISODE_ID, (call: Array<any>): boolean => {
989
+ return !isPageRead(call);
990
+ }),
991
+ ).toBe(0);
992
+
993
+ const otherEpisode: IncidentEpisode = buildEpisode({});
994
+ otherEpisode.id = new ObjectID(OTHER_EPISODE_ID);
995
+ otherEpisode.incidentCount = 5;
996
+
997
+ await act(async () => {
998
+ resolveOther(otherEpisode);
999
+ });
1000
+
1001
+ await waitFor((): void => {
1002
+ expect(statValue("Incidents")).toBe("5");
1003
+ });
1004
+
1005
+ expect(changeStateProps.current?.["episodeId"].toString()).toBe(
1006
+ OTHER_EPISODE_ID,
1007
+ );
1008
+
1009
+ await waitFor((): void => {
1010
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
1011
+ });
1012
+
1013
+ // The details card mounted once for the new episode: one read, not two.
1014
+ expect(
1015
+ readsFor(OTHER_EPISODE_ID, (call: Array<any>): boolean => {
1016
+ return !isPageRead(call);
1017
+ }),
1018
+ ).toBe(1);
472
1019
  expectNoCrash();
473
1020
  });
1021
+
1022
+ test("an action on the previous episode that finishes after the switch cannot cancel the next one's load", async () => {
1023
+ let resolveOther: (episode: IncidentEpisode) => void = (): void => {};
1024
+ const otherPageRead: Promise<IncidentEpisode> =
1025
+ new Promise<IncidentEpisode>(
1026
+ (resolve: (episode: IncidentEpisode) => void) => {
1027
+ resolveOther = resolve;
1028
+ },
1029
+ );
1030
+
1031
+ serveEpisodes(otherPageRead);
1032
+
1033
+ const view: ReturnType<typeof render> = render(
1034
+ <IncidentEpisodeView {...pageProps} />,
1035
+ );
1036
+
1037
+ await waitFor((): void => {
1038
+ expect(screen.getByText("Episode Severity")).toBeInTheDocument();
1039
+ });
1040
+
1041
+ // Handed to the previous episode's header, which can still call it.
1042
+ const previousOnActionComplete: () => Promise<void> =
1043
+ changeStateProps.current!["onActionComplete"];
1044
+
1045
+ navigateTo(OTHER_EPISODE_ID);
1046
+ view.rerender(<IncidentEpisodeView {...pageProps} />);
1047
+
1048
+ await waitFor((): void => {
1049
+ expect(readsFor(OTHER_EPISODE_ID, isPageRead)).toBe(1);
1050
+ });
1051
+
1052
+ const timelineReadsBefore: number = listCallsFor(
1053
+ IncidentEpisodeStateTimeline,
1054
+ ).length;
1055
+ const previousPageReadsBefore: number = readsFor(EPISODE_ID, isPageRead);
1056
+
1057
+ await act(async () => {
1058
+ await previousOnActionComplete();
1059
+ });
1060
+
1061
+ // Nothing was read again for the episode the reader already left.
1062
+ expect(listCallsFor(IncidentEpisodeStateTimeline).length).toBe(
1063
+ timelineReadsBefore,
1064
+ );
1065
+ expect(readsFor(EPISODE_ID, isPageRead)).toBe(previousPageReadsBefore);
1066
+
1067
+ const otherEpisode: IncidentEpisode = buildEpisode({});
1068
+ otherEpisode.id = new ObjectID(OTHER_EPISODE_ID);
1069
+ otherEpisode.incidentCount = 5;
1070
+
1071
+ await act(async () => {
1072
+ resolveOther(otherEpisode);
1073
+ });
1074
+
1075
+ await waitFor((): void => {
1076
+ expect(statValue("Incidents")).toBe("5");
1077
+ });
1078
+ });
474
1079
  });