@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
@@ -206,6 +206,93 @@ function giveRuleListItem(sheet: CSSStyleSheet | null | undefined): void {
206
206
  });
207
207
  }
208
208
 
209
+ interface CrossOriginSheet {
210
+ sheet: CSSStyleSheet;
211
+ /* How many times cssRules was asked for. */
212
+ readCount: () => number;
213
+ /* Every value the capture tried to give `disabled`: there must be none. */
214
+ disabledWrites: Array<boolean>;
215
+ }
216
+
217
+ /*
218
+ * A stylesheet the stage loaded from another site: in force, and closed to
219
+ * the page, whose origin is the Dashboard's. Chromium and Firefox throw a
220
+ * SecurityError from cssRules; WebKit answers null.
221
+ */
222
+ function makeCrossOriginSheet(
223
+ rules: "throws" | "null",
224
+ options?: { isDisabled?: boolean },
225
+ ): CrossOriginSheet {
226
+ const disabledWrites: Array<boolean> = [];
227
+ const sheet: Record<string, unknown> = {};
228
+ let reads: number = 0;
229
+
230
+ Object.defineProperty(sheet, "cssRules", {
231
+ get: (): CSSRuleList | null => {
232
+ reads++;
233
+
234
+ if (rules === "null") {
235
+ return null;
236
+ }
237
+
238
+ throw new DOMException(
239
+ "Cannot access rules of a cross-origin sheet.",
240
+ "SecurityError",
241
+ );
242
+ },
243
+ });
244
+ Object.defineProperty(sheet, "disabled", {
245
+ get: (): boolean => {
246
+ return Boolean(options?.isDisabled);
247
+ },
248
+ set: (value: boolean): void => {
249
+ disabledWrites.push(value);
250
+ },
251
+ });
252
+
253
+ return {
254
+ sheet: sheet as unknown as CSSStyleSheet,
255
+ readCount: (): number => {
256
+ return reads;
257
+ },
258
+ disabledWrites: disabledWrites,
259
+ };
260
+ }
261
+
262
+ /* A readable sheet of plain rule objects, in a list that has item(). */
263
+ function makeMockSheet(rules: Array<Record<string, unknown>>): CSSStyleSheet {
264
+ return {
265
+ disabled: false,
266
+ cssRules: {
267
+ length: rules.length,
268
+ item: (index: number): CSSRule | null => {
269
+ return (rules[index] as CSSRule | undefined) ?? null;
270
+ },
271
+ },
272
+ } as unknown as CSSStyleSheet;
273
+ }
274
+
275
+ /* The sheet a <link> or <style> hands out, in place of jsdom's. */
276
+ function giveSheet(owner: Element, sheet: CSSStyleSheet): void {
277
+ Object.defineProperty(owner, "sheet", {
278
+ configurable: true,
279
+ get: (): CSSStyleSheet => {
280
+ return sheet;
281
+ },
282
+ });
283
+ }
284
+
285
+ /*
286
+ * The first rule of a <style>'s own sheet: jsdom hands its cssRules out
287
+ * as a plain Array.
288
+ */
289
+ function firstRuleOf(style: Element): CSSRule {
290
+ const sheet: CSSStyleSheet = (style as HTMLStyleElement)
291
+ .sheet as CSSStyleSheet;
292
+
293
+ return (sheet.cssRules as unknown as Array<CSSRule>)[0] as CSSRule;
294
+ }
295
+
209
296
  function prepareRuleLists(replayDocument: Document): void {
210
297
  for (
211
298
  let index: number = 0;
@@ -436,6 +523,34 @@ function markImageLoaded(
436
523
  });
437
524
  }
438
525
 
526
+ /*
527
+ * An image the stage is still fetching: not complete, and with no natural
528
+ * size yet - unless it is a progressive one whose header has decoded.
529
+ */
530
+ function markImageLoading(
531
+ image: Element,
532
+ natural?: { width: number; height: number },
533
+ ): void {
534
+ Object.defineProperty(image, "complete", {
535
+ configurable: true,
536
+ get: (): boolean => {
537
+ return false;
538
+ },
539
+ });
540
+ Object.defineProperty(image, "naturalWidth", {
541
+ configurable: true,
542
+ get: (): number => {
543
+ return natural?.width ?? 0;
544
+ },
545
+ });
546
+ Object.defineProperty(image, "naturalHeight", {
547
+ configurable: true,
548
+ get: (): number => {
549
+ return natural?.height ?? 0;
550
+ },
551
+ });
552
+ }
553
+
439
554
  interface ImageCanvasMock {
440
555
  draws: Array<Array<unknown>>;
441
556
  }
@@ -2501,6 +2616,62 @@ describe("serializeReplayFrame", () => {
2501
2616
 
2502
2617
  expect(serialize(replayDocument).svg).not.toContain("cdn.example");
2503
2618
  });
2619
+
2620
+ /*
2621
+ * The same promise with everything the stage now loads from the
2622
+ * recorded site in play: a stylesheet and an @import it cannot read
2623
+ * (so what they hide is pinned hidden), an image still loading, one
2624
+ * that failed and one it may not read.
2625
+ */
2626
+ it("leaves no reference to anything outside the frame while a stylesheet it cannot read is in force", () => {
2627
+ mockImageCanvas({ hasContext: true, isTainted: true });
2628
+
2629
+ const replayDocument: Document = makeReplayDocument(
2630
+ `<div id="offline" class="displayNone" style="background-image: url(https://cdn.example/offline.png)">You're offline.</div>` +
2631
+ `<img id="pending" src="https://cdn.example/pending.png" srcset="https://cdn.example/pending@2x.png 2x">` +
2632
+ `<img id="failed" src="https://cdn.example/failed.png">` +
2633
+ `<img id="photo" src="https://cdn.example/photo.jpg">`,
2634
+ {
2635
+ head:
2636
+ `<link id="site" rel="stylesheet" href="https://cdn.example/site.css">` +
2637
+ `<style id="imports">@import url("https://cdn.example/imported.css"); .own { background: url(https://cdn.example/own.png); }</style>`,
2638
+ },
2639
+ );
2640
+
2641
+ giveSheet(
2642
+ liveById(replayDocument, "site"),
2643
+ makeCrossOriginSheet("throws").sheet,
2644
+ );
2645
+ Object.defineProperty(
2646
+ firstRuleOf(liveById(replayDocument, "imports")),
2647
+ "styleSheet",
2648
+ {
2649
+ configurable: true,
2650
+ get: (): CSSStyleSheet => {
2651
+ return makeCrossOriginSheet("null").sheet;
2652
+ },
2653
+ },
2654
+ );
2655
+ overrideComputedStyle(liveById(replayDocument, "offline"), {
2656
+ display: "none",
2657
+ });
2658
+ markImageLoading(liveById(replayDocument, "pending"));
2659
+ markImageLoaded(liveById(replayDocument, "failed"), {
2660
+ width: 0,
2661
+ height: 0,
2662
+ });
2663
+ markImageLoaded(liveById(replayDocument, "photo"), {
2664
+ width: 640,
2665
+ height: 480,
2666
+ });
2667
+
2668
+ const serialization: ReplayFrameSerialization = serialize(replayDocument);
2669
+
2670
+ expect(serialization.svg).not.toContain("cdn.example");
2671
+ expect(styleOf(byId(readFrame(serialization).svg, "offline"))).toContain(
2672
+ "display: none !important",
2673
+ );
2674
+ });
2504
2675
  });
