@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
@@ -15,6 +15,10 @@ import { JSONObject } from "../../../Types/JSON";
15
15
  import ObjectID from "../../../Types/ObjectID";
16
16
  import OneUptimeDate from "../../../Types/Date";
17
17
  import ChunkMath from "../../../Utils/Rum/ChunkMath";
18
+ import {
19
+ SessionReplayTabEndFacts,
20
+ hasSessionRecordingEnded,
21
+ } from "../../../Utils/Rum/SessionReplayRecordingEnded";
18
22
  import {
19
23
  MAX_SESSION_REPLAY_CHUNKS_PER_READ,
20
24
  MAX_SESSION_REPLAY_READ_BYTES,
@@ -35,6 +39,7 @@ import {
35
39
  } from "../../../Types/Rum/SessionReplayApi";
36
40
  import BadDataException from "../../../Types/Exception/BadDataException";
37
41
  import CaptureSpan from "../Telemetry/CaptureSpan";
42
+ import ServiceType from "../../../Types/Telemetry/ServiceType";
38
43
 
39
44
  /*
40
45
  * Bespoke ClickHouse reads for session-replay playback.
@@ -95,6 +100,15 @@ const READ_QUERY_SETTINGS: string = getQuerySettings({
95
100
  export const DEFAULT_SESSION_REPLAY_LIST_LIMIT: number = 50;
96
101
  export const MAX_SESSION_REPLAY_LIST_LIMIT: number = 200;
97
102
 
103
+ /*
104
+ * Audit tables resolve the opaque session ids on one page in a single
105
+ * ClickHouse read. Keep both the number of bound IN values and each value's
106
+ * size bounded; session ids are browser-minted 32-character hex strings, but
107
+ * older recorders and hand-written API callers may have stored another shape.
108
+ */
109
+ export const MAX_SESSION_REPLAY_SUMMARIES_BATCH_SIZE: number = 200;
110
+ export const MAX_SESSION_REPLAY_SESSION_ID_LENGTH: number = 128;
111
+
98
112
  /*
99
113
  * Page sizes for the per-user rollup (listUsers). Same figures as the
100
114
  * list, but the cost model differs: every page of the rollup
@@ -219,6 +233,11 @@ export interface SessionReplayListRequest {
219
233
  * as well.
220
234
  */
221
235
  includeIdentifiedUserLabel: boolean;
236
+ /*
237
+ * Server "now" in unix ms for the hasRecordingEnded grace. Absent means
238
+ * Date.now(); a test seam, like getApplicationActivitySummary's.
239
+ */
240
+ nowUnixMs?: number | undefined;
222
241
  }
223
242
 
