@oneuptime/common 14.0.8 → 14.0.9

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 (639) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  4. package/Models/DatabaseModels/Project.ts +55 -5
  5. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  6. package/Models/DatabaseModels/RunnerJob.ts +75 -0
  7. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  8. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  9. package/Server/API/AIChatAPI.ts +6 -3
  10. package/Server/API/AIInvestigationAPI.ts +17 -10
  11. package/Server/API/AutoRemediationAPI.ts +207 -25
  12. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  13. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  14. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  15. package/Server/Infrastructure/GlobalCache.ts +32 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/TelemetryIngest.ts +41 -18
  21. package/Server/Services/AIService.ts +70 -1
  22. package/Server/Services/AlertService.ts +352 -11
  23. package/Server/Services/AlertSeverityService.ts +65 -15
  24. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  25. package/Server/Services/IncidentService.ts +280 -104
  26. package/Server/Services/IncidentSeverityService.ts +66 -15
  27. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  28. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  29. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  30. package/Server/Services/KubernetesClusterService.ts +129 -21
  31. package/Server/Services/ProjectService.ts +32 -0
  32. package/Server/Services/ProxmoxResourceService.ts +322 -5
  33. package/Server/Services/RunnerJobService.ts +241 -104
  34. package/Server/Services/RunnerService.ts +1 -1
  35. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  36. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  37. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  38. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  39. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  40. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  41. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
  42. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
  43. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
  44. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  45. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  46. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  47. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
  48. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  49. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  50. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  51. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  52. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  53. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
  54. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  55. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  56. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  57. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  58. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  59. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  60. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  61. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  62. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  63. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  64. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  65. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  66. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  67. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  68. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  69. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  70. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  71. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  72. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
  73. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  74. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  75. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  76. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  77. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  78. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  79. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  80. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  81. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  82. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  83. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  84. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  85. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  86. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  87. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  88. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  89. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  90. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  91. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  92. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  93. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  94. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  95. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  96. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  97. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  98. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  99. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  100. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  101. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  102. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  103. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  104. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  105. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  106. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  107. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  108. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  109. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  110. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  111. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  112. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  113. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  114. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  115. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  116. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  117. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  118. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  119. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  120. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  121. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  122. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  123. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  124. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  125. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  126. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  127. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  128. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  129. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  130. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  131. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  132. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  133. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  134. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  135. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  136. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  137. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  138. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  139. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  140. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  141. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  142. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  143. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  144. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  145. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  146. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  147. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  148. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  149. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  150. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  151. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  152. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  153. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  154. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  155. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  156. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  157. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  158. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  159. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  160. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  161. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  162. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  163. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  164. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  165. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  166. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  167. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  168. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  169. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  170. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  171. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  172. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  173. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  174. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  175. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  176. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  177. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  178. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  179. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  180. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  181. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  182. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  183. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  184. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  185. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  186. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  187. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  188. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  189. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  190. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  191. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  192. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  193. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  194. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  195. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  196. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  197. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  198. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  199. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  200. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  201. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  202. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  203. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  204. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  205. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  206. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  207. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  208. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  209. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  210. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  211. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  212. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  213. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  214. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  215. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  216. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  217. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  218. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  219. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  220. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  221. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  222. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  223. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  224. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  225. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  226. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  227. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
  228. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
  229. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
  230. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  231. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  232. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  233. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  234. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  235. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  236. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  237. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  238. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  239. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  240. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  241. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  242. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  243. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  244. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  245. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  246. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  247. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  248. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  249. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  250. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  251. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  252. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  253. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
  254. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  255. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  256. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  257. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  258. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  259. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  260. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  261. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  262. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  263. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  264. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  265. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  266. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  267. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  268. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  269. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  270. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  271. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  272. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  273. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  274. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  275. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  276. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  277. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  278. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  279. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  280. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  281. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  282. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  283. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  284. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  285. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  286. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
  287. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  288. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  289. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  290. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  291. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  292. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  293. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  294. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  295. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  296. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  297. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  298. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  299. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  300. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  301. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  302. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  303. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  304. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  305. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  306. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  307. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  308. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  309. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  310. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  311. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  312. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  313. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  314. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  315. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  316. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  317. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  318. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  319. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  320. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  321. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  322. package/Tests/UI/Components/Input.test.tsx +80 -1
  323. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  324. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  325. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  326. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  327. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  328. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  329. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  330. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  331. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  332. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  333. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  334. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  335. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  336. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  337. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  338. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  339. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  340. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  341. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  342. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  343. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  344. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  345. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  346. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  347. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  348. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  349. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  350. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  351. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  352. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  353. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  354. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  355. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  356. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  357. package/Types/AI/AIRunType.ts +9 -4
  358. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  359. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
  360. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  361. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  362. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  363. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  364. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  365. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  366. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  367. package/Types/Team/ComplianceRule.ts +434 -0
  368. package/Types/Team/ComplianceRuleType.ts +21 -0
  369. package/Types/Team/TeamComplianceStatus.ts +81 -0
  370. package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
  371. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  372. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  373. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  374. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  375. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  376. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  377. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  378. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  379. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  380. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  381. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  382. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  383. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  384. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  385. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  386. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  387. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  388. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  389. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  390. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  391. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  392. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  393. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  394. package/UI/Components/Input/Input.tsx +90 -57
  395. package/UI/Components/List/ListRow.tsx +8 -74
  396. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  397. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  398. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  399. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  400. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  401. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  402. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  403. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  404. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  405. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  406. package/UI/Components/Table/TableBody.tsx +8 -2
  407. package/UI/Components/Table/TableRow.tsx +20 -140
  408. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  409. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  410. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  411. package/UI/Components/TelemetryViewer/types.ts +2 -0
  412. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  413. package/UI/Components/Toggle/Toggle.tsx +26 -0
  414. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  415. package/UI/Styles/Theme.css +14 -2
  416. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  417. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  418. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  419. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  420. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  421. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  422. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  423. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  424. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  425. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  426. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  427. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  428. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  429. package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
  430. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  431. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  432. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  433. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  434. package/build/dist/Server/API/AIChatAPI.js +6 -1
  435. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  436. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  437. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  438. package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
  439. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  440. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  441. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  442. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  443. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  444. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  445. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  446. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  447. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  448. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  449. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  450. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  451. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  452. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  453. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  454. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  455. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  456. package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
  457. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  458. package/build/dist/Server/Services/AIService.js +59 -1
  459. package/build/dist/Server/Services/AIService.js.map +1 -1
  460. package/build/dist/Server/Services/AlertService.js +248 -13
  461. package/build/dist/Server/Services/AlertService.js.map +1 -1
  462. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  463. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  464. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  465. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  466. package/build/dist/Server/Services/IncidentService.js +199 -72
  467. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  468. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  469. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  470. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  471. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  472. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  473. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  474. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  475. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  476. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  477. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  478. package/build/dist/Server/Services/ProjectService.js +22 -0
  479. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  480. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  481. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  482. package/build/dist/Server/Services/RunnerJobService.js +178 -92
  483. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  484. package/build/dist/Server/Services/RunnerService.js +1 -1
  485. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  486. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  487. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  488. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  489. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  490. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  491. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  492. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  493. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  494. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  495. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  496. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  497. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  498. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
  499. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  500. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
  501. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  502. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
  503. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  504. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  505. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  506. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  507. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  508. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  509. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
  510. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  512. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  513. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  514. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  515. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  516. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  517. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  518. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  519. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  520. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  521. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
  522. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  523. package/build/dist/Types/AI/AIRunType.js +9 -4
  524. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  525. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
  526. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  527. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  528. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  529. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  530. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  531. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  532. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  533. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  534. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  535. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  536. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  537. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  538. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  539. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  540. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  541. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  542. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  543. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  544. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  545. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
  546. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  547. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  548. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  549. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  550. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  551. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  552. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  553. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  554. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  555. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  556. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  557. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  558. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  559. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  560. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  561. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  562. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  563. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  564. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  565. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  566. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  567. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  568. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  569. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  570. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  571. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  572. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  573. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  574. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  575. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  576. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  577. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  578. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  579. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  580. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  581. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  582. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  583. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  584. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  585. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  586. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  587. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  588. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  589. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  590. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  591. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  592. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  593. package/build/dist/UI/Components/Input/Input.js +69 -54
  594. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  595. package/build/dist/UI/Components/List/ListRow.js +4 -39
  596. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  597. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  598. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  600. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  601. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  602. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  603. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  604. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  605. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  606. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  607. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  608. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  609. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  610. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  611. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  612. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  613. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  614. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  615. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  616. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  617. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  618. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  619. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  620. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  621. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  622. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  626. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  627. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  628. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  629. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  630. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  631. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  632. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  633. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  634. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  635. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  636. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  637. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  638. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  639. package/package.json +1 -1