2505
2676
 
2506
2677
  describe("stylesheets", () => {
@@ -2688,6 +2859,698 @@ describe("serializeReplayFrame", () => {
2688
2859
  });
2689
2860
  });
2690
2861
 
2862
+ /*
2863
+ * #4119. The stage loads a stylesheet the recorder could not inline from
2864
+ * the recorded site, and to the replay document - the Dashboard's origin
2865
+ * - it is another site's: its rules cannot be read, so they cannot come
2866
+ * across. What it hid is still hidden on the stage (here a portal's
2867
+ * "You're offline" banner, hidden by .displayNone in such a sheet), so
2868
+ * while such a sheet is in force every light-DOM element the stage
2869
+ * computes display: none for is pinned hidden in the clone. jsdom
2870
+ * applies no rule of a sheet it cannot read either, so the stage's
2871
+ * computed display is given per element.
2872
+ */
2873
+ describe("a stylesheet it cannot read", () => {
2874
+ function makeOfflinePortal(): Document {
2875
+ return makeReplayDocument(
2876
+ `<div id="offline" class="displayNone">You're offline. This is a read only version of the page.</div>` +
2877
+ `<main id="content"><p id="welcome" style="color: rgb(0, 128, 0)">Welcome</p><span id="note" class="displayNone" style="color: red">Saved for later</span></main>`,
2878
+ {
2879
+ head:
2880
+ `<link id="portal" rel="stylesheet" href="https://portal-cdn.example/portal.css">` +
2881
+ `<style>.own { color: green; }</style>`,
2882
+ },
2883
+ );
2884
+ }
2885
+
2886
+ /* What .displayNone { display: none !important } does on the stage. */
2887
+ function hideOnStage(replayDocument: Document, ids: Array<string>): void {
2888
+ for (const id of ids) {
2889
+ overrideComputedStyle(liveById(replayDocument, id), {
2890
+ display: "none",
2891
+ });
2892
+ }
2893
+ }
2894
+
2895
+ /* Every element of the picture pinned hidden, by id or by name. */
2896
+ function pinnedHidden(captured: CapturedFrame): Array<string> {
2897
+ return Array.from(captured.html.querySelectorAll("*"))
2898
+ .filter((element: Element): boolean => {
2899
+ return styleOf(element).includes("display: none !important");
2900
+ })
2901
+ .map((element: Element): string => {
2902
+ return element.getAttribute("id") ?? element.localName;
2903
+ });
2904
+ }
2905
+
2906
+ for (const rules of ["throws", "null"] as const) {
2907
+ it(`keeps what a <link> it cannot read hides hidden, and nothing else, when its cssRules ${
2908
+ rules === "throws" ? "throws a SecurityError" : "is null (WebKit)"
2909
+ }`, () => {
2910
+ const replayDocument: Document = makeOfflinePortal();
2911
+ const portal: CrossOriginSheet = makeCrossOriginSheet(rules);
2912
+
2913
+ giveSheet(liveById(replayDocument, "portal"), portal.sheet);
2914
+ hideOnStage(replayDocument, ["offline", "note"]);
2915
+
2916
+ const before: string = replayDocument.documentElement.outerHTML;
2917
+ const captured: CapturedFrame = capture(replayDocument);
2918
+
2919
+ expect(styleOf(byId(captured.svg, "offline"))).toBe(
2920
+ "display: none !important",
2921
+ );
2922
+ /* After the element's own declarations, which it keeps. */
2923
+ expect(styleOf(byId(captured.svg, "note"))).toBe(
2924
+ "color: red; display: none !important",
2925
+ );
2926
+ /* Nothing for what the stage shows. */
2927
+ expect(byId(captured.svg, "content").getAttribute("style")).toBeNull();
2928
+ expect(styleOf(byId(captured.svg, "welcome"))).toBe(
2929
+ "color: rgb(0, 128, 0)",
2930
+ );
2931
+ /* <head> is display: none in every engine anyway. */
2932
+ expect(pinnedHidden(captured)).toEqual(["head", "offline", "note"]);
2933
+ /* The sheet itself is left out, and nothing points at its host. */
2934
+ expect(captured.svg.getElementsByTagName("link")).toHaveLength(0);
2935
+ expect(headStyleTexts(captured).join("\n")).toContain(".own");
2936
+ expect(captured.serialization.svg).not.toContain("portal-cdn.example");
2937
+ /*
2938
+ * Only read: the live document is as it was, the sheet was never
2939
+ * disabled to see what it does, and it was asked for its rules
2940
+ * once to find out and once when the clone reached its <link> -
2941
+ * not once per element.
2942
+ */
2943
+ expect(replayDocument.documentElement.outerHTML).toBe(before);
2944
+ expect(portal.disabledWrites).toEqual([]);
2945
+ expect(portal.readCount()).toBeLessThanOrEqual(2);
2946
+ });
2947
+ }
2948
+
2949
+ it("adds nothing when every sheet in force can be read, though the page hides elements", () => {
2950
+ const replayDocument: Document = makeOfflinePortal();
2951
+
2952
+ giveSheet(
2953
+ liveById(replayDocument, "portal"),
2954
+ makeMockSheet([{ cssText: ".displayNone { display: none; }" }]),
2955
+ );
2956
+ hideOnStage(replayDocument, ["offline", "note"]);
2957
+
2958
+ const captured: CapturedFrame = capture(replayDocument);
2959
+
2960
+ /* The page's own rule hides them in the picture, as on the stage. */
2961
+ expect(headStyleTexts(captured).join("\n")).toContain(
2962
+ ".displayNone { display: none; }",
2963
+ );
2964
+ expect(byId(captured.svg, "offline").getAttribute("style")).toBeNull();
2965
+ expect(styleOf(byId(captured.svg, "note"))).toBe("color: red");
2966
+ expect(pinnedHidden(captured)).toEqual([]);
2967
+ expect(captured.serialization.svg).not.toContain(
2968
+ "display: none !important",
2969
+ );
2970
+ });
2971
+
2972
+ it("keeps what an @import it cannot read hides hidden, inside a <style> it can read", () => {
2973
+ const replayDocument: Document = makeReplayDocument(
2974
+ `<div id="offline">You're offline.</div><p id="welcome">Welcome</p>`,
2975
+ {
2976
+ head: `<style id="imports">@import url("https://portal-cdn.example/portal.css"); .own { color: green; }</style>`,
2977
+ },
2978
+ );
2979
+
2980
+ Object.defineProperty(
2981
+ firstRuleOf(liveById(replayDocument, "imports")),
2982
+ "styleSheet",
2983
+ {
2984
+ configurable: true,
2985
+ get: (): CSSStyleSheet => {
2986
+ return makeCrossOriginSheet("throws").sheet;
2987
+ },
2988
+ },
2989
+ );
2990
+ hideOnStage(replayDocument, ["offline"]);
2991
+
2992
+ const captured: CapturedFrame = capture(replayDocument);
2993
+ const css: string = headStyleTexts(captured).join("\n");
2994
+
2995
+ expect(styleOf(byId(captured.svg, "offline"))).toBe(
2996
+ "display: none !important",
2997
+ );
2998
+ expect(byId(captured.svg, "welcome").getAttribute("style")).toBeNull();
2999
+ /* The rest of the sheet still comes across; the import cannot. */
3000
+ expect(css).toContain(".own");
3001
+ expect(css).not.toContain("@import");
3002
+ expect(captured.serialization.svg).not.toContain("portal-cdn.example");
3003
+ });
3004
+
3005
+ it("follows @imports through sheets it can read, past an @layer statement, to one it cannot", () => {
3006
+ const replayDocument: Document = makeReplayDocument(
3007
+ `<div id="offline">You're offline.</div>`,
3008
+ {
3009
+ head: `<link id="site" rel="stylesheet" href="https://portal.example/site.css">`,
3010
+ },
3011
+ );
3012
+
3013
+ giveSheet(
3014
+ liveById(replayDocument, "site"),
3015
+ makeMockSheet([
3016
+ { cssText: "@layer base, site;", nameList: ["base", "site"] },
3017
+ {
3018
+ cssText:
3019
+ '@import url("https://portal.example/base.css") layer(base);',
3020
+ media: { mediaText: "" },
3021
+ styleSheet: makeMockSheet([
3022
+ {
3023
+ cssText:
3024
+ '@import url("https://portal-cdn.example/portal.css");',
3025
+ media: { mediaText: "" },
3026
+ styleSheet: makeCrossOriginSheet("null").sheet,
3027
+ },
3028
+ { cssText: ".base { color: blue; }" },
3029
+ ]),
3030
+ },
3031
+ { cssText: ".site { color: green; }" },
3032
+ ]),
3033
+ );
3034
+ hideOnStage(replayDocument, ["offline"]);
3035
+
3036
+ const captured: CapturedFrame = capture(replayDocument);
3037
+ const css: string = headStyleTexts(captured).join("\n");
3038
+
3039
+ expect(styleOf(byId(captured.svg, "offline"))).toBe(
3040
+ "display: none !important",
3041
+ );
3042
+ expect(css).toContain(".base");
3043
+ expect(css).toContain(".site");
3044
+ expect(captured.serialization.svg).not.toContain("portal-cdn.example");
3045
+ });
3046
+
3047
+ /*
3048
+ * The scope is the clone's own: a sheet that is off, or whose media
3049
+ * the replay window does not match, is not applied on the stage
3050
+ * either, and one that never loaded has nothing to apply.
3051
+ */
3052
+ it("leaves out a sheet that is disabled, whose media the window does not match, or that never loaded", () => {
3053
+ const replayDocument: Document = makeReplayDocument(
3054
+ `<div id="offline">You're offline.</div>`,
3055
+ {
3056
+ head:
3057
+ `<link id="disabled" rel="stylesheet" href="https://portal-cdn.example/off.css">` +
3058
+ `<link id="printed" rel="stylesheet" href="https://portal-cdn.example/print.css" media="print">` +
3059
+ `<link id="missing" rel="stylesheet" href="https://portal-cdn.example/gone.css">` +
3060
+ `<link id="readable" rel="stylesheet" href="https://portal.example/site.css">` +
3061
+ `<style id="imports">@import url("https://portal-cdn.example/print-import.css") print; .own { color: green; }</style>`,
3062
+ },
3063
+ );
3064
+
3065
+ giveSheet(
3066
+ liveById(replayDocument, "disabled"),
3067
+ makeCrossOriginSheet("throws", { isDisabled: true }).sheet,
3068
+ );
3069
+ giveSheet(
3070
+ liveById(replayDocument, "printed"),
3071
+ makeCrossOriginSheet("throws").sheet,
3072
+ );
3073
+ /* An import that never loaded has no sheet, on the stage either. */
3074
+ giveSheet(
3075
+ liveById(replayDocument, "readable"),
3076
+ makeMockSheet([
3077
+ {
3078
+ cssText: '@import url("https://portal-cdn.example/gone.css");',
3079
+ media: { mediaText: "" },
3080
+ styleSheet: null,
3081
+ },
3082
+ { cssText: ".site { color: green; }" },
3083
+ ]),
3084
+ );
3085
+ Object.defineProperty(
3086
+ firstRuleOf(liveById(replayDocument, "imports")),
3087
+ "styleSheet",
3088
+ {
3089
+ configurable: true,
3090
+ get: (): CSSStyleSheet => {
3091
+ return makeCrossOriginSheet("throws").sheet;
3092
+ },
3093
+ },
3094
+ );
3095
+ hideOnStage(replayDocument, ["offline"]);
3096
+ defineMatchMedia(replayDocument, (query: string): boolean => {
3097
+ return !query.includes("print");
3098
+ });
3099
+
3100
+ const captured: CapturedFrame = capture(replayDocument);
3101
+
3102
+ expect(byId(captured.svg, "offline").getAttribute("style")).toBeNull();
3103
+ expect(captured.serialization.svg).not.toContain(
3104
+ "display: none !important",
3105
+ );
3106
+
3107
+ /* The same page printed: the print sheet and import are in force. */
3108
+ defineMatchMedia(replayDocument, (): boolean => {
3109
+ return true;
3110
+ });
3111
+
3112
+ expect(styleOf(byId(capture(replayDocument).svg, "offline"))).toBe(
3113
+ "display: none !important",
3114
+ );
3115
+ });
3116
+
3117
+ /*
3118
+ * Stylesheets apply per document: a nested frame is measured by a
3119
+ * serialiser of its own, and neither document's sheets change what
3120
+ * the other's picture keeps hidden.
3121
+ */
3122
+ it("looks at each document's own sheets, the page's and every frame's apart", () => {
3123
+ const replayDocument: Document = makeReplayDocument(
3124
+ `<div id="page-banner">You're offline.</div><iframe id="widget"></iframe><iframe id="chat"></iframe>`,
3125
+ {
3126
+ head: `<link id="page-sheet" rel="stylesheet" href="https://portal-cdn.example/portal.css">`,
3127
+ },
3128
+ );
3129
+ const widget: Document = mountChildFrame(
3130
+ replayDocument,
3131
+ "widget",
3132
+ `<div id="widget-banner">Offline</div>`,
3133
+ { width: 300, height: 150 },
3134
+ );
3135
+ const chat: Document = mountChildFrame(
3136
+ replayDocument,
3137
+ "chat",
3138
+ `<div id="chat-banner">Offline</div>`,
3139
+ { width: 300, height: 150 },
3140
+ );
3141
+ const chatLink: HTMLLinkElement = chat.createElement("link");
3142
+
3143
+ chatLink.setAttribute("id", "chat-sheet");
3144
+ chatLink.setAttribute("rel", "stylesheet");
3145
+ chatLink.setAttribute("href", "https://chat-cdn.example/chat.css");
3146
+ chat.head.appendChild(chatLink);
3147
+
3148
+ giveSheet(
3149
+ liveById(replayDocument, "page-sheet"),
3150
+ makeCrossOriginSheet("throws").sheet,
3151
+ );
3152
+ giveSheet(chatLink, makeCrossOriginSheet("null").sheet);
3153
+ hideOnStage(replayDocument, ["page-banner"]);
3154
+ hideOnStage(widget, ["widget-banner"]);
3155
+ hideOnStage(chat, ["chat-banner"]);
3156
+
3157
+ const captured: CapturedFrame = capture(replayDocument);
3158
+ const frames: Array<ReplayFrameSerialization> =
3159
+ captured.serialization.frames.map(
3160
+ (frame: {
3161
+ serialization: ReplayFrameSerialization;
3162
+ }): ReplayFrameSerialization => {
3163
+ return frame.serialization;
3164
+ },
3165
+ );
3166
+
3167
+ expect(frames).toHaveLength(2);
3168
+ expect(styleOf(byId(captured.svg, "page-banner"))).toBe(
3169
+ "display: none !important",
3170
+ );
3171
+ /* The page's sheet does not reach into the widget's document... */
3172
+ expect(
3173
+ byId(readFrame(frames[0]!).svg, "widget-banner").getAttribute("style"),
3174
+ ).toBeNull();
3175
+ expect(frames[0]!.svg).not.toContain("display: none !important");
3176
+ /* ...and the chat frame's own sheet counts for the chat frame. */
3177
+ expect(styleOf(byId(readFrame(frames[1]!).svg, "chat-banner"))).toBe(
3178
+ "display: none !important",
3179
+ );
3180
+ });
3181
+
3182
+ /*
3183
+ * A shadow tree is flattened with every element's computed style
3184
+ * inlined, display included: it needs nothing more, whatever sheet
3185
+ * is in force, and its own sheets style nothing outside it.
3186
+ */
3187
+ it("leaves flattened shadow content to its computed style, and a shadow tree's own sheets out of it", () => {
3188
+ const replayDocument: Document = makeReplayDocument(
3189
+ `<div id="light-banner">You're offline.</div><fancy-card id="card"></fancy-card>`,
3190
+ {
3191
+ head: `<link id="page-sheet" rel="stylesheet" href="https://portal-cdn.example/portal.css">`,
3192
+ },
3193
+ );
3194
+ const root: ShadowRoot = attachShadow(
3195
+ liveById(replayDocument, "card"),
3196
+ `<p id="shadow-banner">Offline</p><p id="shadow-text">Text</p>`,
3197
+ );
3198
+
3199
+ giveSheet(
3200
+ liveById(replayDocument, "page-sheet"),
3201
+ makeCrossOriginSheet("throws").sheet,
3202
+ );
3203
+ hideOnStage(replayDocument, ["light-banner"]);
3204
+ overrideComputedStyle(root.getElementById("shadow-banner") as Element, {
3205
+ display: "none",
3206
+ });
3207
+
3208
+ const captured: CapturedFrame = capture(replayDocument);
3209
+
3210
+ expect(styleOf(byId(captured.svg, "light-banner"))).toBe(
3211
+ "display: none !important",
3212
+ );
3213
+ /* Once, from its computed style; not a second time on top. */
3214
+ expect(
3215
+ styleOf(byId(captured.svg, "shadow-banner")).split(
3216
+ "display: none !important",
3217
+ ),
3218
+ ).toHaveLength(2);
3219
+ expect(styleOf(byId(captured.svg, "shadow-text"))).not.toContain(
3220
+ "display: none",
3221
+ );
3222
+ });
3223
+
3224
+ it("is not turned on by a sheet inside a shadow tree", () => {
3225
+ const replayDocument: Document = makeReplayDocument(
3226
+ `<div id="light-banner">You're offline.</div><fancy-card id="card"></fancy-card>`,
3227
+ );
3228
+ const root: ShadowRoot = attachShadow(
3229
+ liveById(replayDocument, "card"),
3230
+ `<link id="shadow-sheet" rel="stylesheet" href="https://widget-cdn.example/widget.css"><p id="inner">Inner</p>`,
3231
+ );
3232
+
3233
+ giveSheet(
3234
+ root.getElementById("shadow-sheet") as Element,
3235
+ makeCrossOriginSheet("throws").sheet,
3236
+ );
3237
+ hideOnStage(replayDocument, ["light-banner"]);
3238
+
3239
+ const captured: CapturedFrame = capture(replayDocument);
3240
+
3241
+ expect(
3242
+ byId(captured.svg, "light-banner").getAttribute("style"),
3243
+ ).toBeNull();
3244
+ expect(captured.serialization.svg).not.toContain("widget-cdn.example");
3245
+ });
3246
+
3247
+ /*
3248
+ * visibility is the other way of hiding that a computed style reports
3249
+ * whichever rule did it: an off-canvas menu, a closed tooltip, a
3250
+ * collapsed table row. It is inherited, and a box can show inside a
3251
+ * hidden one, so the stage's value is pinned wherever the clone could
3252
+ * otherwise differ - and nowhere else.
3253
+ */
3254
+ function makeDrawerPage(): Document {
3255
+ return makeReplayDocument(
3256
+ `<nav id="drawer" class="offcanvas"><a id="drawer-link" class="menu-link" href="#home">Home</a><button id="drawer-toggle" class="toggle">Menu</button></nav>` +
3257
+ `<table><tbody id="rows"><tr id="collapsed-row" class="collapse"><td id="collapsed-cell">Archived</td></tr><tr id="open-row"><td id="open-cell">Current</td></tr></tbody></table>` +
3258
+ `<main id="content"><p id="welcome">Welcome</p><span id="badge" style="color: red">New</span></main>`,
3259
+ {
3260
+ head:
3261
+ `<link id="portal" rel="stylesheet" href="https://portal-cdn.example/portal.css">` +
3262
+ /*
3263
+ * A rule the clone keeps, which on the stage the portal's
3264
+ * `.offcanvas .menu-link { visibility: hidden }` outranks.
3265
+ */
3266
+ `<style>.menu-link { visibility: visible; }</style>`,
3267
+ },
3268
+ );
3269
+ }
3270
+
3271
+ /* What the portal's sheet makes the stage compute for each element. */
3272
+ function setStageVisibility(
3273
+ replayDocument: Document,
3274
+ visibilities: Record<string, string>,
3275
+ ): void {
3276
+ for (const [id, visibility] of Object.entries(visibilities)) {
3277
+ overrideComputedStyle(liveById(replayDocument, id), {
3278
+ visibility: visibility,
3279
+ });
3280
+ }
3281
+ }
3282
+
3283
+ /* The drawer closed and a table row collapsed, as the stage has them. */
3284
+ const DRAWER_PAGE_ON_STAGE: Record<string, string> = {
3285
+ drawer: "hidden",
3286
+ "drawer-link": "hidden",
3287
+ "drawer-toggle": "visible",
3288
+ "collapsed-row": "collapse",
3289
+ "collapsed-cell": "collapse",
3290
+ };
3291
+
3292
+ /* Every visibility declaration in the picture, by id or by name. */
3293
+ function carriedVisibility(
3294
+ captured: CapturedFrame,
3295
+ ): Record<string, string> {
3296
+ const carried: Record<string, string> = {};
3297
+
3298
+ for (const element of Array.from(captured.html.querySelectorAll("*"))) {
3299
+ const declarations: Array<string> = declarationsOf(element).filter(
3300
+ (declaration: string): boolean => {
3301
+ return declaration.startsWith("visibility");
3302
+ },
3303
+ );
3304
+
3305
+ if (declarations.length > 0) {
3306
+ carried[element.getAttribute("id") ?? element.localName] =
3307
+ declarations.join("; ");
3308
+ }
3309
+ }
3310
+
3311
+ return carried;
3312
+ }
3313
+
3314
+ it("pins what a sheet it cannot read hides with visibility, hidden and collapse alike, and a child it shows inside a hidden box", () => {
3315
+ const replayDocument: Document = makeDrawerPage();
3316
+ const portal: CrossOriginSheet = makeCrossOriginSheet("throws");
3317
+
3318
+ giveSheet(liveById(replayDocument, "portal"), portal.sheet);
3319
+ setStageVisibility(replayDocument, DRAWER_PAGE_ON_STAGE);
3320
+
3321
+ const before: string = replayDocument.documentElement.outerHTML;
3322
+ const captured: CapturedFrame = capture(replayDocument);
3323
+
3324
+ expect(carriedVisibility(captured)).toEqual({
3325
+ drawer: "visibility: hidden !important",
3326
+ /*
3327
+ * Hidden only by inheriting, and pinned all the same: unpinned,
3328
+ * the kept .menu-link rule would show it in the picture.
3329
+ */
3330
+ "drawer-link": "visibility: hidden !important",
3331
+ /* Visible inside a hidden parent: it would inherit hidden. */
3332
+ "drawer-toggle": "visibility: visible !important",
3333
+ "collapsed-row": "visibility: collapse !important",
3334
+ "collapsed-cell": "visibility: collapse !important",
3335
+ });
3336
+ /*
3337
+ * Nothing for what shows inside a box that shows - it inherits what
3338
+ * the stage has - and an element's own declarations come first.
3339
+ */
3340
+ expect(byId(captured.svg, "content").getAttribute("style")).toBeNull();
3341
+ expect(byId(captured.svg, "welcome").getAttribute("style")).toBeNull();
3342
+ expect(byId(captured.svg, "open-row").getAttribute("style")).toBeNull();
3343
+ expect(styleOf(byId(captured.svg, "badge"))).toBe("color: red");
3344
+ expect(styleOf(byId(captured.svg, "drawer-link"))).toBe(
3345
+ "visibility: hidden !important",
3346
+ );
3347
+ /* Only read, as for display: none. */
3348
+ expect(replayDocument.documentElement.outerHTML).toBe(before);
3349
+ expect(portal.disabledWrites).toEqual([]);
3350
+ expect(portal.readCount()).toBeLessThanOrEqual(2);
3351
+ });
3352
+
3353
+ it("adds no visibility anywhere when every sheet in force can be read, though the page hides elements with it", () => {
3354
+ const replayDocument: Document = makeDrawerPage();
3355
+
3356
+ giveSheet(
3357
+ liveById(replayDocument, "portal"),
3358
+ makeMockSheet([
3359
+ { cssText: ".offcanvas { visibility: hidden; }" },
3360
+ { cssText: ".offcanvas .menu-link { visibility: hidden; }" },
3361
+ { cssText: ".toggle { visibility: visible; }" },
3362
+ { cssText: ".collapse { visibility: collapse; }" },
3363
+ ]),
3364
+ );
3365
+ setStageVisibility(replayDocument, DRAWER_PAGE_ON_STAGE);
3366
+
3367
+ const captured: CapturedFrame = capture(replayDocument);
3368
+
3369
+ /* The page's own rules do it in the picture, as on the stage. */
3370
+ expect(headStyleTexts(captured).join("\n")).toContain(
3371
+ ".offcanvas { visibility: hidden; }",
3372
+ );
3373
+ expect(carriedVisibility(captured)).toEqual({});
3374
+
3375
+ for (const id of Object.keys(DRAWER_PAGE_ON_STAGE)) {
3376
+ expect(byId(captured.svg, id).getAttribute("style")).toBeNull();
3377
+ }
3378
+ });
3379
+
3380
+ it("pins no visibility on a page that hides nothing with it, though a sheet it cannot read is in force", () => {
3381
+ const replayDocument: Document = makeDrawerPage();
3382
+
3383
+ giveSheet(
3384
+ liveById(replayDocument, "portal"),
3385
+ makeCrossOriginSheet("null").sheet,
3386
+ );
3387
+
3388
+ const captured: CapturedFrame = capture(replayDocument);
3389
+
3390
+ expect(carriedVisibility(captured)).toEqual({});
3391
+ expect(byId(captured.svg, "drawer").getAttribute("style")).toBeNull();
3392
+ expect(styleOf(byId(captured.svg, "badge"))).toBe("color: red");
3393
+ });
3394
+
3395
+ it("pins display: none and visibility together on an element the stage hides both ways", () => {
3396
+ const replayDocument: Document = makeReplayDocument(
3397
+ `<div id="offline" class="displayNone invisible" style="color: red"><p id="offline-text">You're offline.</p></div><p id="welcome">Welcome</p>`,
3398
+ {
3399
+ head: `<link id="portal" rel="stylesheet" href="https://portal-cdn.example/portal.css">`,
3400
+ },
3401
+ );
3402
+
3403
+ giveSheet(
3404
+ liveById(replayDocument, "portal"),
3405
+ makeCrossOriginSheet("throws").sheet,
3406
+ );
3407
+ overrideComputedStyle(liveById(replayDocument, "offline"), {
3408
+ display: "none",
3409
+ visibility: "hidden",
3410
+ });
3411
+ /* visibility is inherited, display: none is not. */
3412
+ setStageVisibility(replayDocument, { "offline-text": "hidden" });
3413
+
3414
+ const captured: CapturedFrame = capture(replayDocument);
3415
+
3416
+ expect(styleOf(byId(captured.svg, "offline"))).toBe(
3417
+ "color: red; display: none !important; visibility: hidden !important",
3418
+ );
3419
+ expect(styleOf(byId(captured.svg, "offline-text"))).toBe(
3420
+ "visibility: hidden !important",
3421
+ );
3422
+ expect(byId(captured.svg, "welcome").getAttribute("style")).toBeNull();
3423
+ });
3424
+
3425
+ /*
3426
+ * <use> draws a copy of what it points at, style attribute and all, and
3427
+ * on the stage that copy inherits from the <use>. A sprite sheet in a
3428
+ * hidden <svg> is pinned at the <svg>; what it holds is left to inherit
3429
+ * from there, or every icon drawn from it would vanish from the picture.
3430
+ */
3431
+ it("pins a hidden sprite <svg> but leaves the symbols in it to inherit, so the icons <use> draws from them still show", () => {
3432
+ const replayDocument: Document = makeReplayDocument(
3433
+ `<svg id="sprite" class="hidden-sprite"><symbol id="icon-star"><rect id="star-shape" width="10" height="10"></rect><circle id="star-dot" r="2"></circle></symbol></svg>` +
3434
+ `<svg id="toolbar"><use id="star-use" href="#icon-star"></use></svg>` +
3435
+ `<div id="tip" class="tooltip"><svg id="tip-icon"><use id="tip-use" href="#icon-star"></use></svg></div>`,
3436
+ {
3437
+ head: `<link id="portal" rel="stylesheet" href="https://portal-cdn.example/portal.css">`,
3438
+ },
3439
+ );
3440
+
3441
+ giveSheet(
3442
+ liveById(replayDocument, "portal"),
3443
+ makeCrossOriginSheet("throws").sheet,
3444
+ );
3445
+ setStageVisibility(replayDocument, {
3446
+ sprite: "hidden",
3447
+ "icon-star": "hidden",
3448
+ "star-shape": "hidden",
3449
+ /* Shown by the sheet inside the hidden sprite: pinned visible. */
3450
+ "star-dot": "visible",
3451
+ /* A closed tooltip, and the icon in it that only inherits that. */
3452
+ tip: "hidden",
3453
+ "tip-icon": "hidden",
3454
+ "tip-use": "hidden",
3455
+ });
3456
+
3457
+ const captured: CapturedFrame = capture(replayDocument);
3458
+
3459
+ expect(carriedVisibility(captured)).toEqual({
3460
+ sprite: "visibility: hidden !important",
3461
+ "star-dot": "visibility: visible !important",
3462
+ tip: "visibility: hidden !important",
3463
+ });
3464
+ /* The references the copies are drawn from are kept. */
3465
+ expect(byId(captured.svg, "star-use").getAttribute("href")).toBe(
3466
+ "#icon-star",
3467
+ );
3468
+ });
3469
+
3470
+ /*
3471
+ * An animated visibility is pinned at its paused value with the rest of
3472
+ * the element's animated properties; it is not pinned a second time.
3473
+ */
3474
+ it("leaves an animated visibility to the animation freeze", () => {
3475
+ const replayDocument: Document = makeReplayDocument(
3476
+ `<div id="toast" class="toast">Saved</div>`,
3477
+ {
3478
+ head: `<link id="portal" rel="stylesheet" href="https://portal-cdn.example/portal.css">`,
3479
+ },
3480
+ );
3481
+ const toast: HTMLElement = liveById(replayDocument, "toast");
3482
+
3483
+ giveSheet(
3484
+ liveById(replayDocument, "portal"),
3485
+ makeCrossOriginSheet("throws").sheet,
3486
+ );
3487
+ setStageVisibility(replayDocument, { toast: "hidden" });
3488
+ defineAnimations(replayDocument, (): Array<unknown> => {
3489
+ return [
3490
+ fakeAnimation({
3491
+ target: toast,
3492
+ keyframes: [{ visibility: "visible" }, { visibility: "hidden" }],
3493
+ }),
3494
+ ];
3495
+ });
3496
+
3497
+ expect(
3498
+ declarationsOf(byId(capture(replayDocument).svg, "toast")),
3499
+ ).toEqual(["visibility: hidden !important"]);
3500
+ });
3501
+
3502
+ /*
3503
+ * Where the clone inlines an element's whole computed style anyway -
3504
+ * flattened shadow content, a modal dialog, a frame's stand-in image -
3505
+ * visibility comes with it, once.
3506
+ */
3507
+ it("leaves visibility to the computed style where the clone inlines it anyway", () => {
3508
+ const replayDocument: Document = makeReplayDocument(
3509
+ `<fancy-card id="card"></fancy-card><dialog id="modal" open><p id="modal-text">Confirm</p></dialog><iframe id="widget"></iframe>`,
3510
+ {
3511
+ head: `<link id="portal" rel="stylesheet" href="https://portal-cdn.example/portal.css">`,
3512
+ },
3513
+ );
3514
+ const root: ShadowRoot = attachShadow(
3515
+ liveById(replayDocument, "card"),
3516
+ `<p id="shadow-tip">Tip</p>`,
3517
+ );
3518
+
3519
+ giveSheet(
3520
+ liveById(replayDocument, "portal"),
3521
+ makeCrossOriginSheet("throws").sheet,
3522
+ );
3523
+ makeModal(liveById(replayDocument, "modal"));
3524
+ overrideComputedStyle(root.getElementById("shadow-tip") as Element, {
3525
+ visibility: "hidden",
3526
+ });
3527
+ setStageVisibility(replayDocument, {
3528
+ modal: "hidden",
3529
+ "modal-text": "visible",
3530
+ widget: "hidden",
3531
+ });
3532
+
3533
+ const svg: Document = capture(replayDocument).svg;
3534
+ const count: (id: string, declaration: string) => number = (
3535
+ id: string,
3536
+ declaration: string,
3537
+ ): number => {
3538
+ return declarationsOf(byId(svg, id)).filter(
3539
+ (candidate: string): boolean => {
3540
+ return candidate === declaration;
3541
+ },
3542
+ ).length;
3543
+ };
3544
+
3545
+ expect(count("shadow-tip", "visibility: hidden !important")).toBe(1);
3546
+ expect(count("modal", "visibility: hidden !important")).toBe(1);
3547
+ expect(count("modal-text", "visibility: visible !important")).toBe(1);
3548
+ expect(count("widget", "visibility: hidden !important")).toBe(1);
3549
+ /* The shadow host shows, and its computed style says so by omission. */
3550
+ expect(styleOf(byId(svg, "card"))).not.toContain("visibility");
3551
+ });
3552
+ });
3553
+
2691
3554
  /*
2692
3555
  * The image answers some media features differently from the stage
2693
3556
  * ((hover) and (pointer) are false inside it in Chromium and WebKit),
@@ -2991,24 +3854,161 @@ describe("serializeReplayFrame", () => {
2991
3854
  }
2992
3855
  });
2993
3856
 
2994
- it("draws an image the stage never loaded as broken", () => {
3857
+ /*
3858
+ * Right after a seek or a rebuild the stage is still fetching the
3859
+ * page's images and has drawn nothing where they go. A broken icon and
3860
+ * alt text there would put in the picture what was never on the stage.
3861
+ */
3862
+ it("leaves an image the stage is still loading transparent, in the box the stage laid it out in", () => {
3863
+ const canvas: ImageCanvasMock = mockImageCanvas({
3864
+ hasContext: true,
3865
+ dataUrl: "data:image/png;base64,Q09QSUVE",
3866
+ });
2995
3867
  const replayDocument: Document = makeReplayDocument(
2996
- `<img id="pending" src="https://cdn.example/a.png"><img id="empty" src="https://cdn.example/b.png">`,
3868
+ `<img id="pending" src="https://cdn.example/logo.png" alt="Logo" style="width: 120px; height: 40px">` +
3869
+ `<img id="lazy" src="https://cdn.example/banner.png" loading="lazy" alt="Banner">` +
3870
+ `<img id="progressive" src="https://cdn.example/photo.jpg" alt="Photo" style="width: 320px; height: 240px">`,
2997
3871
  );
2998
3872
 
2999
- markImageLoaded(liveById(replayDocument, "empty"), {
3000
- width: 0,
3001
- height: 0,
3873
+ markImageLoading(liveById(replayDocument, "pending"));
3874
+ markImageLoading(liveById(replayDocument, "lazy"));
3875
+ /* Its header has decoded, so it has a size, but it is not complete. */
3876
+ markImageLoading(liveById(replayDocument, "progressive"), {
3877
+ width: 640,
3878
+ height: 480,
3002
3879
  });
3003
3880
 
3004
3881
  const captured: CapturedFrame = capture(replayDocument);
3882
+ const pending: Element = byId(captured.svg, "pending");
3883
+ const lazy: Element = byId(captured.svg, "lazy");
3884
+ const progressive: Element = byId(captured.svg, "progressive");
3005
3885
 
3006
- expect(byId(captured.svg, "pending").getAttribute("src")).toBe(
3007
- REPLAY_FRAME_BROKEN_IMAGE,
3886
+ for (const image of [pending, lazy, progressive]) {
3887
+ expect(image.getAttribute("src")).toBe(REPLAY_FRAME_TRANSPARENT_IMAGE);
3888
+ }
3889
+
3890
+ expect(declarationsOf(pending)).toEqual([
3891
+ "width: 120px",
3892
+ "height: 40px",
3893
+ "width: 120px !important",
3894
+ "height: 40px !important",
3895
+ "object-fit: fill !important",
3896
+ ]);
3897
+ expect(styleOf(progressive)).toContain("width: 320px !important");
3898
+ expect(styleOf(progressive)).toContain("height: 240px !important");
3899
+ /* jsdom lays nothing out; a browser reports the used size here. */
3900
+ expect(styleOf(lazy)).toBe("object-fit: fill !important");
3901
+ expect(lazy.hasAttribute("loading")).toBe(false);
3902
+ /* Nothing is read from an image that has not finished. */
3903
+ expect(canvas.draws).toEqual([]);
3904
+ });
3905
+
3906
+ /*
3907
+ * A KNOWN LIMIT, pinned so that changing it is a decision - this is not
3908
+ * the picture we would want. A recorded mutation changed the src of an
3909
+ * image that had loaded (a carousel's next slide), or a lazy loader put
3910
+ * the real file in place of a data: placeholder, and the new file is
3911
+ * still on its way. The stage keeps painting the previous image until
3912
+ * it arrives, but Chromium reports what a first load reports - not
3913
+ * complete, no natural size, and a currentSrc of '' - so the capture
3914
+ * cannot tell the two apart and leaves a transparent hole where the
3915
+ * stage shows a picture.
3916
+ */
3917
+ it("leaves an image whose src just changed transparent, although the stage still shows its previous image (a known limit)", () => {
3918
+ const canvas: ImageCanvasMock = mockImageCanvas({
3919
+ hasContext: true,
3920
+ dataUrl: "data:image/png;base64,Q09QSUVE",
3921
+ });
3922
+ const replayDocument: Document = makeReplayDocument(
3923
+ `<img id="slide" src="https://cdn.example/slide-1.jpg" alt="Slide" style="width: 320px; height: 180px">` +
3924
+ `<img id="photo" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Photo" style="width: 200px; height: 150px">`,
3925
+ );
3926
+ const slide: HTMLElement = liveById(replayDocument, "slide");
3927
+ const photo: HTMLElement = liveById(replayDocument, "photo");
3928
+
3929
+ /* The mutations, and what Chromium reports while the new files load. */
3930
+ slide.setAttribute("src", "https://cdn.example/slide-2.jpg");
3931
+ photo.setAttribute("src", "https://cdn.example/photo.jpg");
3932
+
3933
+ for (const image of [slide, photo]) {
3934
+ markImageLoading(image);
3935
+ Object.defineProperty(image, "currentSrc", {
3936
+ configurable: true,
3937
+ get: (): string => {
3938
+ return "";
3939
+ },
3940
+ });
3941
+ }
3942
+
3943
+ const captured: CapturedFrame = capture(replayDocument);
3944
+
3945
+ for (const id of ["slide", "photo"]) {
3946
+ const image: Element = byId(captured.svg, id);
3947
+
3948
+ expect(image.getAttribute("src")).toBe(REPLAY_FRAME_TRANSPARENT_IMAGE);
3949
+ expect(styleOf(image)).toContain("object-fit: fill !important");
3950
+ }
3951
+
3952
+ expect(styleOf(byId(captured.svg, "slide"))).toContain(
3953
+ "width: 320px !important",
3008
3954
  );
3009
- expect(byId(captured.svg, "empty").getAttribute("src")).toBe(
3010
- REPLAY_FRAME_BROKEN_IMAGE,
3955
+ expect(styleOf(byId(captured.svg, "photo"))).toContain(
3956
+ "height: 150px !important",
3011
3957
  );
3958
+ /* Not the placeholder either: currentSrc no longer names it. */
3959
+ expect(captured.serialization.svg).not.toContain("R0lGODlhAQABAIAAAP");
3960
+ expect(captured.serialization.svg).not.toContain("cdn.example");
3961
+ expect(canvas.draws).toEqual([]);
3962
+ });
3963
+
3964
+ /*
3965
+ * Firefox keeps the previous currentSrc until the new file arrives, so
3966
+ * there a data: placeholder being replaced is still drawn, as the stage
3967
+ * draws it.
3968
+ */
3969
+ it("keeps a data: placeholder the engine still reports as currentSrc while its replacement loads", () => {
3970
+ const placeholder: string =
3971
+ "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
3972
+ const replayDocument: Document = makeReplayDocument(
3973
+ `<img id="photo" src="https://cdn.example/photo.jpg" alt="Photo">`,
3974
+ );
3975
+ const photo: HTMLElement = liveById(replayDocument, "photo");
3976
+
3977
+ markImageLoading(photo, { width: 1, height: 1 });
3978
+ Object.defineProperty(photo, "currentSrc", {
3979
+ configurable: true,
3980
+ get: (): string => {
3981
+ return placeholder;
3982
+ },
3983
+ });
3984
+
3985
+ expect(
3986
+ byId(capture(replayDocument).svg, "photo").getAttribute("src"),
3987
+ ).toBe(placeholder);
3988
+ });
3989
+
3990
+ /*
3991
+ * An image that finished with nothing to show failed on the stage too
3992
+ * - it needs the user's sign-in, its site refuses other sites, it has
3993
+ * gone, or it was never loaded under Mask all text - and the stage
3994
+ * draws it broken, with its alt text.
3995
+ */
3996
+ it("draws an image whose load failed as broken, as the stage does", () => {
3997
+ const replayDocument: Document = makeReplayDocument(
3998
+ `<img id="failed" src="https://cdn.example/logo.png" alt="Company logo" style="width: 120px; height: 40px">`,
3999
+ );
4000
+
4001
+ markImageLoaded(liveById(replayDocument, "failed"), {
4002
+ width: 0,
4003
+ height: 0,
4004
+ });
4005
+
4006
+ const image: Element = byId(capture(replayDocument).svg, "failed");
4007
+
4008
+ expect(image.getAttribute("src")).toBe(REPLAY_FRAME_BROKEN_IMAGE);
4009
+ expect(image.getAttribute("alt")).toBe("Company logo");
4010
+ /* Laid out by its own declarations, as on the stage: nothing pinned. */
4011
+ expect(styleOf(image)).toBe("width: 120px; height: 40px");
3012
4012
  });
3013
4013
 
3014
4014
  it("copies the pixels of a loaded image the browser lets it read", () => {