224
243
  export interface SessionReplayListItem {
@@ -278,6 +297,22 @@ export interface SessionReplayListItem {
278
297
  tags: Record<string, string>;
279
298
  startTimeUnixMs: number;
280
299
  endTimeUnixMs: number;
300
+ /*
301
+ * True when the session is not finalized yet but every tab of it has
302
+ * ended (sent its final chunk, or stored its last permitted chunk
303
+ * index) and nothing has been stored for any of them for
304
+ * SESSION_REPLAY_ENDED_FINALIZE_GRACE_MS, judged from the chunk rows by
305
+ * the shared rule in Common/Utils/Rum/SessionReplayRecordingEnded.ts.
306
+ * "Not finalized" only says the finalizer has not run; this says the
307
+ * recording itself is over, which is what the Dashboard needs to stop
308
+ * calling a closed tab "Recording now". The grace is the finalizer's
309
+ * own, measured on the same server clock, so a multi-page app's next
310
+ * page (a new tab id whose first chunk is still on its way) does not
311
+ * flip a live session to "ended" for a poll. Always false for a
312
+ * finalized session, and false when the chunk rows could not be read -
313
+ * the list never fails for it.
314
+ */
315
+ hasRecordingEnded: boolean;
281
316
  }
282
317
 
283
318
  export interface SessionReplayListResult {
@@ -285,6 +320,29 @@ export interface SessionReplayListResult {
285
320
  nextCursor: SessionReplayListCursor | null;
286
321
  }
287
322
 
323
+ export interface SessionReplaySummariesRequest {
324
+ projectId: ObjectID;
325
+ rumApplicationId: ObjectID;
326
+ sessionIds: Array<string>;
327
+ }
328
+
329
+ /*
330
+ * Deliberately excludes every identity field. An audit list needs enough
331
+ * context to distinguish recordings, not the person or browser identifier
332
+ * attached to them.
333
+ */
334
+ export interface SessionReplaySummary {
335
+ sessionId: string;
336
+ startTime: Date;
337
+ startTimeUnixMs: number;
338
+ durationMs: number;
339
+ entryUrl: string;
340
+ browserName: string;
341
+ browserVersion: string;
342
+ osName: string;
343
+ deviceType: string;
344
+ }
345
+
288
346
  /* Routes projected onto a list row; the table shows three and says "(N pages)". */
289
347
  export const MAX_LIST_ROUTES: number = 5;
290
348
 
@@ -425,6 +483,19 @@ export interface SessionReplaySessionHeader {
425
483
  */
426
484
  identifiedUserKey: string;
427
485
  visitorId: string;
486
+ /*
487
+ * Same meaning as SessionReplayListItem.hasRecordingEnded.
488
+ *
489
+ * JUDGED ONLY BY getManifest; getSessionHeader always answers false.
490
+ * The header read is also the authorization lookup behind every chunk
491
+ * and heartbeat request, and its result is cached for 30s there, so a
492
+ * chunk-table read on it would add a query to every uncached seek and
493
+ * cache an answer that goes stale the moment a tab closes. The manifest
494
+ * is the one read that hands the header to the Dashboard, it always
495
+ * resolves the header fresh, and it is pinned to the application the
496
+ * caller was authorized against - which the chunk read needs.
497
+ */
498
+ hasRecordingEnded: boolean;
428
499
  /*
429
500
  * Never populated by getSessionHeader. The manifest handler fills them
430
501
  * from getSessionIdentity ONLY after canReadIdentifiedUserLabel passes,
@@ -694,6 +765,36 @@ const HEADER_AGGREGATES: Array<AggregatedColumn> = [
694
765
  { alias: "aggTags", expression: argMaxColumn("tags") },
695
766
  ];
696
767
 
768
+ /*
769
+ * The narrow projection used by the audit-table summary lookup. Derive it
770
+ * from the list's aggregates so live duration and replacement-row handling
771
+ * cannot drift between the two reads.
772
+ */
773
+ const SESSION_SUMMARY_AGGREGATE_ALIASES: ReadonlyArray<string> = [
774
+ "aggStartTime",
775
+ "aggDurationMs",
776
+ "aggEntryUrl",
777
+ "aggBrowserName",
778
+ "aggBrowserVersion",
779
+ "aggOsName",
780
+ "aggDeviceType",
781
+ ];
782
+
783
+ const SESSION_SUMMARY_AGGREGATES: Array<AggregatedColumn> =
784
+ SESSION_SUMMARY_AGGREGATE_ALIASES.map((alias: string): AggregatedColumn => {
785
+ const column: AggregatedColumn | undefined = HEADER_AGGREGATES.find(
786
+ (candidate: AggregatedColumn): boolean => {
787
+ return candidate.alias === alias;
788
+ },
789
+ );
790
+
791
+ if (!column) {
792
+ throw new Error(`HEADER_AGGREGATES has no column aliased ${alias}`);
793
+ }
794
+
795
+ return column;
796
+ });
797
+
697
798
  /* Only the manifest needs these; the list never renders them. */
698
799
  const HEADER_DETAIL_AGGREGATES: Array<AggregatedColumn> = [
699
800
  { alias: "aggMaskingMode", expression: argMaxColumn("maskingMode") },
@@ -1015,6 +1116,82 @@ function readDate(row: JSONObject, key: string): Date {
1015
1116
  return new Date(readNumber(row, key));
1016
1117
  }
1017
1118
 
1119
+ /*
1120
+ * A number that has to be MEASURED, not defaulted. readNumber answers 0 for
1121
+ * a missing or garbled value, which is the right degradation for a counter
1122
+ * on a table row but the wrong one for a clock the "has this tab ended?"
1123
+ * rule compares: a missing last-chunk start read as 0 would sit before
1124
+ * every final chunk and call a live tab ended. NaN instead, which the
1125
+ * shared rule refuses outright.
1126
+ */
1127
+ function readMeasuredNumber(row: JSONObject, key: string): number {
1128
+ const value: unknown = row[key];
1129
+
1130
+ if (typeof value === "number") {
1131
+ return Number.isFinite(value) ? value : Number.NaN;
1132
+ }
1133
+
1134
+ /* Number("") and Number(" ") are 0, which is exactly the default above. */
1135
+ if (typeof value === "string" && value.trim().length > 0) {
1136
+ const parsed: number = Number(value);
1137
+ return Number.isFinite(parsed) ? parsed : Number.NaN;
1138
+ }
1139
+
1140
+ return Number.NaN;
1141
+ }
1142
+
1143
+ /*
1144
+ * The per-tab facts the shared rule in
1145
+ * Common/Utils/Rum/SessionReplayRecordingEnded.ts is judged on, written
1146
+ * exactly as that file documents them. The clocks and the version are
1147
+ * wrapped in toFloat64 for the same reason as every wide column above:
1148
+ * ClickHouse's JSON format quotes Int64 and UInt64 as strings. The chunk
1149
+ * index is an Int32 and would come back as a number anyway; it is wrapped
1150
+ * too so every fact in the group parses the same way. The aliases do not
1151
+ * collide with a real column (see the NOTE on aliases at the top of this
1152
+ * file).
1153
+ *
1154
+ * Two of the five are not about the tab's own clock:
1155
+ *
1156
+ * - tabMaxChunkIndex lets the rule call a tab that stored its last
1157
+ * permitted chunk index ended. The ingest gate refuses everything past
1158
+ * it, the recorder's own truncation seal included, so that tab never
1159
+ * gets a final chunk and would otherwise read as "recording" until the
1160
+ * idle finalizer.
1161
+ * - tabLastChunkStoredAtUnixMs is max(version): the SERVER unix ms at
1162
+ * which the tab's newest chunk row was written. The grace is measured
1163
+ * on it, never on the device clock the chunk times come from.
1164
+ */
1165
+ const TAB_HAS_FINAL_CHUNK_ALIAS: string = "tabHasFinalChunk";
1166
+ const TAB_FINAL_CHUNK_END_ALIAS: string = "tabFinalChunkEndUnixMs";
1167
+ const TAB_LAST_CHUNK_START_ALIAS: string = "tabLastChunkStartUnixMs";
1168
+ const TAB_MAX_CHUNK_INDEX_ALIAS: string = "tabMaxChunkIndex";
1169
+ const TAB_LAST_CHUNK_STORED_AT_ALIAS: string = "tabLastChunkStoredAtUnixMs";
1170
+
1171
+ const TAB_END_FACT_AGGREGATES: Array<AggregatedColumn> = [
1172
+ {
1173
+ alias: TAB_HAS_FINAL_CHUNK_ALIAS,
1174
+ expression: "max(toUInt8(isFinal))",
1175
+ },
1176
+ {
1177
+ alias: TAB_FINAL_CHUNK_END_ALIAS,
1178
+ expression:
1179
+ "toFloat64(toUnixTimestamp64Milli(maxIf(chunkEndTime, isFinal)))",
1180
+ },
1181
+ {
1182
+ alias: TAB_LAST_CHUNK_START_ALIAS,
1183
+ expression: "toFloat64(toUnixTimestamp64Milli(max(chunkStartTime)))",
1184
+ },
1185
+ {
1186
+ alias: TAB_MAX_CHUNK_INDEX_ALIAS,
1187
+ expression: "toFloat64(max(chunkIndex))",
1188
+ },
1189
+ {
1190
+ alias: TAB_LAST_CHUNK_STORED_AT_ALIAS,
1191
+ expression: "toFloat64(max(version))",
1192
+ },
1193
+ ];
1194
+
1018
1195
  /*
1019
1196
  * The capability list chunk 0 declared, filtered to the vocabulary this
1020
1197
  * build knows so a stored typo never reaches the player as a capability.
@@ -1088,6 +1265,120 @@ export default class SessionReplayReadService {
1088
1265
  activitySummaryCache.clear();
1089
1266
  }
1090
1267
 
1268
+ /*
1269
+ * Resolve the opaque ids stored on audit rows into compact session facts.
1270
+ * This is intentionally one bespoke query instead of N getSessionHeader
1271
+ * calls (or AnalyticsModelAPI, which RumSession does not expose). The
1272
+ * project and application are both pinned before the caller-controlled IN
1273
+ * list, and argMax collapses provisional/finalized ReplacingMergeTree rows.
1274
+ */
1275
+ @CaptureSpan()
1276
+ public static async getSessionSummaries(
1277
+ request: SessionReplaySummariesRequest,
1278
+ ): Promise<Array<SessionReplaySummary>> {
1279
+ if (request.sessionIds.length === 0) {
1280
+ throw new BadDataException("sessionIds must contain at least one id");
1281
+ }
1282
+
1283
+ if (request.sessionIds.length > MAX_SESSION_REPLAY_SUMMARIES_BATCH_SIZE) {
1284
+ throw new BadDataException(
1285
+ `sessionIds must contain at most ${MAX_SESSION_REPLAY_SUMMARIES_BATCH_SIZE} ids`,
1286
+ );
1287
+ }
1288
+
1289
+ for (const sessionId of request.sessionIds) {
1290
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
1291
+ throw new BadDataException(
1292
+ "Every sessionId must be a non-empty string",
1293
+ );
1294
+ }
1295
+
1296
+ if (sessionId.length > MAX_SESSION_REPLAY_SESSION_ID_LENGTH) {
1297
+ throw new BadDataException(
1298
+ `Every sessionId must be at most ${MAX_SESSION_REPLAY_SESSION_ID_LENGTH} characters.`,
1299
+ );
1300
+ }
1301
+ }
1302
+
1303
+ /* Preserve first occurrence order while binding every id only once. */
1304
+ const sessionIds: Array<string> = Array.from(
1305
+ new Set<string>(request.sessionIds),
1306
+ );
1307
+
1308
+ const statement: Statement = SQL`
1309
+ SELECT
1310
+ sessionId,
1311
+ `;
1312
+
1313
+ statement.append(` ${toSelectList(SESSION_SUMMARY_AGGREGATES)}`);
1314
+
1315
+ statement.append(SQL`
1316
+ FROM ${AnalyticsTableName.RumSession}
1317
+ WHERE projectId = ${{
1318
+ type: TableColumnType.ObjectID,
1319
+ value: request.projectId,
1320
+ }}
1321
+ AND rumApplicationId = ${{
1322
+ type: TableColumnType.ObjectID,
1323
+ value: request.rumApplicationId,
1324
+ }}
1325
+ AND sessionId IN (${{
1326
+ type: TableColumnType.Text,
1327
+ value: new Includes(sessionIds),
1328
+ }})
1329
+ `);
1330
+
1331
+ statement.append(RETENTION_FILTER);
1332
+ statement.append(
1333
+ " GROUP BY projectId, rumApplicationId, sessionId ORDER BY aggStartTime DESC",
1334
+ );
1335
+ statement.append(READ_QUERY_SETTINGS);
1336
+
1337
+ const dbResult: Results = await RumSessionService.executeQuery(statement);
1338
+ const response: DbJSONResponse = await dbResult.json<{
1339
+ data?: Array<JSONObject>;
1340
+ }>();
1341
+
1342
+ const requestedIds: Set<string> = new Set<string>(sessionIds);
1343
+ const summariesById: Map<string, SessionReplaySummary> = new Map<
1344
+ string,
1345
+ SessionReplaySummary
1346
+ >();
1347
+
1348
+ for (const row of response.data || []) {
1349
+ const sessionId: string = readString(row, "sessionId");
1350
+
1351
+ /* A malformed/unexpected driver row can never add data to the reply. */
1352
+ if (!requestedIds.has(sessionId) || summariesById.has(sessionId)) {
1353
+ continue;
1354
+ }
1355
+
1356
+ const startTime: Date = readDate(row, "aggStartTime");
1357
+
1358
+ summariesById.set(sessionId, {
1359
+ sessionId: sessionId,
1360
+ startTime: startTime,
1361
+ startTimeUnixMs: startTime.getTime(),
1362
+ durationMs: readNumber(row, "aggDurationMs"),
1363
+ entryUrl: readString(row, "aggEntryUrl"),
1364
+ browserName: readString(row, "aggBrowserName"),
1365
+ browserVersion: readString(row, "aggBrowserVersion"),
1366
+ osName: readString(row, "aggOsName"),
1367
+ deviceType: readString(row, "aggDeviceType"),
1368
+ });
1369
+ }
1370
+
1371
+ /* Stable request order makes consumers deterministic; missing ids omit. */
1372
+ return sessionIds.flatMap(
1373
+ (sessionId: string): Array<SessionReplaySummary> => {
1374
+ const summary: SessionReplaySummary | undefined =
1375
+ summariesById.get(sessionId);
1376
+
1377
+ return summary ? [summary] : [];
1378
+ },
1379
+ );
1380
+ }
1381
+
1091
1382
  /*
1092
1383
  * Session list.
1093
1384
  *
@@ -1304,6 +1595,8 @@ export default class SessionReplayReadService {
1304
1595
  tags: readStringMap(row, "aggTags"),
1305
1596
  startTimeUnixMs: startTime.getTime(),
1306
1597
  endTimeUnixMs: endTime.getTime(),
1598
+ /* Judged below, only for the page's unfinalized sessions. */
1599
+ hasRecordingEnded: false,
1307
1600
  };
1308
1601
 
1309
1602
  if (request.includeIdentifiedUserLabel) {
@@ -1318,6 +1611,44 @@ export default class SessionReplayReadService {
1318
1611
  },
1319
1612
  );
1320
1613
 
1614
+ /*
1615
+ * "Recording now" or "recording ended"? The header cannot say: a
1616
+ * provisional header stays unfinalized for the whole idle window
1617
+ * after its last tab closed, which is how a closed tab kept its
1618
+ * "Recording now" badge for 10-15 minutes. The chunk rows can, so the
1619
+ * page's unfinalized sessions - and only those - get ONE follow-up
1620
+ * read over the chunk table. A page of finalized sessions (the
1621
+ * overwhelming majority of any list older than a few minutes) runs
1622
+ * no second query at all.
1623
+ *
1624
+ * It is a separate statement rather than a JOIN on the list query
1625
+ * because the list's own read is a range over the HEADER table's sort
1626
+ * key, and joining the chunk table there would aggregate chunk rows
1627
+ * for every session in the window before the LIMIT picked a page.
1628
+ */
1629
+ const unfinalizedSessionIds: Array<string> = sessions
1630
+ .filter((session: SessionReplayListItem): boolean => {
1631
+ return !session.isFinalized;
1632
+ })
1633
+ .map((session: SessionReplayListItem): string => {
1634
+ return session.sessionId;
1635
+ });
1636
+
1637
+ if (unfinalizedSessionIds.length > 0) {
1638
+ const endedSessionIds: Set<string> =
1639
+ await SessionReplayReadService.readRecordingEndedSessionIds({
1640
+ projectId: request.projectId,
1641
+ rumApplicationId: request.rumApplicationId,
1642
+ sessionIds: unfinalizedSessionIds,
1643
+ nowUnixMs: request.nowUnixMs ?? Date.now(),
1644
+ });
1645
+
1646
+ for (const session of sessions) {
1647
+ session.hasRecordingEnded =
1648
+ !session.isFinalized && endedSessionIds.has(session.sessionId);
1649
+ }
1650
+ }
1651
+
1321
1652
  const lastSession: SessionReplayListItem | undefined =
1322
1653
  sessions[sessions.length - 1];
1323
1654
 
@@ -1719,6 +2050,12 @@ export default class SessionReplayReadService {
1719
2050
  recorderCapabilities: readRecorderCapabilities(row),
1720
2051
  identifiedUserKey: readString(row, "aggIdentifiedUserKey"),
1721
2052
  visitorId: readString(row, "aggVisitorId"),
2053
+ /*
2054
+ * Not judged here: see SessionReplaySessionHeader.hasRecordingEnded.
2055
+ * getManifest, the read that returns this header to the Dashboard,
2056
+ * replaces it for an unfinalized session.
2057
+ */
2058
+ hasRecordingEnded: false,
1722
2059
  };
1723
2060
  }
1724
2061
 
@@ -1870,6 +2207,11 @@ export default class SessionReplayReadService {
1870
2207
  */
1871
2208
  rumApplicationId: ObjectID;
1872
2209
  sessionId: string;
2210
+ /*
2211
+ * Server "now" in unix ms for the hasRecordingEnded grace. Absent
2212
+ * means Date.now(); a test seam, like listSessions'.
2213
+ */
2214
+ nowUnixMs?: number | undefined;
1873
2215
  }): Promise<SessionReplayManifest> {
1874
2216
  /*
1875
2217
  * LIMIT 1 BY (tabId, chunkIndex) after ORDER BY ... version DESC
@@ -1929,8 +2271,38 @@ export default class SessionReplayReadService {
1929
2271
 
1930
2272
  statement.append(READ_QUERY_SETTINGS);
1931
2273
 
1932
- const dbResult: Results =
1933
- await RumSessionChunkService.executeQuery(statement);
2274
+ /*
2275
+ * Whether this unfinalized session's recording is already over, read
2276
+ * alongside the manifest rather than after it: the player re-fetches
2277
+ * the manifest every 30s while a session is not finalized, and this
2278
+ * is what lets it stop calling a closed session "Live" once the
2279
+ * ended grace has passed, instead of waiting for a finalizer run to
2280
+ * seal the header. The same helper, the same grace and the same
2281
+ * clock the list uses, so the list badge and the player pill can
2282
+ * never disagree about one session.
2283
+ *
2284
+ * Its own grouped read rather than a fold over the rows below: those
2285
+ * rows are capped at MAX_MANIFEST_ROWS in (tabId, chunkIndex) order, so
2286
+ * a truncated manifest would lose a tab's newest chunks - exactly the
2287
+ * rows that say whether it kept recording after its final chunk. A
2288
+ * finalized header is authoritative and runs no second read. The
2289
+ * helper never rejects (a failure answers "not ended"), so it cannot
2290
+ * fail the manifest. The manifest statement is issued first.
2291
+ */
2292
+ const manifestResultPromise: Promise<Results> =
2293
+ RumSessionChunkService.executeQuery(statement);
2294
+
2295
+ const endedSessionIdsPromise: Promise<Set<string>> = data.header.isFinalized
2296
+ ? Promise.resolve(new Set<string>())
2297
+ : SessionReplayReadService.readRecordingEndedSessionIds({
2298
+ projectId: data.projectId,
2299
+ rumApplicationId: data.rumApplicationId,
2300
+ sessionIds: [data.sessionId],
2301
+ nowUnixMs: data.nowUnixMs ?? Date.now(),
2302
+ });
2303
+
2304
+ const [dbResult, endedSessionIds]: [Results, Set<string>] =
2305
+ await Promise.all([manifestResultPromise, endedSessionIdsPromise]);
1934
2306
  const response: DbJSONResponse = await dbResult.json<{
1935
2307
  data?: Array<JSONObject>;
1936
2308
  }>();
@@ -2040,6 +2412,7 @@ export default class SessionReplayReadService {
2040
2412
  liveDurationMs: liveDurationMs,
2041
2413
  liveEventCount: liveEventCount,
2042
2414
  liveMaxChunkIndex: liveMaxChunkIndex,
2415
+ hasRecordingEnded: endedSessionIds.has(data.sessionId),
2043
2416
  }),