@@ -11,9 +11,12 @@ import { act, cleanup, render } from "@testing-library/react";
11
11
  */
12
12
  import * as React from "react";
13
13
  import { afterEach, describe, expect, it, jest } from "@jest/globals";
14
- import type { SpyInstance } from "jest-mock";
14
+ import type { Mock, SpyInstance } from "jest-mock";
15
+ import SessionReplayMaskingMode from "../../../Types/Rum/SessionReplayMaskingMode";
15
16
  import ReplayStage, {
17
+ REPLAY_ASSET_FAILURE_FLUSH_MS,
16
18
  REPLAY_DOCUMENT_CSP,
19
+ REPLAY_MASKED_DOCUMENT_CSP,
17
20
  REPLAY_STAGE_ASPECT_CSS_VAR,
18
21
  REPLAY_STAGE_FILL_BOX_CLASS,
19
22
  REPLAY_STAGE_FIT_OVERFLOW_CLASS,
@@ -30,8 +33,13 @@ import ReplayStage, {
30
33
  disableReplayTextSelection,
31
34
  enableReplayTextSelection,
32
35
  formatReplayStageAspect,
36
+ getReplayDocumentCsp,
33
37
  getReplayStageBoxClassName,
34
38
  } from "../../../../App/FeatureSet/Dashboard/src/Components/SessionReplay/ReplayStage";
39
+ import {
40
+ REPLAY_ASSET_FAILURE_MAX_LISTED,
41
+ ReplayAssetFailure,
42
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/SessionReplay/ReplayRecordedAssets";
35
43
  import {
36
44
  ReplayEngine,
37
45
  ReplayEngineDiagnostics,
@@ -972,7 +980,13 @@ describe("ReplayStage replay document", () => {
972
980
  const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
973
981
  makeReplayer();
974
982
 
975
- render(<ReplayStage engine={engine} isTextSelectionEnabled={true} />);
983
+ render(
984
+ <ReplayStage
985
+ engine={engine}
986
+ isTextSelectionEnabled={true}
987
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
988
+ />,
989
+ );
976
990
 
977
991
  act((): void => {
978
992
  engine.emitReplayer({
@@ -1687,6 +1701,903 @@ describe("ReplayStage replay document", () => {
1687
1701
  });
1688
1702
  });
1689
1703
 
1704
+ /*
1705
+ * The replay document's policy, and the recorded assets it lets through,
1706
+ * from github.com/OneUptime/oneuptime/issues/4119. The previous policy
1707
+ * allowed only data: and blob: images and inline styles, and it was in
1708
+ * force before rrweb's first rebuild - so every image of every recording
1709
+ * played back broken, and every stylesheet the recorder could not inline
1710
+ * was refused, taking the rules that hid a Power Pages "You're offline"
1711
+ * banner with it.
1712
+ *
1713
+ * jsdom enforces no CSP and fetches nothing, so what these pin is the
1714
+ * policy's text, which document gets which, and the stage's side of the
1715
+ * failure reporting. That the policy really loads (and refuses) what it
1716
+ * says is proven in a real browser by the Playwright suite
1717
+ * (packages/E2E/SessionReplay/RecordedAssets.spec.ts).
1718
+ */
1719
+
1720
+ function parsePolicy(policy: string): Map<string, Array<string>> {
1721
+ const directives: Map<string, Array<string>> = new Map<
1722
+ string,
1723
+ Array<string>
1724
+ >();
1725
+
1726
+ for (const part of policy.split(";")) {
1727
+ const tokens: Array<string> = part.trim().split(/\s+/).filter(Boolean);
1728
+ const name: string | undefined = tokens[0];
1729
+
1730
+ if (!name) {
1731
+ continue;
1732
+ }
1733
+
1734
+ /* A repeated directive is ignored by the browser, silently. */
1735
+ expect(directives.has(name)).toBe(false);
1736
+ directives.set(name, tokens.slice(1));
1737
+ }
1738
+
1739
+ return directives;
1740
+ }
1741
+
1742
+ function readInjectedPolicy(
1743
+ replayer: ReplayerLike & { iframe: HTMLIFrameElement },
1744
+ ): string | null {
1745
+ return (
1746
+ replayer.iframe.contentDocument?.head
1747
+ ?.querySelector('meta[http-equiv="Content-Security-Policy"]')
1748
+ ?.getAttribute("content") ?? null
1749
+ );
1750
+ }
1751
+
1752
+ /* An element's error event, from its own window, not bubbling - as a browser fires it. */
1753
+ function failToLoad(element: Element): void {
1754
+ const view: Window & typeof globalThis = element.ownerDocument
1755
+ .defaultView as Window & typeof globalThis;
1756
+
1757
+ element.dispatchEvent(new view.Event("error"));
1758
+ }
1759
+
1760
+ function addRecordedElement(
1761
+ replayer: ReplayerLike & { iframe: HTMLIFrameElement },
1762
+ tagName: string,
1763
+ attributes: Record<string, string>,
1764
+ ): Element {
1765
+ const doc: Document = replayer.iframe.contentDocument as Document;
1766
+ const element: Element = doc.createElement(tagName);
1767
+
1768
+ for (const [name, value] of Object.entries(attributes)) {
1769
+ element.setAttribute(name, value);
1770
+ }
1771
+
1772
+ (tagName === "link" ? doc.head : doc.body).appendChild(element);
1773
+
1774
+ return element;
1775
+ }
1776
+
1777
+ type FailureListener = (
1778
+ failures: ReadonlyArray<ReplayAssetFailure>,
1779
+ isTruncated: boolean,
1780
+ ) => void;
1781
+
1782
+ describe("REPLAY_DOCUMENT_CSP", () => {
1783
+ it("loads the recorded page's images, stylesheets and fonts, and nothing that runs, connects, embeds or submits", () => {
1784
+ const directives: Map<string, Array<string>> = parsePolicy(
1785
+ REPLAY_DOCUMENT_CSP,
1786
+ );
1787
+
1788
+ expect(Object.fromEntries(directives)).toEqual({
1789
+ "script-src": ["'none'"],
1790
+ "default-src": ["'none'"],
1791
+ "img-src": ["data:", "blob:", "http:", "https:"],
1792
+ "style-src": ["'unsafe-inline'", "http:", "https:"],
1793
+ "font-src": ["data:", "http:", "https:"],
1794
+ "media-src": ["'none'"],
1795
+ "connect-src": ["'none'"],
1796
+ "object-src": ["'none'"],
1797
+ "form-action": ["'none'"],
1798
+ });
1799
+ });
1800
+
1801
+ it("never grows a source that would let a document built from recorded HTML run or reach anything else", () => {
1802
+ for (const policy of [REPLAY_DOCUMENT_CSP, REPLAY_MASKED_DOCUMENT_CSP]) {
1803
+ const directives: Map<string, Array<string>> = parsePolicy(policy);
1804
+ const everySource: Array<string> = Array.from(directives.values()).flat();
1805
+
1806
+ for (const forbidden of [
1807
+ "*",
1808
+ "'unsafe-eval'",
1809
+ "'wasm-unsafe-eval'",
1810
+ "'strict-dynamic'",
1811
+ "'unsafe-hashes'",
1812
+ "'self'",
1813
+ "ws:",
1814
+ "wss:",
1815
+ "filesystem:",
1816
+ ]) {
1817
+ expect(everySource).not.toContain(forbidden);
1818
+ }
1819
+
1820
+ /* Everything not listed falls back to default-src: frames, workers, manifests. */
1821
+ for (const unlisted of [
1822
+ "frame-src",
1823
+ "child-src",
1824
+ "worker-src",
1825
+ "manifest-src",
1826
+ "script-src-attr",
1827
+ "script-src-elem",
1828
+ ]) {
1829
+ expect(directives.has(unlisted)).toBe(false);
1830
+ }
1831
+ }
1832
+ });
1833
+
1834
+ it("differs for a masked replay only in refusing images and web fonts from the network", () => {
1835
+ /*
1836
+ * With images refused, a web font is the one fetch a stylesheet in the
1837
+ * recording could make depend on what the page shows - so a masked
1838
+ * replay loads neither. Its stylesheets still load.
1839
+ */
1840
+ const readable: Map<string, Array<string>> = parsePolicy(
1841
+ REPLAY_DOCUMENT_CSP,
1842
+ );
1843
+ const masked: Map<string, Array<string>> = parsePolicy(
1844
+ REPLAY_MASKED_DOCUMENT_CSP,
1845
+ );
1846
+
1847
+ expect(masked.get("img-src")).toEqual(["data:", "blob:"]);
1848
+ expect(masked.get("font-src")).toEqual(["data:"]);
1849
+ expect(masked.get("style-src")).toEqual([
1850
+ "'unsafe-inline'",
1851
+ "http:",
1852
+ "https:",
1853
+ ]);
1854
+
1855
+ for (const policy of [readable, masked]) {
1856
+ policy.delete("img-src");
1857
+ policy.delete("font-src");
1858
+ }
1859
+
1860
+ expect(Object.fromEntries(masked)).toEqual(Object.fromEntries(readable));
1861
+ });
1862
+
1863
+ it("is chosen by whether the replay is masked", () => {
1864
+ expect(getReplayDocumentCsp(false)).toBe(REPLAY_DOCUMENT_CSP);
1865
+ expect(getReplayDocumentCsp(true)).toBe(REPLAY_MASKED_DOCUMENT_CSP);
1866
+ });
1867
+ });
1868
+
1869
+ describe("ReplayStage recorded assets", () => {
1870
+ it.each([
1871
+ [SessionReplayMaskingMode.MaskSensitiveInputsOnly],
1872
+ [SessionReplayMaskingMode.MaskInputsOnly],
1873
+ ])(
1874
+ "gives a %s recording the policy that loads its images, as soon as the Replayer exists",
1875
+ (mode: string) => {
1876
+ const engine: FakeEngine = new FakeEngine();
1877
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
1878
+ makeReplayer();
1879
+
1880
+ render(<ReplayStage engine={engine} maskingMode={mode} />);
1881
+
1882
+ /* Created, not yet rebuilt: the policy must already be in force. */
1883
+ act((): void => {
1884
+ engine.emitReplayer({ type: "created", replayer: replayer });
1885
+ });
1886
+
1887
+ expect(readInjectedPolicy(replayer)).toBe(REPLAY_DOCUMENT_CSP);
1888
+ },
1889
+ );
1890
+
1891
+ it.each([
1892
+ [SessionReplayMaskingMode.MaskAllText],
1893
+ [undefined],
1894
+ [null],
1895
+ [""],
1896
+ ["SomeModeFromANewerServer"],
1897
+ ])(
1898
+ "gives a %p recording the masked policy - Mask all text, or a mode it cannot read",
1899
+ (mode: string | null | undefined) => {
1900
+ const engine: FakeEngine = new FakeEngine();
1901
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
1902
+ makeReplayer();
1903
+
1904
+ render(<ReplayStage engine={engine} maskingMode={mode} />);
1905
+ act((): void => {
1906
+ engine.emitReplayer({ type: "created", replayer: replayer });
1907
+ });
1908
+
1909
+ expect(readInjectedPolicy(replayer)).toBe(REPLAY_MASKED_DOCUMENT_CSP);
1910
+ },
1911
+ );
1912
+
1913
+ it("puts the same policy back after a rebuild, whatever the mode says by then", () => {
1914
+ const engine: FakeEngine = new FakeEngine();
1915
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
1916
+ makeReplayer();
1917
+ const doc: Document = replayer.iframe.contentDocument as Document;
1918
+ const { rerender } = render(
1919
+ <ReplayStage
1920
+ engine={engine}
1921
+ maskingMode={SessionReplayMaskingMode.MaskAllText}
1922
+ />,
1923
+ );
1924
+
1925
+ act((): void => {
1926
+ engine.emitReplayer({ type: "created", replayer: replayer });
1927
+ });
1928
+
1929
+ rerender(
1930
+ <ReplayStage
1931
+ engine={engine}
1932
+ maskingMode={SessionReplayMaskingMode.MaskInputsOnly}
1933
+ />,
1934
+ );
1935
+
1936
+ /* rrweb's rebuild replaces <head>; the document keeps its first policy. */
1937
+ doc.head.innerHTML = "";
1938
+ act((): void => {
1939
+ engine.emitReplayer({
1940
+ type: "fullsnapshot-rebuilded",
1941
+ replayer: replayer,
1942
+ });
1943
+ });
1944
+
1945
+ expect(readInjectedPolicy(replayer)).toBe(REPLAY_MASKED_DOCUMENT_CSP);
1946
+
1947
+ /* A Replayer created after the change gets the new one. */
1948
+ const next: ReplayerLike & { iframe: HTMLIFrameElement } = makeReplayer();
1949
+
1950
+ act((): void => {
1951
+ engine.emitReplayer({ type: "created", replayer: next });
1952
+ });
1953
+
1954
+ expect(readInjectedPolicy(next)).toBe(REPLAY_DOCUMENT_CSP);
1955
+ });
1956
+
1957
+ it("reports a recorded image that failed to load once, after the burst it failed in", () => {
1958
+ jest.useFakeTimers();
1959
+
1960
+ const engine: FakeEngine = new FakeEngine();
1961
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
1962
+ makeReplayer();
1963
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
1964
+
1965
+ render(
1966
+ <ReplayStage
1967
+ engine={engine}
1968
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
1969
+ onAssetLoadFailures={onFailures}
1970
+ />,
1971
+ );
1972
+ act((): void => {
1973
+ engine.emitReplayer({ type: "created", replayer: replayer });
1974
+ });
1975
+
1976
+ const logo: Element = addRecordedElement(replayer, "img", {
1977
+ src: "https://wbdynprod.powerappsportals.com/logo.png",
1978
+ alt: "logo",
1979
+ });
1980
+
1981
+ failToLoad(logo);
1982
+ failToLoad(logo);
1983
+
1984
+ expect(onFailures).not.toHaveBeenCalled();
1985
+
1986
+ act((): void => {
1987
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
1988
+ });
1989
+
1990
+ expect(onFailures).toHaveBeenCalledTimes(1);
1991
+ expect(onFailures).toHaveBeenLastCalledWith(
1992
+ [
1993
+ {
1994
+ kind: "image",
1995
+ url: "https://wbdynprod.powerappsportals.com/logo.png",
1996
+ host: "wbdynprod.powerappsportals.com",
1997
+ },
1998
+ ],
1999
+ false,
2000
+ );
2001
+ });
2002
+
2003
+ it("hands a burst over once, and again only when a new address fails", () => {
2004
+ jest.useFakeTimers();
2005
+
2006
+ const engine: FakeEngine = new FakeEngine();
2007
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2008
+ makeReplayer();
2009
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2010
+
2011
+ render(
2012
+ <ReplayStage
2013
+ engine={engine}
2014
+ maskingMode={SessionReplayMaskingMode.MaskInputsOnly}
2015
+ onAssetLoadFailures={onFailures}
2016
+ />,
2017
+ );
2018
+ act((): void => {
2019
+ engine.emitReplayer({ type: "created", replayer: replayer });
2020
+ });
2021
+
2022
+ for (const name of ["web", "close", "logo"]) {
2023
+ failToLoad(
2024
+ addRecordedElement(replayer, "img", {
2025
+ src: `https://content.powerapps.com/img/${name}.png`,
2026
+ }),
2027
+ );
2028
+ }
2029
+
2030
+ act((): void => {
2031
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2032
+ });
2033
+
2034
+ expect(onFailures).toHaveBeenCalledTimes(1);
2035
+ expect(onFailures.mock.calls[0]?.[0]).toHaveLength(3);
2036
+
2037
+ /* The same three again: nothing new to say. */
2038
+ for (const image of Array.from(
2039
+ (replayer.iframe.contentDocument as Document).images,
2040
+ )) {
2041
+ failToLoad(image);
2042
+ }
2043
+
2044
+ act((): void => {
2045
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2046
+ });
2047
+
2048
+ expect(onFailures).toHaveBeenCalledTimes(1);
2049
+
2050
+ failToLoad(
2051
+ addRecordedElement(replayer, "link", {
2052
+ rel: "stylesheet",
2053
+ href: "https://content.powerapps.com/dist/pwa-style.css",
2054
+ }),
2055
+ );
2056
+
2057
+ act((): void => {
2058
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2059
+ });
2060
+
2061
+ expect(onFailures).toHaveBeenCalledTimes(2);
2062
+ expect(
2063
+ (onFailures.mock.calls[1]?.[0] ?? []).map(
2064
+ (failure: ReplayAssetFailure): string => {
2065
+ return `${failure.kind} ${failure.url}`;
2066
+ },
2067
+ ),
2068
+ ).toEqual([
2069
+ "image https://content.powerapps.com/img/web.png",
2070
+ "image https://content.powerapps.com/img/close.png",
2071
+ "image https://content.powerapps.com/img/logo.png",
2072
+ "stylesheet https://content.powerapps.com/dist/pwa-style.css",
2073
+ ]);
2074
+ });
2075
+
2076
+ it("counts a Mask all text recording's stylesheets but never the images its policy refuses", () => {
2077
+ jest.useFakeTimers();
2078
+
2079
+ const engine: FakeEngine = new FakeEngine();
2080
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2081
+ makeReplayer();
2082
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2083
+
2084
+ render(
2085
+ <ReplayStage
2086
+ engine={engine}
2087
+ maskingMode={SessionReplayMaskingMode.MaskAllText}
2088
+ onAssetLoadFailures={onFailures}
2089
+ />,
2090
+ );
2091
+ act((): void => {
2092
+ engine.emitReplayer({ type: "created", replayer: replayer });
2093
+ });
2094
+
2095
+ failToLoad(
2096
+ addRecordedElement(replayer, "img", {
2097
+ src: "https://cdn.example/avatar.png",
2098
+ }),
2099
+ );
2100
+ failToLoad(
2101
+ addRecordedElement(replayer, "link", {
2102
+ rel: "stylesheet",
2103
+ href: "https://cdn.example/site.css",
2104
+ }),
2105
+ );
2106
+
2107
+ act((): void => {
2108
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2109
+ });
2110
+
2111
+ expect(onFailures).toHaveBeenCalledTimes(1);
2112
+ expect(onFailures).toHaveBeenLastCalledWith(
2113
+ [
2114
+ {
2115
+ kind: "stylesheet",
2116
+ url: "https://cdn.example/site.css",
2117
+ host: "cdn.example",
2118
+ },
2119
+ ],
2120
+ false,
2121
+ );
2122
+ });
2123
+
2124
+ it("stops listing past its cap, marks the list cut short, and hands that over once", () => {
2125
+ jest.useFakeTimers();
2126
+
2127
+ const engine: FakeEngine = new FakeEngine();
2128
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2129
+ makeReplayer();
2130
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2131
+
2132
+ render(
2133
+ <ReplayStage
2134
+ engine={engine}
2135
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2136
+ onAssetLoadFailures={onFailures}
2137
+ />,
2138
+ );
2139
+ act((): void => {
2140
+ engine.emitReplayer({ type: "created", replayer: replayer });
2141
+ });
2142
+
2143
+ /* A recording built to flood the viewer with distinct failing addresses. */
2144
+ for (
2145
+ let index: number = 0;
2146
+ index < REPLAY_ASSET_FAILURE_MAX_LISTED + 50;
2147
+ index++
2148
+ ) {
2149
+ failToLoad(
2150
+ addRecordedElement(replayer, "img", {
2151
+ src: `https://h${index}.flood.example/x.png`,
2152
+ }),
2153
+ );
2154
+ }
2155
+
2156
+ act((): void => {
2157
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2158
+ });
2159
+
2160
+ expect(onFailures).toHaveBeenCalledTimes(1);
2161
+ expect(onFailures.mock.calls[0]?.[0]).toHaveLength(
2162
+ REPLAY_ASSET_FAILURE_MAX_LISTED,
2163
+ );
2164
+ expect(onFailures.mock.calls[0]?.[1]).toBe(true);
2165
+ /* The first ones are the ones kept. */
2166
+ expect(onFailures.mock.calls[0]?.[0]?.[0]?.url).toBe(
2167
+ "https://h0.flood.example/x.png",
2168
+ );
2169
+
2170
+ /* Past the cap nothing new is handed over. */
2171
+ failToLoad(
2172
+ addRecordedElement(replayer, "img", {
2173
+ src: "https://one-more.flood.example/x.png",
2174
+ }),
2175
+ );
2176
+ act((): void => {
2177
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2178
+ });
2179
+
2180
+ expect(onFailures).toHaveBeenCalledTimes(1);
2181
+ });
2182
+
2183
+ it("says nothing about what no site owner can fix, or what the policy refuses on purpose", () => {
2184
+ jest.useFakeTimers();
2185
+
2186
+ const engine: FakeEngine = new FakeEngine();
2187
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2188
+ makeReplayer();
2189
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2190
+
2191
+ render(
2192
+ <ReplayStage
2193
+ engine={engine}
2194
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2195
+ onAssetLoadFailures={onFailures}
2196
+ />,
2197
+ );
2198
+ act((): void => {
2199
+ engine.emitReplayer({ type: "created", replayer: replayer });
2200
+ });
2201
+
2202
+ failToLoad(
2203
+ addRecordedElement(replayer, "img", {
2204
+ src: "data:image/png;base64,AAAA",
2205
+ }),
2206
+ );
2207
+ failToLoad(
2208
+ addRecordedElement(replayer, "video", {
2209
+ src: "https://cdn.example/clip.mp4",
2210
+ }),
2211
+ );
2212
+ failToLoad(
2213
+ addRecordedElement(replayer, "iframe", {
2214
+ src: "https://widgets.example/frame.html",
2215
+ }),
2216
+ );
2217
+ failToLoad(
2218
+ addRecordedElement(replayer, "link", {
2219
+ rel: "preload",
2220
+ href: "https://cdn.example/hero.png",
2221
+ }),
2222
+ );
2223
+
2224
+ act((): void => {
2225
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS * 4);
2226
+ });
2227
+
2228
+ expect(onFailures).not.toHaveBeenCalled();
2229
+ });
2230
+
2231
+ it("listens again after a rebuild whose document.open() dropped the listener", () => {
2232
+ jest.useFakeTimers();
2233
+
2234
+ const engine: FakeEngine = new FakeEngine();
2235
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2236
+ makeReplayer();
2237
+ const doc: Document = replayer.iframe.contentDocument as Document;
2238
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2239
+ const added: SpyInstance<Document["addEventListener"]> = jest.spyOn(
2240
+ doc,
2241
+ "addEventListener",
2242
+ );
2243
+
2244
+ render(
2245
+ <ReplayStage
2246
+ engine={engine}
2247
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2248
+ onAssetLoadFailures={onFailures}
2249
+ />,
2250
+ );
2251
+ act((): void => {
2252
+ engine.emitReplayer({ type: "created", replayer: replayer });
2253
+ });
2254
+
2255
+ const errorListener: EventListenerOrEventListenerObject | undefined =
2256
+ added.mock.calls.find(
2257
+ (call: Parameters<Document["addEventListener"]>): boolean => {
2258
+ return call[0] === "error";
2259
+ },
2260
+ )?.[1] as EventListenerOrEventListenerObject | undefined;
2261
+
2262
+ expect(errorListener).toBeDefined();
2263
+
2264
+ /*
2265
+ * What Chromium's document.open() does to every listener on the
2266
+ * document - and what jsdom's does not, so it is done by hand.
2267
+ */
2268
+ doc.removeEventListener(
2269
+ "error",
2270
+ errorListener as EventListenerOrEventListenerObject,
2271
+ true,
2272
+ );
2273
+
2274
+ const lost: Element = addRecordedElement(replayer, "img", {
2275
+ src: "https://cdn.example/lost.png",
2276
+ });
2277
+
2278
+ failToLoad(lost);
2279
+ act((): void => {
2280
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2281
+ });
2282
+
2283
+ expect(onFailures).not.toHaveBeenCalled();
2284
+
2285
+ act((): void => {
2286
+ engine.emitReplayer({
2287
+ type: "fullsnapshot-rebuilded",
2288
+ replayer: replayer,
2289
+ });
2290
+ });
2291
+
2292
+ failToLoad(lost);
2293
+ act((): void => {
2294
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2295
+ });
2296
+
2297
+ expect(onFailures).toHaveBeenCalledTimes(1);
2298
+ expect(onFailures.mock.calls[0]?.[0]?.[0]?.url).toBe(
2299
+ "https://cdn.example/lost.png",
2300
+ );
2301
+
2302
+ added.mockRestore();
2303
+ });
2304
+
2305
+ it("keeps exactly one listener on a document, however often it is installed", () => {
2306
+ const engine: FakeEngine = new FakeEngine();
2307
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2308
+ makeReplayer();
2309
+ const doc: Document = replayer.iframe.contentDocument as Document;
2310
+ /*
2311
+ * Every add and remove of an "error" listener, in order, passed through
2312
+ * to the real document - which in jsdom, unlike Chromium, keeps its
2313
+ * listeners across a rebuild, so a leak would really accumulate here.
2314
+ */
2315
+ type ListenerCall = [
2316
+ "add" | "remove",
2317
+ EventListenerOrEventListenerObject | null,
2318
+ boolean,
2319
+ ];
2320
+ const calls: Array<ListenerCall> = [];
2321
+ const isCapture: (
2322
+ options: boolean | EventListenerOptions | undefined,
2323
+ ) => boolean = (
2324
+ options: boolean | EventListenerOptions | undefined,
2325
+ ): boolean => {
2326
+ return typeof options === "boolean" ? options : Boolean(options?.capture);
2327
+ };
2328
+ const realAdd: Document["addEventListener"] =
2329
+ doc.addEventListener.bind(doc);
2330
+ const realRemove: Document["removeEventListener"] =
2331
+ doc.removeEventListener.bind(doc);
2332
+ const added: SpyInstance<Document["addEventListener"]> = jest
2333
+ .spyOn(doc, "addEventListener")
2334
+ .mockImplementation(
2335
+ (...args: Parameters<Document["addEventListener"]>): void => {
2336
+ if (args[0] === "error") {
2337
+ calls.push(["add", args[1], isCapture(args[2])]);
2338
+ }
2339
+
2340
+ realAdd(...args);
2341
+ },
2342
+ );
2343
+ const removed: SpyInstance<Document["removeEventListener"]> = jest
2344
+ .spyOn(doc, "removeEventListener")
2345
+ .mockImplementation(
2346
+ (...args: Parameters<Document["removeEventListener"]>): void => {
2347
+ if (args[0] === "error") {
2348
+ calls.push(["remove", args[1], isCapture(args[2])]);
2349
+ }
2350
+
2351
+ realRemove(...args);
2352
+ },
2353
+ );
2354
+
2355
+ render(
2356
+ <ReplayStage
2357
+ engine={engine}
2358
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2359
+ onAssetLoadFailures={(): void => {
2360
+ // not asserted here
2361
+ }}
2362
+ />,
2363
+ );
2364
+
2365
+ act((): void => {
2366
+ engine.emitReplayer({ type: "created", replayer: replayer });
2367
+ engine.emitReplayer({ type: "fullsnapshot-rebuilded", replayer });
2368
+ engine.emitReplayer({ type: "fullsnapshot-rebuilded", replayer });
2369
+ engine.emitReplayer({ type: "fullsnapshot-rebuilded", replayer });
2370
+ });
2371
+
2372
+ /* Replayed as the DOM keeps listeners: one per (listener, capture) pair. */
2373
+ const live: Array<EventListenerOrEventListenerObject | null> = [];
2374
+
2375
+ for (const [kind, listener, capture] of calls) {
2376
+ if (!capture) {
2377
+ continue;
2378
+ }
2379
+
2380
+ const at: number = live.indexOf(listener);
2381
+
2382
+ if (kind === "add" && at === -1) {
2383
+ live.push(listener);
2384
+ } else if (kind === "remove" && at !== -1) {
2385
+ live.splice(at, 1);
2386
+ }
2387
+ }
2388
+
2389
+ const lastAdded: ListenerCall | undefined = calls
2390
+ .filter((call: ListenerCall): boolean => {
2391
+ return call[0] === "add";
2392
+ })
2393
+ .pop();
2394
+
2395
+ expect(live).toHaveLength(1);
2396
+ expect(live[0]).toBe(lastAdded?.[1]);
2397
+
2398
+ added.mockRestore();
2399
+ removed.mockRestore();
2400
+ });
2401
+
2402
+ it("gives an address one entry across every Replayer of the engine", () => {
2403
+ jest.useFakeTimers();
2404
+
2405
+ const engine: FakeEngine = new FakeEngine();
2406
+ const first: ReplayerLike & { iframe: HTMLIFrameElement } = makeReplayer();
2407
+ const second: ReplayerLike & { iframe: HTMLIFrameElement } = makeReplayer();
2408
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2409
+
2410
+ render(
2411
+ <ReplayStage
2412
+ engine={engine}
2413
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2414
+ onAssetLoadFailures={onFailures}
2415
+ />,
2416
+ );
2417
+ act((): void => {
2418
+ engine.emitReplayer({ type: "created", replayer: first });
2419
+ });
2420
+
2421
+ failToLoad(
2422
+ addRecordedElement(first, "img", { src: "https://cdn.example/logo.png" }),
2423
+ );
2424
+ act((): void => {
2425
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2426
+ });
2427
+
2428
+ /* A seek: a new Replayer rebuilds the same page and fails the same image. */
2429
+ act((): void => {
2430
+ engine.emitReplayer({ type: "destroyed", replayer: first });
2431
+ engine.emitReplayer({ type: "created", replayer: second });
2432
+ });
2433
+
2434
+ failToLoad(
2435
+ addRecordedElement(second, "img", {
2436
+ src: "https://cdn.example/logo.png",
2437
+ }),
2438
+ );
2439
+ act((): void => {
2440
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2441
+ });
2442
+
2443
+ expect(onFailures).toHaveBeenCalledTimes(1);
2444
+ });
2445
+
2446
+ it("stops listening to a destroyed Replayer's document", () => {
2447
+ jest.useFakeTimers();
2448
+
2449
+ const engine: FakeEngine = new FakeEngine();
2450
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2451
+ makeReplayer();
2452
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2453
+
2454
+ render(
2455
+ <ReplayStage
2456
+ engine={engine}
2457
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2458
+ onAssetLoadFailures={onFailures}
2459
+ />,
2460
+ );
2461
+ act((): void => {
2462
+ engine.emitReplayer({ type: "created", replayer: replayer });
2463
+ });
2464
+
2465
+ const image: Element = addRecordedElement(replayer, "img", {
2466
+ src: "https://cdn.example/late.png",
2467
+ });
2468
+
2469
+ act((): void => {
2470
+ engine.emitReplayer({ type: "destroyed", replayer: replayer });
2471
+ });
2472
+
2473
+ failToLoad(image);
2474
+ act((): void => {
2475
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2476
+ });
2477
+
2478
+ expect(onFailures).not.toHaveBeenCalled();
2479
+ });
2480
+
2481
+ it("drops a pending hand-over and stops listening when the stage unmounts", () => {
2482
+ jest.useFakeTimers();
2483
+
2484
+ const engine: FakeEngine = new FakeEngine();
2485
+ const replayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2486
+ makeReplayer();
2487
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2488
+ const { unmount } = render(
2489
+ <ReplayStage
2490
+ engine={engine}
2491
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2492
+ onAssetLoadFailures={onFailures}
2493
+ />,
2494
+ );
2495
+
2496
+ act((): void => {
2497
+ engine.emitReplayer({ type: "created", replayer: replayer });
2498
+ });
2499
+
2500
+ const image: Element = addRecordedElement(replayer, "img", {
2501
+ src: "https://cdn.example/pending.png",
2502
+ });
2503
+
2504
+ failToLoad(image);
2505
+ unmount();
2506
+
2507
+ act((): void => {
2508
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2509
+ });
2510
+
2511
+ /*
2512
+ * A FRESH address: the one above is already known, and the per-address
2513
+ * dedupe would swallow it whether or not the listener survived.
2514
+ */
2515
+ failToLoad(
2516
+ addRecordedElement(replayer, "img", {
2517
+ src: "https://cdn.example/after-unmount.png",
2518
+ }),
2519
+ );
2520
+ act((): void => {
2521
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2522
+ });
2523
+
2524
+ expect(onFailures).not.toHaveBeenCalled();
2525
+ });
2526
+
2527
+ it("starts again for a new engine: its failures are its own", () => {
2528
+ jest.useFakeTimers();
2529
+
2530
+ const firstEngine: FakeEngine = new FakeEngine();
2531
+ const secondEngine: FakeEngine = new FakeEngine();
2532
+ const firstReplayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2533
+ makeReplayer();
2534
+ const secondReplayer: ReplayerLike & { iframe: HTMLIFrameElement } =
2535
+ makeReplayer();
2536
+ const onFailures: Mock<FailureListener> = jest.fn<FailureListener>();
2537
+ const { rerender } = render(
2538
+ <ReplayStage
2539
+ engine={firstEngine}
2540
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2541
+ onAssetLoadFailures={onFailures}
2542
+ />,
2543
+ );
2544
+
2545
+ act((): void => {
2546
+ firstEngine.emitReplayer({ type: "created", replayer: firstReplayer });
2547
+ });
2548
+
2549
+ failToLoad(
2550
+ addRecordedElement(firstReplayer, "img", {
2551
+ src: "https://cdn.example/tab-one.png",
2552
+ }),
2553
+ );
2554
+ act((): void => {
2555
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2556
+ });
2557
+
2558
+ rerender(
2559
+ <ReplayStage
2560
+ engine={secondEngine}
2561
+ maskingMode={SessionReplayMaskingMode.MaskSensitiveInputsOnly}
2562
+ onAssetLoadFailures={onFailures}
2563
+ />,
2564
+ );
2565
+ act((): void => {
2566
+ secondEngine.emitReplayer({ type: "created", replayer: secondReplayer });
2567
+ });
2568
+
2569
+ failToLoad(
2570
+ addRecordedElement(secondReplayer, "img", {
2571
+ src: "https://cdn.example/tab-one.png",
2572
+ }),
2573
+ );
2574
+ act((): void => {
2575
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2576
+ });
2577
+
2578
+ expect(onFailures).toHaveBeenCalledTimes(2);
2579
+ expect(onFailures.mock.calls[1]?.[0]).toEqual([
2580
+ {
2581
+ kind: "image",
2582
+ url: "https://cdn.example/tab-one.png",
2583
+ host: "cdn.example",
2584
+ },
2585
+ ]);
2586
+
2587
+ /* The old engine's documents are no longer heard. */
2588
+ failToLoad(
2589
+ addRecordedElement(firstReplayer, "img", {
2590
+ src: "https://cdn.example/old-engine.png",
2591
+ }),
2592
+ );
2593
+ act((): void => {
2594
+ jest.advanceTimersByTime(REPLAY_ASSET_FAILURE_FLUSH_MS);
2595
+ });
2596
+
2597
+ expect(onFailures).toHaveBeenCalledTimes(2);
2598
+ });
2599
+ });
2600
+
1690
2601
  describe("ReplayStage device frame and touch", () => {
1691
2602
  it("draws the phone frame for a mobile-width recording", () => {
1692
2603
  const engine: FakeEngine = new FakeEngine({