2044
2417
  tabs: tabs,
2045
2418
  isChunkIndexTruncated: rows.length >= MAX_MANIFEST_ROWS,
@@ -2051,6 +2424,10 @@ export default class SessionReplayReadService {
2051
2424
  * 0 and eventCount 0 while its chunk rows say otherwise; the manifest
2052
2425
  * has just read every chunk row, so it reports what the rows prove. A
2053
2426
  * finalized header is authoritative and returned untouched.
2427
+ *
2428
+ * hasRecordingEnded is set on every unfinalized header, including one
2429
+ * whose chunk rows have not landed yet: getSessionHeader never judges
2430
+ * it, so the manifest is the only place the field is ever true.
2054
2431
  */
2055
2432
  private static reconcileLiveHeader(data: {
2056
2433
  header: SessionReplaySessionHeader;
@@ -2058,11 +2435,19 @@ export default class SessionReplayReadService {
2058
2435
  liveDurationMs: number;
2059
2436
  liveEventCount: number;
2060
2437
  liveMaxChunkIndex: number;
2438
+ hasRecordingEnded: boolean;
2061
2439
  }): SessionReplaySessionHeader {
2062
- if (data.header.isFinalized || data.chunkRowCount === 0) {
2440
+ if (data.header.isFinalized) {
2063
2441
  return data.header;
2064
2442
  }
2065
2443
 
2444
+ if (data.chunkRowCount === 0) {
2445
+ return {
2446
+ ...data.header,
2447
+ hasRecordingEnded: data.hasRecordingEnded,
2448
+ };
2449
+ }
2450
+
2066
2451
  const durationMs: number = Math.max(
2067
2452
  data.header.durationMs,
2068
2453
  data.liveDurationMs,
@@ -2083,9 +2468,174 @@ export default class SessionReplayReadService {
2083
2468
  data.header.maxChunkIndex,
2084
2469
  data.liveMaxChunkIndex,
2085
2470
  ),
2471
+ hasRecordingEnded: data.hasRecordingEnded,
2086
2472
  };
2087
2473
  }
2088
2474
 
2475
+ /*
2476
+ * Which of these unfinalized sessions have stopped recording, judged by
2477
+ * the shared rule (Common/Utils/Rum/SessionReplayRecordingEnded.ts) over
2478
+ * their chunk rows. The one helper behind both the list badge and the
2479
+ * player's Live pill.
2480
+ *
2481
+ * One statement for any number of sessions:
2482
+ *
2483
+ * WHERE projectId = ? AND sessionId IN (...) AND rumApplicationId = ?
2484
+ * GROUP BY sessionId, tabId
2485
+ *
2486
+ * A KEY-RANGE READ. RumSessionChunk is sorted (and primary-keyed) by
2487
+ * (projectId, sessionId, tabId, chunkIndex), so projectId plus a
2488
+ * sessionId IN list is a prefix of the primary index: ClickHouse reads
2489
+ * only the granules of the named sessions - a handful of narrow columns
2490
+ * over at most a few hundred rows each - and never the payload column.
2491
+ * The GROUP BY follows the same key order. rumApplicationId is a plain
2492
+ * column filtered over those granules, and it is not optional: the
2493
+ * table's replace key has no application in it, so two applications
2494
+ * that share a browser-minted sessionId share one key space, and
2495
+ * without the pin one application's closed tab could end another's
2496
+ * recording.
2497
+ *
2498
+ * Grouped per tab, never per session, because the rule is per tab: a
2499
+ * session shared by several tabs (or several page loads of a multi-page
2500
+ * app) is over only when every one of them is. The five aggregates are
2501
+ * maxima, so a redelivered chunk's duplicate row - or a pinned copy -
2502
+ * cannot make a live tab look ended and no LIMIT 1 BY is needed. Such a
2503
+ * row can carry a NEWER version than the original, which only restarts
2504
+ * the grace once; it never shortens it. The retention filter is kept
2505
+ * like on every other replay read.
2506
+ *
2507
+ * The grace. Every tab having ended is not enough on its own: in a
2508
+ * multi-page app, page A's final chunk lands while page B - a new tab id
2509
+ * under the same session - has not stored its first chunk yet, and for
2510
+ * that moment the only tab the rows know about has ended. So the rule
2511
+ * also wants the newest stored chunk of the session to be at least
2512
+ * SESSION_REPLAY_ENDED_FINALIZE_GRACE_MS older than nowUnixMs, the same
2513
+ * grace the finalizer waits on, measured on the same server clock (the
2514
+ * rows' version against this process's Date.now()). The list, the player
2515
+ * and the finalizer therefore call a session ended at the same moment,
2516
+ * give or take the finalizer's one-minute schedule, and none of them
2517
+ * depends on Redis for it.
2518
+ *
2519
+ * Best-effort. A failure logs a warning and answers "nothing has ended",
2520
+ * which is what the Dashboard showed before this existed; neither the
2521
+ * list nor the manifest may fail because of it. A session with no chunk
2522
+ * rows has no tabs, and no tabs is "not known", never "ended".
2523
+ */
2524
+ private static async readRecordingEndedSessionIds(data: {
2525
+ projectId: ObjectID;
2526
+ rumApplicationId: ObjectID;
2527
+ sessionIds: Array<string>;
2528
+ /* Server unix ms the grace is measured against. */
2529
+ nowUnixMs: number;
2530
+ }): Promise<Set<string>> {
2531
+ const endedSessionIds: Set<string> = new Set<string>();
2532
+
2533
+ const sessionIds: Array<string> = Array.from(
2534
+ new Set<string>(
2535
+ data.sessionIds.filter((sessionId: string): boolean => {
2536
+ return sessionId.length > 0;
2537
+ }),
2538
+ ),
2539
+ );
2540
+
2541
+ if (sessionIds.length === 0) {
2542
+ return endedSessionIds;
2543
+ }
2544
+
2545
+ const statement: Statement = SQL`
2546
+ SELECT
2547
+ sessionId,
2548
+ tabId,
2549
+ `;
2550
+
2551
+ statement.append(` ${toSelectList(TAB_END_FACT_AGGREGATES)}`);
2552
+
2553
+ statement.append(SQL`
2554
+ FROM ${AnalyticsTableName.RumSessionChunk}
2555
+ WHERE projectId = ${{
2556
+ type: TableColumnType.ObjectID,
2557
+ value: data.projectId,
2558
+ }}
2559
+ AND sessionId IN (${{
2560
+ type: TableColumnType.Text,
2561
+ value: new Includes(sessionIds),
2562
+ }})
2563
+ AND rumApplicationId = ${{
2564
+ type: TableColumnType.ObjectID,
2565
+ value: data.rumApplicationId,
2566
+ }}
2567
+ `);
2568
+
2569
+ statement.append(RETENTION_FILTER);
2570
+ statement.append(" GROUP BY sessionId, tabId");
2571
+ statement.append(READ_QUERY_SETTINGS);
2572
+
2573
+ let rows: Array<JSONObject>;
2574
+
2575
+ try {
2576
+ const dbResult: Results =
2577
+ await RumSessionChunkService.executeQuery(statement);
2578
+ const response: DbJSONResponse = await dbResult.json<{
2579
+ data?: Array<JSONObject>;
2580
+ }>();
2581
+
2582
+ rows = response.data || [];
2583
+ } catch (err: unknown) {
2584
+ logger.warn(
2585
+ "SessionReplayReadService: could not read chunk rows to tell whether unfinalized sessions have ended; reporting them as still recording",
2586
+ );
2587
+ logger.warn(err);
2588
+
2589
+ return endedSessionIds;
2590
+ }
2591
+
2592
+ const requested: Set<string> = new Set<string>(sessionIds);
2593
+ const tabsBySessionId: Map<
2594
+ string,
2595
+ Array<SessionReplayTabEndFacts>
2596
+ > = new Map<string, Array<SessionReplayTabEndFacts>>();
2597
+
2598
+ for (const row of rows) {
2599
+ const sessionId: string = readString(row, "sessionId");
2600
+
2601
+ /* Defensive: a row for a session nobody asked about decides nothing. */
2602
+ if (!requested.has(sessionId)) {
2603
+ continue;
2604
+ }
2605
+
2606
+ const tab: SessionReplayTabEndFacts = {
2607
+ hasFinalChunk: readBoolean(row, TAB_HAS_FINAL_CHUNK_ALIAS),
2608
+ finalChunkEndUnixMs: readMeasuredNumber(row, TAB_FINAL_CHUNK_END_ALIAS),
2609
+ lastChunkStartUnixMs: readMeasuredNumber(
2610
+ row,
2611
+ TAB_LAST_CHUNK_START_ALIAS,
2612
+ ),
2613
+ maxChunkIndex: readMeasuredNumber(row, TAB_MAX_CHUNK_INDEX_ALIAS),
2614
+ lastChunkStoredAtUnixMs: readMeasuredNumber(
2615
+ row,
2616
+ TAB_LAST_CHUNK_STORED_AT_ALIAS,
2617
+ ),
2618
+ };
2619
+
2620
+ const existing: Array<SessionReplayTabEndFacts> | undefined =
2621
+ tabsBySessionId.get(sessionId);
2622
+
2623
+ if (existing) {
2624
+ existing.push(tab);
2625
+ } else {
2626
+ tabsBySessionId.set(sessionId, [tab]);
2627
+ }
2628
+ }
2629
+
2630
+ for (const [sessionId, tabs] of tabsBySessionId) {
2631
+ if (hasSessionRecordingEnded(tabs, data.nowUnixMs)) {
2632
+ endedSessionIds.add(sessionId);
2633
+ }
2634
+ }
2635
+
2636
+ return endedSessionIds;
2637
+ }
2638
+
2089
2639
  /*
2090
2640
  * The payload read. The only query in the system that names the
2091
2641
  * `payload` column.
@@ -2263,14 +2813,28 @@ export default class SessionReplayReadService {
2263
2813
  * fingerprint present only on a superseded row does not produce a false
2264
2814
  * positive.
2265
2815
  *
2266
- * Always windowed. RumSession is partitioned by day, so without a
2267
- * window this scanned every partition the project ever wrote on every
2268
- * exception page load.
2816
+ * Typed RUM and fully unscoped legacy lookups are windowed in WHERE for
2817
+ * partition pruning. Scoped non-RUM and ID-only type-unknown lookups apply
2818
+ * the window in QUALIFY: their instance-proven session ids must first be
2819
+ * checked across every retained application in the project so a duplicate
2820
+ * id cannot be hidden outside the caller's time or authorization scope.
2269
2821
  */
2270
2822
  @CaptureSpan()
2271
2823
  public static async getSessionsForException(data: {
2272
2824
  projectId: ObjectID;
2273
2825
  exceptionFingerprint: string;
2826
+ /*
2827
+ * Exception fingerprints are unique per primary entity, not per project.
2828
+ * Keep this optional for older callers, but exception pages should always
2829
+ * provide it so neither the live side index nor finalized headers can mix
2830
+ * identically fingerprinted groups from different services.
2831
+ */
2832
+ primaryEntityId?: ObjectID | undefined;
2833
+ /*
2834
+ * Identifies the owning entity table. ID-only is accepted as a rolling
2835
+ * compatibility mode and uses the conservative scoped-unknown branch.
2836
+ */
2837
+ primaryEntityType?: ServiceType | undefined;
2274
2838
  /*
2275
2839
  * null means "no label restriction". An EMPTY array means the caller
2276
2840
  * can reach no applications at all and must get no rows - the two are
@@ -2284,6 +2848,20 @@ export default class SessionReplayReadService {
2284
2848
  sessionId?: string | undefined;
2285
2849
  limit: number;
2286
2850
  }): Promise<Array<SessionReplayExceptionSession>> {
2851
+ const hasPrimaryEntityId: boolean = data.primaryEntityId !== undefined;
2852
+ const hasPrimaryEntityType: boolean = data.primaryEntityType !== undefined;
2853
+
2854
+ if (!hasPrimaryEntityId && hasPrimaryEntityType) {
2855
+ throw new BadDataException("primaryEntityId is required with its type");
2856
+ }
2857
+
2858
+ if (
2859
+ data.primaryEntityType !== undefined &&
2860
+ !Object.values(ServiceType).includes(data.primaryEntityType)
2861
+ ) {
2862
+ throw new BadDataException("primaryEntityType is not valid");
2863
+ }
2864
+
2287
2865
  if (
2288
2866
  data.accessibleRumApplicationIds &&
2289
2867
  data.accessibleRumApplicationIds.length === 0
@@ -2308,11 +2886,28 @@ export default class SessionReplayReadService {
2308
2886
  await SessionReplayReadService.getSessionIdsForExceptionInstances({
2309
2887
  projectId: data.projectId,
2310
2888
  exceptionFingerprint: data.exceptionFingerprint,
2889
+ primaryEntityId: data.primaryEntityId,
2890
+ primaryEntityType: data.primaryEntityType,
2311
2891
  startTime: startTime,
2312
2892
  endTime: endTime,
2313
2893
  sessionId: data.sessionId,
2314
2894
  });
2315
2895
 
2896
+ const isScopedRumException: boolean =
2897
+ data.primaryEntityType === ServiceType.RealUserMonitor;
2898
+ const isScopedNonRumOrUnknownException: boolean =
2899
+ hasPrimaryEntityId && !isScopedRumException;
2900
+
2901
+ /*
2902
+ * A finalized replay header has only a flat fingerprint list. For a
2903
+ * non-RUM or legacy type-unknown entity that list cannot prove ownership,
2904
+ * so only the scoped instance side index may admit a session. Failure or
2905
+ * an empty lookup must therefore fail closed.
2906
+ */
2907
+ if (isScopedNonRumOrUnknownException && instanceSessionIds.length === 0) {
2908
+ return [];
2909
+ }
2910
+
2316
2911
  const selectList: string = toSelectList([
2317
2912
  { alias: "aggStartTime", expression: argMaxDateTime("startTime") },
2318
2913
  { alias: "aggEndTime", expression: argMaxDateTime("endTime") },
@@ -2362,6 +2957,13 @@ export default class SessionReplayReadService {
2362
2957
 
2363
2958
  statement.append(` ${selectList}`);
2364
2959
 
2960
+ if (isScopedNonRumOrUnknownException) {
2961
+ /* One grouped row per application makes this a cross-app collision count. */
2962
+ statement.append(
2963
+ ", count() OVER (PARTITION BY sessionId) AS matchedApplicationCount",
2964
+ );
2965
+ }
2966
+
2365
2967
  statement.append(SQL`
2366
2968
  FROM ${AnalyticsTableName.RumSession}
2367
2969
  WHERE projectId = ${{
@@ -2370,7 +2972,12 @@ export default class SessionReplayReadService {
2370
2972
  }}
2371
2973
  `);
2372
2974
 
2373
- if (data.accessibleRumApplicationIds) {
2975
+ /*
2976
+ * For non-RUM and legacy type-unknown exceptions, authorization belongs
2977
+ * in QUALIFY after the window has counted inaccessible applications too.
2978
+ * Applying it here would make an ambiguous id appear unique.
2979
+ */
2980
+ if (!isScopedNonRumOrUnknownException && data.accessibleRumApplicationIds) {
2374
2981
  statement.append(
2375
2982
  SQL` AND rumApplicationId IN (${{
2376
2983
  type: TableColumnType.ObjectID,
@@ -2379,15 +2986,17 @@ export default class SessionReplayReadService {
2379
2986
  );
2380
2987
  }
2381
2988
 
2382
- statement.append(
2383
- SQL` AND startTime >= ${{
2384
- type: TableColumnType.DateTime64,
2385
- value: startTime,
2386
- }} AND startTime <= ${{
2387
- type: TableColumnType.DateTime64,
2388
- value: endTime,
2389
- }}`,
2390
- );
2989
+ if (!isScopedNonRumOrUnknownException) {
2990
+ statement.append(
2991
+ SQL` AND startTime >= ${{
2992
+ type: TableColumnType.DateTime64,
2993
+ value: startTime,
2994
+ }} AND startTime <= ${{
2995
+ type: TableColumnType.DateTime64,
2996
+ value: endTime,
2997
+ }}`,
2998
+ );
2999
+ }
2391
3000
 
2392
3001
  statement.append(RETENTION_FILTER);
2393
3002
 
@@ -2400,33 +3009,35 @@ export default class SessionReplayReadService {
2400
3009
  );
2401
3010
  }
2402
3011
 
2403
- statement.append(
2404
- SQL` AND (hasAny(exceptionFingerprints, [${{
2405
- type: TableColumnType.Text,
2406
- value: data.exceptionFingerprint,
2407
- }}])`,
2408
- );
3012
+ if (isScopedRumException && data.primaryEntityId) {
3013
+ /* Both finalized fingerprints and instance-proven ids stay in this RUM app. */
3014
+ statement.append(
3015
+ SQL` AND rumApplicationId = ${{
3016
+ type: TableColumnType.ObjectID,
3017
+ value: data.primaryEntityId,
3018
+ }}`,
3019
+ );
3020
+ }
3021
+
3022
+ statement.append(" AND (");
2409
3023
 
2410
- if (instanceSessionIds.length > 0) {
3024
+ if (isScopedNonRumOrUnknownException) {
2411
3025
  statement.append(
2412
- SQL` OR sessionId IN (${{
3026
+ SQL`sessionId IN (${{
2413
3027
  type: TableColumnType.Text,
2414
3028
  value: new Includes(instanceSessionIds),
2415
3029
  }})`,
2416
3030
  );
3031
+ } else {
3032
+ statement.append(
3033
+ SQL`hasAny(exceptionFingerprints, [${{
3034
+ type: TableColumnType.Text,
3035
+ value: data.exceptionFingerprint,
3036
+ }}])`,
3037
+ );
2417
3038
  }
2418
3039
 
2419
- statement.append(")");
2420
-
2421
- statement.append(
2422
- SQL` GROUP BY projectId, rumApplicationId, sessionId
2423
- HAVING (hasAny(aggExceptionFingerprints, [${{
2424
- type: TableColumnType.Text,
2425
- value: data.exceptionFingerprint,
2426
- }}])`,
2427
- );
2428
-
2429
- if (instanceSessionIds.length > 0) {
3040
+ if (!isScopedNonRumOrUnknownException && instanceSessionIds.length > 0) {
2430
3041
  statement.append(
2431
3042
  SQL` OR sessionId IN (${{
2432
3043
  type: TableColumnType.Text,
@@ -2437,6 +3048,53 @@ export default class SessionReplayReadService {
2437
3048
 
2438
3049
  statement.append(")");
2439
3050
 
3051
+ statement.append(" GROUP BY projectId, rumApplicationId, sessionId");
3052
+
3053
+ if (isScopedNonRumOrUnknownException) {
3054
+ /*
3055
+ * Windowing happens over every application before either the time
3056
+ * window or accessible-app filter is applied. QUALIFY also precedes
3057
+ * ORDER/LIMIT, so a second application can never be sorted away.
3058
+ */
3059
+ statement.append(" QUALIFY matchedApplicationCount = 1");
3060
+ statement.append(
3061
+ SQL` AND aggStartTime >= ${{
3062
+ type: TableColumnType.BigNumber,
3063
+ value: startTime.getTime(),
3064
+ }} AND aggStartTime <= ${{
3065
+ type: TableColumnType.BigNumber,
3066
+ value: endTime.getTime(),
3067
+ }}`,
3068
+ );
3069
+
3070
+ if (data.accessibleRumApplicationIds) {
3071
+ statement.append(
3072
+ SQL` AND rumApplicationId IN (${{
3073
+ type: TableColumnType.ObjectID,
3074
+ value: new Includes(data.accessibleRumApplicationIds),
3075
+ }})`,
3076
+ );
3077
+ }
3078
+ } else {
3079
+ statement.append(" HAVING (");
3080
+ statement.append(
3081
+ SQL`hasAny(aggExceptionFingerprints, [${{
3082
+ type: TableColumnType.Text,
3083
+ value: data.exceptionFingerprint,
3084
+ }}])`,
3085
+ );
3086
+ if (instanceSessionIds.length > 0) {
3087
+ statement.append(
3088
+ SQL` OR sessionId IN (${{
3089
+ type: TableColumnType.Text,
3090
+ value: new Includes(instanceSessionIds),
3091
+ }})`,
3092
+ );
3093
+ }
3094
+
3095
+ statement.append(")");
3096
+ }
3097
+
2440
3098
  statement.append(
2441
3099
  SQL` ORDER BY aggStartTime DESC
2442
3100
  LIMIT ${{
@@ -2452,7 +3110,21 @@ export default class SessionReplayReadService {
2452
3110
  data?: Array<JSONObject>;
2453
3111
  }>();
2454
3112
 
2455
- return (response.data || []).map(
3113
+ const responseRows: Array<JSONObject> = (response.data || []).filter(
3114
+ (row: JSONObject): boolean => {
3115
+ /*
3116
+ * QUALIFY is authoritative. Its count was computed over every app
3117
+ * before ORDER/LIMIT, so retaining only count=1 is also a fail-closed
3118
+ * guard if a changed driver ever returns a row that should not survive.
3119
+ */
3120
+ return (
3121
+ !isScopedNonRumOrUnknownException ||
3122
+ readNumber(row, "matchedApplicationCount") === 1
3123
+ );
3124
+ },
3125
+ );
3126
+
3127
+ return responseRows.map(
2456
3128
  (row: JSONObject): SessionReplayExceptionSession => {
2457
3129
  return {
2458
3130
  sessionId: readString(row, "sessionId"),
@@ -2484,9 +3156,10 @@ export default class SessionReplayReadService {
2484
3156
  * session, so a session that started inside the window threw inside
2485
3157
  * [startTime, endTime + max session length].
2486
3158
  *
2487
- * Best-effort: the side index only ADDS live sessions to the answer, so
2488
- * a failure here degrades to the finalized-only lookup with a warning
2489
- * rather than failing the exception page's replay card.
3159
+ * Best-effort for fully unscoped legacy and typed RUM lookups: there it only
3160
+ * ADDS live sessions, so a failure degrades to finalized headers. Scoped
3161
+ * non-RUM and ID-only lookups rely on it as their sole entity-ownership
3162
+ * proof and therefore fail closed.
2490
3163
  *
2491
3164
  * A caller-pinned sessionId narrows the lookup rather than bypassing it,
2492
3165
  * so the pin can never assert that a session threw something the
@@ -2495,6 +3168,8 @@ export default class SessionReplayReadService {
2495
3168
  private static async getSessionIdsForExceptionInstances(data: {
2496
3169
  projectId: ObjectID;
2497
3170
  exceptionFingerprint: string;
3171
+ primaryEntityId?: ObjectID | undefined;
3172
+ primaryEntityType?: ServiceType | undefined;
2498
3173
  startTime: Date;
2499
3174
  endTime: Date;
2500
3175
  sessionId?: string | undefined;
@@ -2523,6 +3198,32 @@ export default class SessionReplayReadService {
2523
3198
  }}
2524
3199
  `;
2525
3200
 
3201
+ if (data.primaryEntityId) {
3202
+ statement.append(
3203
+ SQL` AND primaryEntityId = ${{
3204
+ type: TableColumnType.ObjectID,
3205
+ value: data.primaryEntityId,
3206
+ }}`,
3207
+ );
3208
+ }
3209
+
3210
+ if (data.primaryEntityType === ServiceType.OpenTelemetry) {
3211
+ /* NULL/empty is the historical discriminator for an OTel service. */
3212
+ statement.append(
3213
+ SQL` AND (ifNull(primaryEntityType, '') = '' OR primaryEntityType = ${{
3214
+ type: TableColumnType.Text,
3215
+ value: ServiceType.OpenTelemetry as string,
3216
+ }})`,
3217
+ );
3218
+ } else if (data.primaryEntityType) {
3219
+ statement.append(
3220
+ SQL` AND primaryEntityType = ${{
3221
+ type: TableColumnType.Text,
3222
+ value: data.primaryEntityType as string,
3223
+ }}`,
3224
+ );
3225
+ }
3226
+
2526
3227
  /*
2527
3228
  * A pinned sessionId narrows this lookup; it does NOT replace it.
2528
3229
  *
@@ -2572,7 +3273,7 @@ export default class SessionReplayReadService {
2572
3273
  });
2573
3274
  } catch (err: unknown) {
2574
3275
  logger.warn(
2575
- "SessionReplayReadService: could not look up exception instances by session; answering from finalized headers only",
3276
+ "SessionReplayReadService: could not look up exception instances by session; scoped non-RUM lookups fail closed and other lookups answer from finalized headers only",
2576
3277
  );
2577
3278
  logger.warn(err);
2578
3279