@oneuptime/common 13.0.1 → 13.0.4

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 (781) hide show
  1. package/Models/AnalyticsModels/RumSession.ts +34 -0
  2. package/Models/DatabaseModels/AlertEpisodeLabelRule.ts +2 -2
  3. package/Models/DatabaseModels/AlertEpisodeOnCallRule.ts +2 -2
  4. package/Models/DatabaseModels/AlertEpisodeOwnerRule.ts +2 -2
  5. package/Models/DatabaseModels/AlertEpisodePrivacyRule.ts +2 -2
  6. package/Models/DatabaseModels/AlertGroupingRule.ts +2 -2
  7. package/Models/DatabaseModels/AlertLabelRule.ts +2 -2
  8. package/Models/DatabaseModels/AlertOnCallRule.ts +2 -2
  9. package/Models/DatabaseModels/AlertOwnerRule.ts +2 -2
  10. package/Models/DatabaseModels/AlertPrivacyRule.ts +2 -2
  11. package/Models/DatabaseModels/AlertReminderRule.ts +6 -4
  12. package/Models/DatabaseModels/AutoRemediationRule.ts +2 -2
  13. package/Models/DatabaseModels/CephClusterLabelRule.ts +2 -2
  14. package/Models/DatabaseModels/CephClusterOwnerRule.ts +2 -2
  15. package/Models/DatabaseModels/CloudResourceLabelRule.ts +2 -2
  16. package/Models/DatabaseModels/CloudResourceOwnerRule.ts +2 -2
  17. package/Models/DatabaseModels/DashboardLabelRule.ts +2 -2
  18. package/Models/DatabaseModels/DashboardOwnerRule.ts +2 -2
  19. package/Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel.ts +11 -0
  20. package/Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel.ts +44 -0
  21. package/Models/DatabaseModels/DockerHostLabelRule.ts +2 -2
  22. package/Models/DatabaseModels/DockerHostOwnerRule.ts +2 -2
  23. package/Models/DatabaseModels/DockerSwarmClusterLabelRule.ts +2 -2
  24. package/Models/DatabaseModels/DockerSwarmClusterOwnerRule.ts +2 -2
  25. package/Models/DatabaseModels/HostLabelRule.ts +2 -2
  26. package/Models/DatabaseModels/HostOwnerRule.ts +2 -2
  27. package/Models/DatabaseModels/IncidentEpisodeLabelRule.ts +2 -2
  28. package/Models/DatabaseModels/IncidentEpisodeOnCallRule.ts +2 -2
  29. package/Models/DatabaseModels/IncidentEpisodeOwnerRule.ts +2 -2
  30. package/Models/DatabaseModels/IncidentEpisodePrivacyRule.ts +2 -2
  31. package/Models/DatabaseModels/IncidentGroupingRule.ts +2 -2
  32. package/Models/DatabaseModels/IncidentLabelRule.ts +2 -2
  33. package/Models/DatabaseModels/IncidentOnCallRule.ts +2 -2
  34. package/Models/DatabaseModels/IncidentOwnerRule.ts +2 -2
  35. package/Models/DatabaseModels/IncidentPrivacyRule.ts +2 -2
  36. package/Models/DatabaseModels/IncidentReminderRule.ts +6 -4
  37. package/Models/DatabaseModels/IncidentSlaRule.ts +2 -2
  38. package/Models/DatabaseModels/IncomingCallPolicy.ts +3 -4
  39. package/Models/DatabaseModels/IncomingCallPolicyLabelRule.ts +2 -2
  40. package/Models/DatabaseModels/IncomingCallPolicyOwnerRule.ts +2 -2
  41. package/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.ts +254 -0
  42. package/Models/DatabaseModels/Index.ts +2 -0
  43. package/Models/DatabaseModels/IoTFleetLabelRule.ts +2 -2
  44. package/Models/DatabaseModels/IoTFleetOwnerRule.ts +2 -2
  45. package/Models/DatabaseModels/KubernetesClusterLabelRule.ts +2 -2
  46. package/Models/DatabaseModels/KubernetesClusterOwnerRule.ts +2 -2
  47. package/Models/DatabaseModels/MonitorLabelRule.ts +2 -2
  48. package/Models/DatabaseModels/MonitorOwnerRule.ts +2 -2
  49. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +2 -2
  50. package/Models/DatabaseModels/NetworkDeviceLabelRule.ts +2 -2
  51. package/Models/DatabaseModels/NetworkDeviceOwnerRule.ts +2 -2
  52. package/Models/DatabaseModels/NetworkSiteAssignmentRule.ts +2 -2
  53. package/Models/DatabaseModels/OnCallDutyPolicyLabelRule.ts +2 -2
  54. package/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.ts +2 -2
  55. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.ts +2 -2
  56. package/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.ts +2 -2
  57. package/Models/DatabaseModels/PodmanHostLabelRule.ts +2 -2
  58. package/Models/DatabaseModels/PodmanHostOwnerRule.ts +2 -2
  59. package/Models/DatabaseModels/ProxmoxClusterLabelRule.ts +2 -2
  60. package/Models/DatabaseModels/ProxmoxClusterOwnerRule.ts +2 -2
  61. package/Models/DatabaseModels/RumApplicationLabelRule.ts +2 -2
  62. package/Models/DatabaseModels/RumApplicationOwnerRule.ts +2 -2
  63. package/Models/DatabaseModels/RunbookLabelRule.ts +2 -2
  64. package/Models/DatabaseModels/RunbookOwnerRule.ts +2 -2
  65. package/Models/DatabaseModels/RunbookRule.ts +2 -2
  66. package/Models/DatabaseModels/ScheduledMaintenanceLabelRule.ts +2 -2
  67. package/Models/DatabaseModels/ScheduledMaintenanceOwnerRule.ts +2 -2
  68. package/Models/DatabaseModels/ScheduledMaintenanceReminderRule.ts +6 -4
  69. package/Models/DatabaseModels/ServerlessFunctionLabelRule.ts +2 -2
  70. package/Models/DatabaseModels/ServerlessFunctionOwnerRule.ts +2 -2
  71. package/Models/DatabaseModels/ServiceLabelRule.ts +2 -2
  72. package/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.ts +283 -4
  73. package/Models/DatabaseModels/ServiceOwnerRule.ts +2 -2
  74. package/Models/DatabaseModels/StatusPageLabelRule.ts +2 -2
  75. package/Models/DatabaseModels/StatusPageMonitorRule.ts +2 -2
  76. package/Models/DatabaseModels/StatusPageOwnerRule.ts +2 -2
  77. package/Models/DatabaseModels/VMwareVCenterLabelRule.ts +2 -2
  78. package/Models/DatabaseModels/VMwareVCenterOwnerRule.ts +2 -2
  79. package/Models/DatabaseModels/WorkflowLabelRule.ts +2 -2
  80. package/Models/DatabaseModels/WorkflowOwnerRule.ts +2 -2
  81. package/Server/API/TelemetryAPI.ts +323 -2
  82. package/Server/Infrastructure/Postgres/SchemaMigrations/1792400000000-AddSloBurnRateRuleIncidents.ts +111 -0
  83. package/Server/Infrastructure/Postgres/SchemaMigrations/1792500000000-AddConfigurableRuleCriteria.ts +644 -0
  84. package/Server/Infrastructure/Postgres/SchemaMigrations/1792600000000-AddIncomingCallPolicyPhoneNumbers.ts +72 -0
  85. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  86. package/Server/Middleware/MultipartFormData.ts +46 -21
  87. package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +21 -0
  88. package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +21 -0
  89. package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +21 -0
  90. package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +21 -0
  91. package/Server/Services/AlertGroupingEngineService.ts +27 -0
  92. package/Server/Services/AlertLabelRuleEngineService.ts +27 -0
  93. package/Server/Services/AlertOnCallRuleEngineService.ts +27 -0
  94. package/Server/Services/AlertOwnerRuleEngineService.ts +27 -0
  95. package/Server/Services/AlertPrivacyRuleEngineService.ts +27 -0
  96. package/Server/Services/AlertReminderRuleService.ts +65 -33
  97. package/Server/Services/AutoRemediationRuleEngineService.ts +90 -8
  98. package/Server/Services/CephClusterLabelRuleEngineService.ts +20 -0
  99. package/Server/Services/CephClusterOwnerRuleEngineService.ts +20 -0
  100. package/Server/Services/CloudResourceLabelRuleEngineService.ts +20 -0
  101. package/Server/Services/CloudResourceOwnerRuleEngineService.ts +20 -0
  102. package/Server/Services/DashboardLabelRuleEngineService.ts +20 -0
  103. package/Server/Services/DashboardOwnerRuleEngineService.ts +20 -0
  104. package/Server/Services/DatabaseService.ts +409 -6
  105. package/Server/Services/DockerHostLabelRuleEngineService.ts +20 -0
  106. package/Server/Services/DockerHostOwnerRuleEngineService.ts +20 -0
  107. package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +23 -0
  108. package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +23 -0
  109. package/Server/Services/HostLabelRuleEngineService.ts +13 -0
  110. package/Server/Services/HostOwnerRuleEngineService.ts +13 -0
  111. package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +21 -0
  112. package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +21 -0
  113. package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +21 -0
  114. package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +21 -0
  115. package/Server/Services/IncidentGroupingEngineService.ts +64 -13
  116. package/Server/Services/IncidentLabelRuleEngineService.ts +64 -9
  117. package/Server/Services/IncidentOnCallRuleEngineService.ts +64 -10
  118. package/Server/Services/IncidentOwnerRuleEngineService.ts +64 -10
  119. package/Server/Services/IncidentPrivacyRuleEngineService.ts +64 -10
  120. package/Server/Services/IncidentReminderRuleService.ts +66 -34
  121. package/Server/Services/IncidentSlaRuleService.ts +55 -12
  122. package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +20 -0
  123. package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +20 -0
  124. package/Server/Services/IncomingCallPolicyPhoneNumberService.ts +184 -0
  125. package/Server/Services/IncomingCallPolicyService.ts +276 -5
  126. package/Server/Services/Index.ts +2 -0
  127. package/Server/Services/IoTFleetLabelRuleEngineService.ts +20 -0
  128. package/Server/Services/IoTFleetOwnerRuleEngineService.ts +20 -0
  129. package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +23 -0
  130. package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +23 -0
  131. package/Server/Services/LabelService.ts +50 -7
  132. package/Server/Services/MonitorLabelRuleEngineService.ts +20 -0
  133. package/Server/Services/MonitorOwnerRuleEngineService.ts +20 -0
  134. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +2 -0
  135. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +93 -1
  136. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +26 -0
  137. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +25 -0
  138. package/Server/Services/NetworkDeviceService.ts +2 -0
  139. package/Server/Services/NetworkSiteAssignmentRuleService.ts +90 -1
  140. package/Server/Services/NetworkSiteService.ts +242 -29
  141. package/Server/Services/NetworkSiteTypeService.ts +177 -77
  142. package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +20 -0
  143. package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +20 -0
  144. package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +22 -0
  145. package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +22 -0
  146. package/Server/Services/PodmanHostLabelRuleEngineService.ts +20 -0
  147. package/Server/Services/PodmanHostOwnerRuleEngineService.ts +20 -0
  148. package/Server/Services/ProjectCallSMSConfigService.ts +156 -7
  149. package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +25 -0
  150. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +25 -0
  151. package/Server/Services/RumApplicationLabelRuleEngineService.ts +22 -0
  152. package/Server/Services/RumApplicationOwnerRuleEngineService.ts +22 -0
  153. package/Server/Services/RunbookLabelRuleEngineService.ts +20 -0
  154. package/Server/Services/RunbookOwnerRuleEngineService.ts +20 -0
  155. package/Server/Services/RunbookRuleEngineService.ts +18 -1
  156. package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +62 -10
  157. package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +62 -11
  158. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +48 -18
  159. package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +25 -0
  160. package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +25 -0
  161. package/Server/Services/ServiceLabelRuleEngineService.ts +20 -0
  162. package/Server/Services/ServiceLevelObjectiveBurnRateRuleService.ts +675 -12
  163. package/Server/Services/ServiceLevelObjectiveService.ts +55 -13
  164. package/Server/Services/ServiceOwnerRuleEngineService.ts +20 -0
  165. package/Server/Services/StatusPageLabelRuleEngineService.ts +20 -0
  166. package/Server/Services/StatusPageMonitorRuleEngineService.ts +31 -2
  167. package/Server/Services/StatusPageMonitorRuleService.ts +34 -1
  168. package/Server/Services/StatusPageOwnerRuleEngineService.ts +20 -0
  169. package/Server/Services/UserService.ts +23 -50
  170. package/Server/Services/VMwareVCenterLabelRuleEngineService.ts +25 -0
  171. package/Server/Services/VMwareVCenterOwnerRuleEngineService.ts +25 -0
  172. package/Server/Services/WorkflowLabelRuleEngineService.ts +20 -0
  173. package/Server/Services/WorkflowOwnerRuleEngineService.ts +20 -0
  174. package/Server/Static/Brand/README.md +10 -5
  175. package/Server/Static/Brand/favicons/oneuptime-up-v1.svg +8 -0
  176. package/Server/Types/Database/QueryHelper.ts +88 -0
  177. package/Server/Utils/AI/Toolbox/SloTools.ts +19 -2
  178. package/Server/Utils/IncomingCallPhoneNumber.ts +29 -16
  179. package/Server/Utils/Profiling.ts +13 -1
  180. package/Server/Utils/Rules/MonitorRuleCriteriaCache.ts +38 -0
  181. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +498 -0
  182. package/Server/Utils/Telemetry.ts +3 -1
  183. package/Server/Utils/VendorAssets.ts +3 -0
  184. package/Tests/App/Dashboard/AIInsightViewTriage.test.tsx +348 -0
  185. package/Tests/App/Dashboard/ArchiveResourceCard.test.tsx +476 -0
  186. package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +25 -0
  187. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +449 -24
  188. package/Tests/App/Dashboard/DashboardPersistentSideMenuLayouts.test.tsx +702 -0
  189. package/Tests/App/Dashboard/ExceptionsLandingRedirect.test.tsx +63 -0
  190. package/Tests/App/Dashboard/FeedOrdering.test.ts +150 -0
  191. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +279 -100
  192. package/Tests/App/Dashboard/InventoryItemSideMenu.test.tsx +155 -0
  193. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +11 -0
  194. package/Tests/App/Dashboard/PasskeySettings.test.tsx +215 -71
  195. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +347 -0
  196. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +263 -0
  197. package/Tests/App/Dashboard/ResourceFeed.test.tsx +413 -17
  198. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +138 -0
  199. package/Tests/App/Dashboard/SloDashboardTemplateWidgets.test.tsx +940 -0
  200. package/Tests/App/Dashboard/TelemetryIngestionKeyEdit.test.tsx +378 -0
  201. package/Tests/App/Dashboard/TelemetryIngestionKeyForm.test.tsx +735 -0
  202. package/Tests/App/Dashboard/TwoFactorStatus.test.tsx +275 -108
  203. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +13 -3
  204. package/Tests/Models/AnalyticsModels/RumSessionReplayColumns.test.ts +127 -4
  205. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -4
  206. package/Tests/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.test.ts +362 -0
  207. package/Tests/Models/DatabaseModels/RuleBaseModel.test.ts +69 -0
  208. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +181 -0
  209. package/Tests/Server/API/SessionReplayAPI.test.ts +1303 -145
  210. package/Tests/Server/API/UserTwoFactorSelfService.test.ts +407 -0
  211. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +611 -0
  212. package/Tests/Server/Middleware/MultipartFormData.test.ts +61 -3
  213. package/Tests/Server/Middleware/TelemetryIngestionDisabled.test.ts +211 -0
  214. package/Tests/Server/Services/AlertIncidentRuleCriteriaEngineServices.test.ts +651 -0
  215. package/Tests/Server/Services/AutoRemediationRuleEngineService.test.ts +79 -0
  216. package/Tests/Server/Services/DatabaseServiceRelationOnlyRuleRollout.test.ts +330 -0
  217. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +522 -0
  218. package/Tests/Server/Services/IncidentSlaRuleCriteriaCorrelation.test.ts +99 -0
  219. package/Tests/Server/Services/IncomingCallPolicyPhoneNumberService.test.ts +1540 -0
  220. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +179 -2
  221. package/Tests/Server/Services/NetworkSiteAssignmentRuleService.test.ts +170 -0
  222. package/Tests/Server/Services/NetworkSiteService.test.ts +559 -53
  223. package/Tests/Server/Services/NetworkSiteTypeService.test.ts +122 -55
  224. package/Tests/Server/Services/ReminderRuleCriteria.test.ts +173 -0
  225. package/Tests/Server/Services/ResourceRuleCriteriaEngineServices.test.ts +170 -0
  226. package/Tests/Server/Services/ScheduledMaintenanceRuleCriteriaCorrelation.test.ts +154 -0
  227. package/Tests/Server/Services/ServiceLevelObjectiveBurnRateRuleService.test.ts +2450 -470
  228. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +464 -121
  229. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +281 -0
  230. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +132 -0
  231. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +159 -14
  232. package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +6 -8
  233. package/Tests/Server/Services/UserTwoFactorAuthAdmin.test.ts +203 -34
  234. package/Tests/Server/Types/Database/QueryHelperJsonArrayObjects.test.ts +128 -0
  235. package/Tests/Server/Utils/AI/SloTools.test.ts +190 -1
  236. package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +461 -0
  237. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +25 -0
  238. package/Tests/Server/Utils/Billing/BillingFailureNoticeThrottle.test.ts +359 -0
  239. package/Tests/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.test.ts +318 -0
  240. package/Tests/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.test.ts +220 -0
  241. package/Tests/Server/Utils/Dashboard/SloTemplatePublicDashboard.test.ts +1043 -0
  242. package/Tests/Server/Utils/Execute.test.ts +302 -0
  243. package/Tests/Server/Utils/GracefulShutdown.test.ts +565 -0
  244. package/Tests/Server/Utils/IncomingCallPhoneNumber.test.ts +36 -20
  245. package/Tests/Server/Utils/LoggerCore.test.ts +597 -0
  246. package/Tests/Server/Utils/Monitor/MonitorTemplateUtil.test.ts +1109 -0
  247. package/Tests/Server/Utils/Monitor/SloTemplateMetricEmitterContract.test.ts +750 -0
  248. package/Tests/Server/Utils/ProductAnalytics.test.ts +404 -0
  249. package/Tests/Server/Utils/Profiling.test.ts +366 -0
  250. package/Tests/Server/Utils/Rules/MonitorRuleCriteriaCache.test.ts +99 -0
  251. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +558 -0
  252. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +711 -0
  253. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +522 -0
  254. package/Tests/Server/Utils/VendorAssets.test.ts +17 -0
  255. package/Tests/Types/Dashboard/DashboardTemplateEditorialInvariants.test.ts +659 -0
  256. package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +97 -0
  257. package/Tests/Types/Dashboard/SloDashboardTemplate.test.ts +1179 -0
  258. package/Tests/Types/Database/AccessControl/OwnedThrough.test.ts +136 -0
  259. package/Tests/Types/OnCallDutyPolicy/LayerUtilCorePaths.test.ts +1014 -0
  260. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +9 -6
  261. package/Tests/Types/Rules/RuleCriteriaFieldRegistry.test.ts +276 -0
  262. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +180 -1
  263. package/Tests/Types/Telemetry/LlmConventions.test.ts +495 -0
  264. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +13 -6
  265. package/Tests/UI/Components/Alert.test.tsx +532 -93
  266. package/Tests/UI/Components/Feed.test.tsx +241 -0
  267. package/Tests/UI/Components/Forms/BasicFormStepNavigation.test.tsx +368 -0
  268. package/Tests/UI/Components/Forms/FormSummary.test.tsx +454 -0
  269. package/Tests/UI/Components/Forms/ModelFormJsonColumns.test.tsx +290 -0
  270. package/Tests/UI/Components/Forms/ModelFormRuleCriteria.test.tsx +331 -0
  271. package/Tests/UI/Components/IconOpticalSize.test.tsx +79 -31
  272. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +6 -6
  273. package/Tests/UI/Components/NavBar.test.tsx +91 -2
  274. package/Tests/UI/Components/PencilButton.test.tsx +211 -0
  275. package/Tests/UI/Components/RuleCriteria/RuleCriteriaBuilder.test.tsx +510 -0
  276. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +350 -0
  277. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +50 -0
  278. package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +329 -0
  279. package/Tests/UI/Components/useFeedItems.test.tsx +107 -0
  280. package/Tests/UI/Rum/ChunkLoader.test.ts +401 -0
  281. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +1 -1
  282. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +7 -1
  283. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +2 -0
  284. package/Tests/UI/Rum/ReplayEngine.test.ts +1069 -5
  285. package/Tests/UI/Rum/ReplayHeader.test.tsx +579 -2
  286. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +23 -5
  287. package/Tests/UI/Rum/ReplayRail.test.tsx +143 -1
  288. package/Tests/UI/Rum/ReplayRenderBudget.test.tsx +338 -0
  289. package/Tests/UI/Rum/ReplaySignals.test.ts +179 -0
  290. package/Tests/UI/Rum/ReplayStage.test.tsx +210 -3
  291. package/Tests/UI/Rum/ReplayTimeline.test.tsx +180 -1
  292. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +405 -0
  293. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +13 -16
  294. package/Tests/UI/Rum/SessionReplayFacets.test.tsx +199 -0
  295. package/Tests/UI/Rum/SessionReplaySearchBar.test.tsx +58 -21
  296. package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +2 -0
  297. package/Tests/UI/Rum/SessionReplayTable.test.tsx +717 -4
  298. package/Tests/UI/Rum/SessionReplayUsersPage.test.tsx +435 -0
  299. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +577 -0
  300. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +1 -1
  301. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -1
  302. package/Tests/UI/Utils/LabelRuleImportExport.test.ts +192 -0
  303. package/Tests/UI/Utils/Navigation.test.ts +30 -0
  304. package/Tests/Utils/Dashboard/SloTemplateVariableScoping.test.ts +880 -0
  305. package/Tests/Utils/LabelRuleImportExport.test.ts +158 -0
  306. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +93 -0
  307. package/Tests/Utils/NetworkSite/CidrMatchUtil.test.ts +138 -0
  308. package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +174 -0
  309. package/Tests/Utils/Rules/RuleCriteriaMatcher.test.ts +1424 -0
  310. package/Tests/Utils/Rum/SessionIdentity.test.ts +72 -0
  311. package/Tests/Utils/Rum/SessionReplayHealth.test.ts +884 -0
  312. package/Tests/Utils/Slo/SloBurnRateRuleState.test.ts +374 -28
  313. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +118 -0
  314. package/Types/Dashboard/DashboardTemplates.ts +465 -0
  315. package/Types/OnCallDutyPolicy/Layer.ts +12 -0
  316. package/Types/Rules/RuleCriteria.ts +51 -0
  317. package/Types/Rules/RuleCriteriaFieldRegistry.ts +449 -0
  318. package/Types/Rum/SessionReplay.ts +75 -0
  319. package/Types/Rum/SessionReplayApi.ts +93 -0
  320. package/UI/Components/Alerts/Alert.tsx +102 -72
  321. package/UI/Components/Button/Button.tsx +4 -1
  322. package/UI/Components/Dropdown/Dropdown.tsx +2 -0
  323. package/UI/Components/Feed/Feed.tsx +15 -0
  324. package/UI/Components/Feed/useFeedItems.ts +227 -0
  325. package/UI/Components/Forms/BasicForm.tsx +42 -21
  326. package/UI/Components/Forms/ModelForm.tsx +80 -1
  327. package/UI/Components/Icon/Icon.tsx +3 -35
  328. package/UI/Components/ModelTable/ModelTable.tsx +19 -0
  329. package/UI/Components/Navbar/NavBar.tsx +1 -0
  330. package/UI/Components/RuleCriteria/RuleCriteriaBuilder.tsx +735 -0
  331. package/UI/Components/RuleCriteria/RuleCriteriaModelForm.tsx +209 -0
  332. package/UI/Components/RuleCriteria/RuleCriteriaModelTable.tsx +205 -0
  333. package/UI/Components/RuleCriteria/RuleCriteriaSummary.tsx +123 -0
  334. package/UI/Components/Table/Table.tsx +3 -0
  335. package/UI/Components/Table/TableBody.tsx +3 -0
  336. package/UI/Components/Table/TableRow.tsx +8 -2
  337. package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +14 -2
  338. package/UI/Styles/Theme.css +1 -0
  339. package/UI/Utils/LabelRuleImportExport.ts +114 -6
  340. package/UI/Utils/Navigation.ts +1 -2
  341. package/UI/esbuild-config.js +46 -0
  342. package/Utils/Faker.ts +93 -6
  343. package/Utils/LabelRuleImportExport.ts +295 -2
  344. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +108 -8
  345. package/Utils/NetworkSite/CidrMatchUtil.ts +102 -0
  346. package/Utils/NetworkSite/TypeHierarchyUtil.ts +152 -2
  347. package/Utils/Rules/RuleCriteriaMatcher.ts +931 -0
  348. package/Utils/Rum/SessionIdentity.ts +16 -0
  349. package/Utils/Slo/SloBurnRateRuleState.ts +87 -33
  350. package/Utils/Telemetry/OriginAllowList.ts +74 -0
  351. package/build/dist/Models/AnalyticsModels/RumSession.js +32 -0
  352. package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -1
  353. package/build/dist/Models/DatabaseModels/AlertEpisodeLabelRule.js +2 -2
  354. package/build/dist/Models/DatabaseModels/AlertEpisodeLabelRule.js.map +1 -1
  355. package/build/dist/Models/DatabaseModels/AlertEpisodeOnCallRule.js +2 -2
  356. package/build/dist/Models/DatabaseModels/AlertEpisodeOnCallRule.js.map +1 -1
  357. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerRule.js +2 -2
  358. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerRule.js.map +1 -1
  359. package/build/dist/Models/DatabaseModels/AlertEpisodePrivacyRule.js +2 -2
  360. package/build/dist/Models/DatabaseModels/AlertEpisodePrivacyRule.js.map +1 -1
  361. package/build/dist/Models/DatabaseModels/AlertGroupingRule.js +2 -2
  362. package/build/dist/Models/DatabaseModels/AlertGroupingRule.js.map +1 -1
  363. package/build/dist/Models/DatabaseModels/AlertLabelRule.js +2 -2
  364. package/build/dist/Models/DatabaseModels/AlertLabelRule.js.map +1 -1
  365. package/build/dist/Models/DatabaseModels/AlertOnCallRule.js +2 -2
  366. package/build/dist/Models/DatabaseModels/AlertOnCallRule.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/AlertOwnerRule.js +2 -2
  368. package/build/dist/Models/DatabaseModels/AlertOwnerRule.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/AlertPrivacyRule.js +2 -2
  370. package/build/dist/Models/DatabaseModels/AlertPrivacyRule.js.map +1 -1
  371. package/build/dist/Models/DatabaseModels/AlertReminderRule.js +6 -5
  372. package/build/dist/Models/DatabaseModels/AlertReminderRule.js.map +1 -1
  373. package/build/dist/Models/DatabaseModels/AutoRemediationRule.js +2 -2
  374. package/build/dist/Models/DatabaseModels/AutoRemediationRule.js.map +1 -1
  375. package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js +2 -2
  376. package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js.map +1 -1
  377. package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js +2 -2
  378. package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js.map +1 -1
  379. package/build/dist/Models/DatabaseModels/CloudResourceLabelRule.js +2 -2
  380. package/build/dist/Models/DatabaseModels/CloudResourceLabelRule.js.map +1 -1
  381. package/build/dist/Models/DatabaseModels/CloudResourceOwnerRule.js +2 -2
  382. package/build/dist/Models/DatabaseModels/CloudResourceOwnerRule.js.map +1 -1
  383. package/build/dist/Models/DatabaseModels/DashboardLabelRule.js +2 -2
  384. package/build/dist/Models/DatabaseModels/DashboardLabelRule.js.map +1 -1
  385. package/build/dist/Models/DatabaseModels/DashboardOwnerRule.js +2 -2
  386. package/build/dist/Models/DatabaseModels/DashboardOwnerRule.js.map +1 -1
  387. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel.js +12 -0
  388. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel.js.map +1 -0
  389. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel.js +50 -0
  390. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel.js.map +1 -0
  391. package/build/dist/Models/DatabaseModels/DockerHostLabelRule.js +2 -2
  392. package/build/dist/Models/DatabaseModels/DockerHostLabelRule.js.map +1 -1
  393. package/build/dist/Models/DatabaseModels/DockerHostOwnerRule.js +2 -2
  394. package/build/dist/Models/DatabaseModels/DockerHostOwnerRule.js.map +1 -1
  395. package/build/dist/Models/DatabaseModels/DockerSwarmClusterLabelRule.js +2 -2
  396. package/build/dist/Models/DatabaseModels/DockerSwarmClusterLabelRule.js.map +1 -1
  397. package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerRule.js +2 -2
  398. package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerRule.js.map +1 -1
  399. package/build/dist/Models/DatabaseModels/HostLabelRule.js +2 -2
  400. package/build/dist/Models/DatabaseModels/HostLabelRule.js.map +1 -1
  401. package/build/dist/Models/DatabaseModels/HostOwnerRule.js +2 -2
  402. package/build/dist/Models/DatabaseModels/HostOwnerRule.js.map +1 -1
  403. package/build/dist/Models/DatabaseModels/IncidentEpisodeLabelRule.js +2 -2
  404. package/build/dist/Models/DatabaseModels/IncidentEpisodeLabelRule.js.map +1 -1
  405. package/build/dist/Models/DatabaseModels/IncidentEpisodeOnCallRule.js +2 -2
  406. package/build/dist/Models/DatabaseModels/IncidentEpisodeOnCallRule.js.map +1 -1
  407. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerRule.js +2 -2
  408. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerRule.js.map +1 -1
  409. package/build/dist/Models/DatabaseModels/IncidentEpisodePrivacyRule.js +2 -2
  410. package/build/dist/Models/DatabaseModels/IncidentEpisodePrivacyRule.js.map +1 -1
  411. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +2 -2
  412. package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
  413. package/build/dist/Models/DatabaseModels/IncidentLabelRule.js +2 -2
  414. package/build/dist/Models/DatabaseModels/IncidentLabelRule.js.map +1 -1
  415. package/build/dist/Models/DatabaseModels/IncidentOnCallRule.js +2 -2
  416. package/build/dist/Models/DatabaseModels/IncidentOnCallRule.js.map +1 -1
  417. package/build/dist/Models/DatabaseModels/IncidentOwnerRule.js +2 -2
  418. package/build/dist/Models/DatabaseModels/IncidentOwnerRule.js.map +1 -1
  419. package/build/dist/Models/DatabaseModels/IncidentPrivacyRule.js +2 -2
  420. package/build/dist/Models/DatabaseModels/IncidentPrivacyRule.js.map +1 -1
  421. package/build/dist/Models/DatabaseModels/IncidentReminderRule.js +6 -5
  422. package/build/dist/Models/DatabaseModels/IncidentReminderRule.js.map +1 -1
  423. package/build/dist/Models/DatabaseModels/IncidentSlaRule.js +2 -2
  424. package/build/dist/Models/DatabaseModels/IncidentSlaRule.js.map +1 -1
  425. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +3 -4
  426. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
  427. package/build/dist/Models/DatabaseModels/IncomingCallPolicyLabelRule.js +2 -2
  428. package/build/dist/Models/DatabaseModels/IncomingCallPolicyLabelRule.js.map +1 -1
  429. package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerRule.js +2 -2
  430. package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerRule.js.map +1 -1
  431. package/build/dist/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.js +277 -0
  432. package/build/dist/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.js.map +1 -0
  433. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  434. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  435. package/build/dist/Models/DatabaseModels/IoTFleetLabelRule.js +2 -2
  436. package/build/dist/Models/DatabaseModels/IoTFleetLabelRule.js.map +1 -1
  437. package/build/dist/Models/DatabaseModels/IoTFleetOwnerRule.js +2 -2
  438. package/build/dist/Models/DatabaseModels/IoTFleetOwnerRule.js.map +1 -1
  439. package/build/dist/Models/DatabaseModels/KubernetesClusterLabelRule.js +2 -2
  440. package/build/dist/Models/DatabaseModels/KubernetesClusterLabelRule.js.map +1 -1
  441. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerRule.js +2 -2
  442. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerRule.js.map +1 -1
  443. package/build/dist/Models/DatabaseModels/MonitorLabelRule.js +2 -2
  444. package/build/dist/Models/DatabaseModels/MonitorLabelRule.js.map +1 -1
  445. package/build/dist/Models/DatabaseModels/MonitorOwnerRule.js +2 -2
  446. package/build/dist/Models/DatabaseModels/MonitorOwnerRule.js.map +1 -1
  447. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +2 -2
  448. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  449. package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js +2 -2
  450. package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js.map +1 -1
  451. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js +2 -2
  452. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js.map +1 -1
  453. package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js +2 -2
  454. package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js.map +1 -1
  455. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyLabelRule.js +2 -2
  456. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyLabelRule.js.map +1 -1
  457. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.js +2 -2
  458. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.js.map +1 -1
  459. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.js +2 -2
  460. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.js.map +1 -1
  461. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.js +2 -2
  462. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.js.map +1 -1
  463. package/build/dist/Models/DatabaseModels/PodmanHostLabelRule.js +2 -2
  464. package/build/dist/Models/DatabaseModels/PodmanHostLabelRule.js.map +1 -1
  465. package/build/dist/Models/DatabaseModels/PodmanHostOwnerRule.js +2 -2
  466. package/build/dist/Models/DatabaseModels/PodmanHostOwnerRule.js.map +1 -1
  467. package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js +2 -2
  468. package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js.map +1 -1
  469. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js +2 -2
  470. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js.map +1 -1
  471. package/build/dist/Models/DatabaseModels/RumApplicationLabelRule.js +2 -2
  472. package/build/dist/Models/DatabaseModels/RumApplicationLabelRule.js.map +1 -1
  473. package/build/dist/Models/DatabaseModels/RumApplicationOwnerRule.js +2 -2
  474. package/build/dist/Models/DatabaseModels/RumApplicationOwnerRule.js.map +1 -1
  475. package/build/dist/Models/DatabaseModels/RunbookLabelRule.js +2 -2
  476. package/build/dist/Models/DatabaseModels/RunbookLabelRule.js.map +1 -1
  477. package/build/dist/Models/DatabaseModels/RunbookOwnerRule.js +2 -2
  478. package/build/dist/Models/DatabaseModels/RunbookOwnerRule.js.map +1 -1
  479. package/build/dist/Models/DatabaseModels/RunbookRule.js +2 -2
  480. package/build/dist/Models/DatabaseModels/RunbookRule.js.map +1 -1
  481. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceLabelRule.js +2 -2
  482. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceLabelRule.js.map +1 -1
  483. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerRule.js +2 -2
  484. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerRule.js.map +1 -1
  485. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js +6 -5
  486. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js.map +1 -1
  487. package/build/dist/Models/DatabaseModels/ServerlessFunctionLabelRule.js +2 -2
  488. package/build/dist/Models/DatabaseModels/ServerlessFunctionLabelRule.js.map +1 -1
  489. package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerRule.js +2 -2
  490. package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerRule.js.map +1 -1
  491. package/build/dist/Models/DatabaseModels/ServiceLabelRule.js +2 -2
  492. package/build/dist/Models/DatabaseModels/ServiceLabelRule.js.map +1 -1
  493. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js +281 -4
  494. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js.map +1 -1
  495. package/build/dist/Models/DatabaseModels/ServiceOwnerRule.js +2 -2
  496. package/build/dist/Models/DatabaseModels/ServiceOwnerRule.js.map +1 -1
  497. package/build/dist/Models/DatabaseModels/StatusPageLabelRule.js +2 -2
  498. package/build/dist/Models/DatabaseModels/StatusPageLabelRule.js.map +1 -1
  499. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +2 -2
  500. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -1
  501. package/build/dist/Models/DatabaseModels/StatusPageOwnerRule.js +2 -2
  502. package/build/dist/Models/DatabaseModels/StatusPageOwnerRule.js.map +1 -1
  503. package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js +2 -2
  504. package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js.map +1 -1
  505. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js +2 -2
  506. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js.map +1 -1
  507. package/build/dist/Models/DatabaseModels/WorkflowLabelRule.js +2 -2
  508. package/build/dist/Models/DatabaseModels/WorkflowLabelRule.js.map +1 -1
  509. package/build/dist/Models/DatabaseModels/WorkflowOwnerRule.js +2 -2
  510. package/build/dist/Models/DatabaseModels/WorkflowOwnerRule.js.map +1 -1
  511. package/build/dist/Server/API/TelemetryAPI.js +211 -4
  512. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  513. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792400000000-AddSloBurnRateRuleIncidents.js +60 -0
  514. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792400000000-AddSloBurnRateRuleIncidents.js.map +1 -0
  515. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792500000000-AddConfigurableRuleCriteria.js +301 -0
  516. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792500000000-AddConfigurableRuleCriteria.js.map +1 -0
  517. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792600000000-AddIncomingCallPolicyPhoneNumbers.js +30 -0
  518. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792600000000-AddIncomingCallPolicyPhoneNumbers.js.map +1 -0
  519. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  520. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  521. package/build/dist/Server/Middleware/MultipartFormData.js +38 -21
  522. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  523. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +17 -0
  524. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
  525. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +17 -0
  526. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
  527. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +17 -0
  528. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
  529. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +17 -0
  530. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
  531. package/build/dist/Server/Services/AlertGroupingEngineService.js +21 -0
  532. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  533. package/build/dist/Server/Services/AlertLabelRuleEngineService.js +21 -0
  534. package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
  535. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +21 -0
  536. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
  537. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +21 -0
  538. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
  539. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +21 -0
  540. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
  541. package/build/dist/Server/Services/AlertReminderRuleService.js +44 -26
  542. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  543. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +54 -11
  544. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  545. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +16 -0
  546. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
  547. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +16 -0
  548. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
  549. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +16 -0
  550. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
  551. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +16 -0
  552. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
  553. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +16 -0
  554. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
  555. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +16 -0
  556. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
  557. package/build/dist/Server/Services/DatabaseService.js +235 -10
  558. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  559. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +16 -0
  560. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
  561. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +16 -0
  562. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
  563. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +16 -0
  564. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
  565. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +16 -0
  566. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
  567. package/build/dist/Server/Services/HostLabelRuleEngineService.js +12 -0
  568. package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
  569. package/build/dist/Server/Services/HostOwnerRuleEngineService.js +12 -0
  570. package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
  571. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +17 -0
  572. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
  573. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +17 -0
  574. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
  575. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +17 -0
  576. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
  577. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +17 -0
  578. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
  579. package/build/dist/Server/Services/IncidentGroupingEngineService.js +39 -13
  580. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  581. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +39 -9
  582. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
  583. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +39 -10
  584. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
  585. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +39 -10
  586. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
  587. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +39 -10
  588. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
  589. package/build/dist/Server/Services/IncidentReminderRuleService.js +44 -26
  590. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  591. package/build/dist/Server/Services/IncidentSlaRuleService.js +33 -12
  592. package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
  593. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +16 -0
  594. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
  595. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +16 -0
  596. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
  597. package/build/dist/Server/Services/IncomingCallPolicyPhoneNumberService.js +127 -0
  598. package/build/dist/Server/Services/IncomingCallPolicyPhoneNumberService.js.map +1 -0
  599. package/build/dist/Server/Services/IncomingCallPolicyService.js +187 -4
  600. package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
  601. package/build/dist/Server/Services/Index.js +2 -0
  602. package/build/dist/Server/Services/Index.js.map +1 -1
  603. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +16 -0
  604. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
  605. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +16 -0
  606. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
  607. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +16 -0
  608. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
  609. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +16 -0
  610. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
  611. package/build/dist/Server/Services/LabelService.js +39 -7
  612. package/build/dist/Server/Services/LabelService.js.map +1 -1
  613. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +16 -0
  614. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
  615. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +16 -0
  616. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
  617. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +2 -0
  618. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  619. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +57 -1
  620. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  621. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +17 -0
  622. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  623. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +16 -0
  624. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  625. package/build/dist/Server/Services/NetworkDeviceService.js +2 -0
  626. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  627. package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js +56 -1
  628. package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js.map +1 -1
  629. package/build/dist/Server/Services/NetworkSiteService.js +187 -22
  630. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  631. package/build/dist/Server/Services/NetworkSiteTypeService.js +126 -51
  632. package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
  633. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +16 -0
  634. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
  635. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +16 -0
  636. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
  637. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +16 -0
  638. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
  639. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +16 -0
  640. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
  641. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +16 -0
  642. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
  643. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +16 -0
  644. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
  645. package/build/dist/Server/Services/ProjectCallSMSConfigService.js +115 -6
  646. package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
  647. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +16 -0
  648. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
  649. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +16 -0
  650. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
  651. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +16 -0
  652. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
  653. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +16 -0
  654. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
  655. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +16 -0
  656. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
  657. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +16 -0
  658. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
  659. package/build/dist/Server/Services/RunbookRuleEngineService.js +12 -0
  660. package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
  661. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +38 -9
  662. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
  663. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +38 -10
  664. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
  665. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +32 -14
  666. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  667. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +16 -0
  668. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
  669. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +16 -0
  670. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
  671. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +16 -0
  672. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
  673. package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js +554 -12
  674. package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js.map +1 -1
  675. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +50 -14
  676. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  677. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +16 -0
  678. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
  679. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +16 -0
  680. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
  681. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +25 -2
  682. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
  683. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +19 -1
  684. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -1
  685. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +16 -0
  686. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
  687. package/build/dist/Server/Services/UserService.js +14 -49
  688. package/build/dist/Server/Services/UserService.js.map +1 -1
  689. package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js +16 -0
  690. package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js.map +1 -1
  691. package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js +16 -0
  692. package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js.map +1 -1
  693. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +16 -0
  694. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
  695. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +16 -0
  696. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
  697. package/build/dist/Server/Types/Database/QueryHelper.js +67 -0
  698. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  699. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +21 -4
  700. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  701. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +9 -3
  702. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -1
  703. package/build/dist/Server/Utils/Profiling.js +10 -1
  704. package/build/dist/Server/Utils/Profiling.js.map +1 -1
  705. package/build/dist/Server/Utils/Rules/MonitorRuleCriteriaCache.js +30 -0
  706. package/build/dist/Server/Utils/Rules/MonitorRuleCriteriaCache.js.map +1 -0
  707. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +344 -0
  708. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  709. package/build/dist/Server/Utils/Telemetry.js +1 -1
  710. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  711. package/build/dist/Server/Utils/VendorAssets.js +2 -0
  712. package/build/dist/Server/Utils/VendorAssets.js.map +1 -1
  713. package/build/dist/Types/Dashboard/DashboardTemplates.js +425 -0
  714. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  715. package/build/dist/Types/OnCallDutyPolicy/Layer.js +11 -0
  716. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  717. package/build/dist/Types/Rules/RuleCriteria.js +18 -0
  718. package/build/dist/Types/Rules/RuleCriteria.js.map +1 -0
  719. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +435 -0
  720. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -0
  721. package/build/dist/Types/Rum/SessionReplay.js +64 -0
  722. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  723. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  724. package/build/dist/UI/Components/Alerts/Alert.js +63 -41
  725. package/build/dist/UI/Components/Alerts/Alert.js.map +1 -1
  726. package/build/dist/UI/Components/Button/Button.js +3 -1
  727. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  728. package/build/dist/UI/Components/Dropdown/Dropdown.js +1 -1
  729. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  730. package/build/dist/UI/Components/Feed/Feed.js +4 -1
  731. package/build/dist/UI/Components/Feed/Feed.js.map +1 -1
  732. package/build/dist/UI/Components/Feed/useFeedItems.js +137 -0
  733. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -0
  734. package/build/dist/UI/Components/Forms/BasicForm.js +38 -19
  735. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  736. package/build/dist/UI/Components/Forms/ModelForm.js +65 -1
  737. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  738. package/build/dist/UI/Components/Icon/Icon.js +3 -35
  739. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  740. package/build/dist/UI/Components/ModelTable/ModelTable.js +12 -1
  741. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  742. package/build/dist/UI/Components/Navbar/NavBar.js +1 -0
  743. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  744. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaBuilder.js +383 -0
  745. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaBuilder.js.map +1 -0
  746. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelForm.js +124 -0
  747. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelForm.js.map +1 -0
  748. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelTable.js +126 -0
  749. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelTable.js.map +1 -0
  750. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaSummary.js +63 -0
  751. package/build/dist/UI/Components/RuleCriteria/RuleCriteriaSummary.js.map +1 -0
  752. package/build/dist/UI/Components/Table/Table.js +1 -1
  753. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  754. package/build/dist/UI/Components/Table/TableBody.js +4 -2
  755. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  756. package/build/dist/UI/Components/Table/TableRow.js +3 -2
  757. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  758. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +14 -2
  759. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +1 -1
  760. package/build/dist/UI/Utils/LabelRuleImportExport.js +81 -5
  761. package/build/dist/UI/Utils/LabelRuleImportExport.js.map +1 -1
  762. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  763. package/build/dist/Utils/Faker.js +76 -6
  764. package/build/dist/Utils/Faker.js.map +1 -1
  765. package/build/dist/Utils/LabelRuleImportExport.js +157 -3
  766. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  767. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +81 -5
  768. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  769. package/build/dist/Utils/NetworkSite/CidrMatchUtil.js +77 -0
  770. package/build/dist/Utils/NetworkSite/CidrMatchUtil.js.map +1 -1
  771. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +109 -2
  772. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -1
  773. package/build/dist/Utils/Rules/RuleCriteriaMatcher.js +526 -0
  774. package/build/dist/Utils/Rules/RuleCriteriaMatcher.js.map +1 -0
  775. package/build/dist/Utils/Rum/SessionIdentity.js +13 -1
  776. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  777. package/build/dist/Utils/Slo/SloBurnRateRuleState.js +28 -15
  778. package/build/dist/Utils/Slo/SloBurnRateRuleState.js.map +1 -1
  779. package/build/dist/Utils/Telemetry/OriginAllowList.js +61 -0
  780. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  781. package/package.json +20 -20
@@ -10,6 +10,7 @@ import ChunkLoader, {
10
10
  import { createReplayEngine } from "../../../../App/FeatureSet/Dashboard/src/Components/SessionReplay/Engine/ReplayEngine";
11
11
  import {
12
12
  REPLAY_FEED_AHEAD_MIN_MS,
13
+ REPLAY_HIDDEN_TICK_MS,
13
14
  ReplayEngine,
14
15
  ReplayEngineSnapshot,
15
16
  ReplayScheduleHandle,
@@ -263,6 +264,11 @@ interface HarnessOptions {
263
264
  * background tab.
264
265
  */
265
266
  isDocumentHidden?: () => boolean;
267
+ /*
268
+ * Left undefined by default, so every scenario that does not ask for it
269
+ * feeds exactly as it did before the yield existed.
270
+ */
271
+ yieldToBrowser?: () => Promise<void>;
266
272
  }
267
273
 
268
274
  interface Harness {
@@ -272,6 +278,10 @@ interface Harness {
272
278
  requests: Array<Array<number>>;
273
279
  signals: Array<AbortSignal>;
274
280
  snapshots: Array<ReplayEngineSnapshot>;
281
+ /* What the structural channel delivered: one entry per structural change. */
282
+ structuralSnapshots: Array<ReplayEngineSnapshot>;
283
+ /* What the clock channel delivered: one entry per publish. */
284
+ clockTicks: Array<number>;
275
285
  scheduled: Array<{ callback: () => void; delayMs: number }>;
276
286
  resolveFetch: () => void;
277
287
  /*
@@ -284,6 +294,11 @@ interface Harness {
284
294
  now: () => number;
285
295
  /* Advance the wall clock AND every playing fake Replayer, then TICK. */
286
296
  tick: (ms: number) => Promise<void>;
297
+ /*
298
+ * Fire a document visibilitychange, the way createBrowserReplayEngineDeps
299
+ * does. Only wired when the harness was given an isDocumentHidden.
300
+ */
301
+ fireVisibilityChange: () => void;
287
302
  live: () => FakeReplayer;
288
303
  snapshot: () => ReplayEngineSnapshot;
289
304
  }
@@ -297,12 +312,15 @@ function makeHarness(options: HarnessOptions): Harness {
297
312
  const requests: Array<Array<number>> = [];
298
313
  const signals: Array<AbortSignal> = [];
299
314
  const snapshots: Array<ReplayEngineSnapshot> = [];
315
+ const structuralSnapshots: Array<ReplayEngineSnapshot> = [];
316
+ const clockTicks: Array<number> = [];
300
317
  const scheduled: Array<{ callback: () => void; delayMs: number }> = [];
301
318
  const pending: Array<{ chunkIndexes: Array<number>; resolve: () => void }> =
302
319
  [];
303
320
  const omitted: Set<number> = new Set<number>(options.omitChunkIndexes ?? []);
304
321
  const failing: Set<number> = new Set<number>(options.failChunkIndexes ?? []);
305
322
  const hanging: Set<number> = new Set<number>(options.hangChunkIndexes ?? []);
323
+ const visibilityListeners: Set<() => void> = new Set<() => void>();
306
324
  let clock: number = 10_000;
307
325
 
308
326
  const loader: ChunkLoader = new ChunkLoader({
@@ -405,6 +423,20 @@ function makeHarness(options: HarnessOptions): Harness {
405
423
  // Recorded callbacks are never run; tests drive TICK themselves.
406
424
  },
407
425
  isDocumentHidden: options.isDocumentHidden,
426
+ /*
427
+ * Only a harness that models a hidden tab gets the subscription, so
428
+ * every other scenario drives exactly the engine it drove before.
429
+ */
430
+ subscribeVisibility: options.isDocumentHidden
431
+ ? (listener: () => void): (() => void) => {
432
+ visibilityListeners.add(listener);
433
+
434
+ return (): void => {
435
+ visibilityListeners.delete(listener);
436
+ };
437
+ }
438
+ : undefined,
439
+ yieldToBrowser: options.yieldToBrowser,
408
440
  },
409
441
  {
410
442
  tabId: "tab-1",
@@ -415,6 +447,12 @@ function makeHarness(options: HarnessOptions): Harness {
415
447
  engine.subscribe((snapshot: ReplayEngineSnapshot): void => {
416
448
  snapshots.push(snapshot);
417
449
  });
450
+ engine.subscribeStructural?.((snapshot: ReplayEngineSnapshot): void => {
451
+ structuralSnapshots.push(snapshot);
452
+ });
453
+ engine.subscribeClock?.((currentTimeMs: number): void => {
454
+ clockTicks.push(currentTimeMs);
455
+ });
418
456
 
419
457
  const harness: Harness = {
420
458
  loader: loader,
@@ -423,6 +461,8 @@ function makeHarness(options: HarnessOptions): Harness {
423
461
  requests: requests,
424
462
  signals: signals,
425
463
  snapshots: snapshots,
464
+ structuralSnapshots: structuralSnapshots,
465
+ clockTicks: clockTicks,
426
466
  scheduled: scheduled,
427
467
  resolveFetch: (): void => {
428
468
  const waiting: Array<{
@@ -473,6 +513,11 @@ function makeHarness(options: HarnessOptions): Harness {
473
513
  engine.dispatch({ type: "TICK", nowMs: clock });
474
514
  await flush();
475
515
  },
516
+ fireVisibilityChange: (): void => {
517
+ for (const listener of [...visibilityListeners]) {
518
+ listener();
519
+ }
520
+ },
476
521
  live: (): FakeReplayer => {
477
522
  const candidates: Array<FakeReplayer> = replayers.filter(
478
523
  (replayer: FakeReplayer): boolean => {
@@ -1407,6 +1452,238 @@ describe("ReplayEngine Replayer configuration", () => {
1407
1452
 
1408
1453
  expect(harness.replayers[0]!.configs).toContainEqual({ speed: 2 });
1409
1454
  });
1455
+
1456
+ it("never lets a replayed Focus steal keyboard focus into the iframe", async () => {
1457
+ /*
1458
+ * rrweb's default answers every recorded Focus with target.focus()
1459
+ * inside its sandboxed iframe, which makes the iframe the Dashboard's
1460
+ * activeElement: keydown then fires in the iframe's document and never
1461
+ * reaches the window listener the shortcuts hang off, so Space and the
1462
+ * arrows die the first time the recorded user focused a field.
1463
+ */
1464
+ const harness: Harness = makeHarness({
1465
+ entries: [makeEntry(0, { hasFullSnapshot: true })],
1466
+ });
1467
+
1468
+ await loadAndFlush(harness, 0, 0);
1469
+ harness.engine.dispatch({ type: "SET_SPEED", speed: 2 });
1470
+
1471
+ expect(harness.replayers[0]!.constructorConfig["triggerFocus"]).toBe(false);
1472
+
1473
+ for (const config of harness.replayers[0]!.configs) {
1474
+ expect(config["triggerFocus"]).not.toBe(true);
1475
+ }
1476
+ });
1477
+ });
1478
+
1479
+ describe("ReplayEngine snapshot channels", () => {
1480
+ /*
1481
+ * The whole-snapshot channel is a new object on every ~33ms publish, so
1482
+ * a React root subscribed to it reconciles the entire player on every
1483
+ * tick even when only the playhead moved. The structural channel hands
1484
+ * back the same object until something other than currentTimeMs
1485
+ * changes; the clock channel carries the playhead alone.
1486
+ */
1487
+ it("keeps the structural snapshot's identity across ticks that move only the playhead", async () => {
1488
+ const harness: Harness = makeHarness({
1489
+ entries: [makeEntry(0, { hasFullSnapshot: true })],
1490
+ });
1491
+
1492
+ await loadAndFlush(harness, 0, 0);
1493
+ harness.engine.dispatch({ type: "PLAY" });
1494
+
1495
+ const structural: ReplayEngineSnapshot | undefined =
1496
+ harness.engine.getStructuralSnapshot?.();
1497
+ const snapshotsBefore: number = harness.snapshots.length;
1498
+ const structuralBefore: number = harness.structuralSnapshots.length;
1499
+ const clockBefore: number = harness.clockTicks.length;
1500
+ const ticks: number = 12;
1501
+
1502
+ for (let i: number = 0; i < ticks; i++) {
1503
+ await harness.tick(40);
1504
+ }
1505
+
1506
+ /* The plain channel's cadence is unchanged: one snapshot per publish. */
1507
+ expect(harness.snapshots.length - snapshotsBefore).toBe(ticks);
1508
+ /* The clock channel fires on every publish with the live playhead. */
1509
+ expect(harness.clockTicks.length - clockBefore).toBe(ticks);
1510
+ expect(harness.clockTicks[harness.clockTicks.length - 1]).toBe(
1511
+ harness.snapshot().currentTimeMs,
1512
+ );
1513
+ /* And nothing structural moved, so the structural object is the same one. */
1514
+ expect(harness.structuralSnapshots.length - structuralBefore).toBe(0);
1515
+ expect(harness.engine.getStructuralSnapshot?.()).toBe(structural);
1516
+ expect(harness.snapshot().currentTimeMs).toBeGreaterThan(
1517
+ structural!.currentTimeMs,
1518
+ );
1519
+ expect(harness.engine.getCurrentTimeMs?.()).toBe(
1520
+ harness.snapshot().currentTimeMs,
1521
+ );
1522
+
1523
+ /* (b) on the clock channel too: non-decreasing between seeks. */
1524
+ let previous: number = -1;
1525
+
1526
+ for (const value of harness.clockTicks) {
1527
+ expect(value).toBeGreaterThanOrEqual(previous);
1528
+ previous = value;
1529
+ }
1530
+ });
1531
+
1532
+ it("publishes a new structural snapshot for every change that is not the playhead", async () => {
1533
+ const harness: Harness = makeHarness({
1534
+ entries: [
1535
+ makeEntry(0, { hasFullSnapshot: true }),
1536
+ makeEntry(1),
1537
+ makeEntry(2),
1538
+ makeEntry(3),
1539
+ ],
1540
+ });
1541
+
1542
+ await loadAndFlush(harness, 0, 0);
1543
+
1544
+ const expectStructuralChange: (act: () => void) => void = (
1545
+ act: () => void,
1546
+ ): void => {
1547
+ const before: number = harness.structuralSnapshots.length;
1548
+ const previous: ReplayEngineSnapshot | undefined =
1549
+ harness.engine.getStructuralSnapshot?.();
1550
+
1551
+ act();
1552
+
1553
+ expect(harness.structuralSnapshots.length).toBeGreaterThan(before);
1554
+ expect(harness.engine.getStructuralSnapshot?.()).not.toBe(previous);
1555
+ /* The structural object carries the playhead as of that change. */
1556
+ expect(harness.engine.getStructuralSnapshot?.()!.currentTimeMs).toBe(
1557
+ harness.engine.getCurrentTimeMs?.(),
1558
+ );
1559
+ expect(harness.engine.getStructuralSnapshot?.()).toBe(harness.snapshot());
1560
+ };
1561
+
1562
+ /* phase / intent */
1563
+ expectStructuralChange((): void => {
1564
+ harness.engine.dispatch({ type: "PLAY" });
1565
+ });
1566
+ expectStructuralChange((): void => {
1567
+ harness.engine.dispatch({ type: "PAUSE" });
1568
+ });
1569
+ /*
1570
+ * A seek is deliberately NOT here. Landing inside the fed range while
1571
+ * paused sets pendingSeekMs and clears it again inside the same
1572
+ * publish, so the only field that ends up different is the playhead -
1573
+ * which is the whole point of the split, and is pinned on its own
1574
+ * below rather than being asserted as a structural change here.
1575
+ */
1576
+ /* recordedSize via rrweb's resize */
1577
+ expectStructuralChange((): void => {
1578
+ harness.live().emit("resize", { width: 1024, height: 768 });
1579
+ });
1580
+ /* speed */
1581
+ expectStructuralChange((): void => {
1582
+ harness.engine.dispatch({ type: "SET_SPEED", speed: 2 });
1583
+ });
1584
+ /* buffer, via a stall */
1585
+ expectStructuralChange((): void => {
1586
+ harness.engine.dispatch({ type: "PLAY" });
1587
+ harness.live().emit("finish");
1588
+ });
1589
+ });
1590
+
1591
+ it("treats a paused seek inside the fed range as a clock-only change", async () => {
1592
+ /*
1593
+ * The case the rail, the header and the overlays are re-rendered for
1594
+ * today and must not be: dragging the scrubber while paused to a
1595
+ * moment that is already fed. performSeek sets pendingSeekMs and
1596
+ * landAt clears it before the publish, so nothing structural differs -
1597
+ * the playhead moved, and the picture is repainted by the Replayer
1598
+ * itself, not by React.
1599
+ */
1600
+ /*
1601
+ * One chunk, so the seek cannot also grow the fed range: a seek that
1602
+ * pulls in more footage legitimately IS a structural change, and this
1603
+ * test is about the one that pulls in nothing.
1604
+ */
1605
+ const harness: Harness = makeHarness({
1606
+ entries: [makeEntry(0, { hasFullSnapshot: true })],
1607
+ });
1608
+
1609
+ await loadAndFlush(harness, 0, 0);
1610
+
1611
+ const structural: ReplayEngineSnapshot | undefined =
1612
+ harness.engine.getStructuralSnapshot?.();
1613
+ const structuralBefore: number = harness.structuralSnapshots.length;
1614
+ const clockBefore: number = harness.clockTicks.length;
1615
+
1616
+ harness.engine.dispatch({ type: "SEEK", offsetMs: 5000, token: 1 });
1617
+
1618
+ expect(harness.structuralSnapshots.length).toBe(structuralBefore);
1619
+ expect(harness.engine.getStructuralSnapshot?.()).toBe(structural);
1620
+ expect(harness.clockTicks.length).toBeGreaterThan(clockBefore);
1621
+ expect(harness.engine.getCurrentTimeMs?.()).toBe(5000);
1622
+ expect(harness.snapshot().currentTimeMs).toBe(5000);
1623
+ });
1624
+
1625
+ it("publishes a new structural snapshot when the fed range and loaded chunks grow", async () => {
1626
+ const harness: Harness = makeHarness({
1627
+ entries: [
1628
+ makeEntry(0, { hasFullSnapshot: true }),
1629
+ makeEntry(1),
1630
+ makeEntry(2),
1631
+ makeEntry(3),
1632
+ ],
1633
+ });
1634
+
1635
+ await loadAndFlush(harness, 0, 0);
1636
+ harness.engine.dispatch({ type: "PLAY" });
1637
+
1638
+ const before: ReplayEngineSnapshot | undefined =
1639
+ harness.engine.getStructuralSnapshot?.();
1640
+
1641
+ /* The first tick asks the feeder for headroom, which feeds chunk 2. */
1642
+ await harness.tick(16);
1643
+
1644
+ const after: ReplayEngineSnapshot | undefined =
1645
+ harness.engine.getStructuralSnapshot?.();
1646
+
1647
+ expect(after).not.toBe(before);
1648
+ expect(after!.fedRange!.toMs).toBeGreaterThan(before!.fedRange!.toMs);
1649
+ expect(after!.loadedChunkIndexes).not.toBe(before!.loadedChunkIndexes);
1650
+ });
1651
+
1652
+ it("stops notifying a listener once it unsubscribes", async () => {
1653
+ const harness: Harness = makeHarness({
1654
+ entries: [makeEntry(0, { hasFullSnapshot: true })],
1655
+ });
1656
+
1657
+ const structuralSeen: Array<ReplayEngineSnapshot> = [];
1658
+ const clockSeen: Array<number> = [];
1659
+ const unsubscribeStructural: (() => void) | undefined =
1660
+ harness.engine.subscribeStructural?.(
1661
+ (snapshot: ReplayEngineSnapshot): void => {
1662
+ structuralSeen.push(snapshot);
1663
+ },
1664
+ );
1665
+ const unsubscribeClock: (() => void) | undefined =
1666
+ harness.engine.subscribeClock?.((currentTimeMs: number): void => {
1667
+ clockSeen.push(currentTimeMs);
1668
+ });
1669
+
1670
+ await loadAndFlush(harness, 0, 0);
1671
+
1672
+ expect(structuralSeen.length).toBeGreaterThan(0);
1673
+ expect(clockSeen.length).toBeGreaterThan(0);
1674
+
1675
+ unsubscribeStructural?.();
1676
+ unsubscribeClock?.();
1677
+
1678
+ const structuralCount: number = structuralSeen.length;
1679
+ const clockCount: number = clockSeen.length;
1680
+
1681
+ harness.engine.dispatch({ type: "PLAY" });
1682
+ await harness.tick(40);
1683
+
1684
+ expect(structuralSeen.length).toBe(structuralCount);
1685
+ expect(clockSeen.length).toBe(clockCount);
1686
+ });
1410
1687
  });
1411
1688
 
1412
1689
  describe("ReplayEngine finish handling", () => {
@@ -1482,6 +1759,190 @@ describe("ReplayEngine finish handling", () => {
1482
1759
  });
1483
1760
  });
1484
1761
 
1762
+ /*
1763
+ * rrweb forgets which event it last played the moment it ends a cast, so
1764
+ * the next play() rebuilds the DOM from the last full snapshot and
1765
+ * re-applies everything since it, synchronously - a visible hitch on
1766
+ * EVERY buffering stall. The engine pre-empts that by pausing rrweb as it
1767
+ * casts the last event it was given: a paused machine drops the END it
1768
+ * would otherwise send itself, and the resume replays nothing.
1769
+ */
1770
+ describe("ReplayEngine stall pre-emption", () => {
1771
+ const streamingEntries: Array<SessionReplayChunkManifestEntry> = [
1772
+ makeEntry(0, { hasFullSnapshot: true }),
1773
+ makeEntry(1),
1774
+ makeEntry(2),
1775
+ ];
1776
+
1777
+ it("pre-empts rrweb's END at the last fed event so a stall resumes without a rebuild", async () => {
1778
+ const harness: Harness = makeHarness({
1779
+ entries: streamingEntries,
1780
+ deferFetch: true,
1781
+ });
1782
+
1783
+ harness.engine.dispatch({ type: "LOAD", anchorChunkIndex: 0, targetMs: 0 });
1784
+ harness.engine.dispatch({ type: "PLAY" });
1785
+ harness.resolveFetch();
1786
+ await flush();
1787
+
1788
+ const live: FakeReplayer = harness.replayers[0]!;
1789
+
1790
+ expect(harness.snapshot().buffer).toBe("ok");
1791
+
1792
+ const lastFed: SessionReplayRecordedEvent =
1793
+ live.initialEvents[live.initialEvents.length - 1]!;
1794
+
1795
+ live.emit("event-cast", lastFed);
1796
+ await flush();
1797
+
1798
+ /* Paused with NO offset: the offset form is a play(), which rebuilds. */
1799
+ expect(live.pauseOffsets).toEqual([undefined]);
1800
+ expect(live.isPlaying).toBe(false);
1801
+ expect(harness.snapshot().buffer).toBe("stalled");
1802
+ expect(harness.snapshot().phase).toBe("buffering");
1803
+ expect(harness.snapshot().intent).toBe("playing");
1804
+ expect(harness.snapshot().bufferingSinceMs).toBe(harness.now());
1805
+ expect(harness.replayers.length).toBe(1);
1806
+
1807
+ /* rrweb's own 50ms finish timer, landing on an already-stalled engine. */
1808
+ live.emit("finish");
1809
+
1810
+ expect(harness.snapshot().buffer).toBe("stalled");
1811
+ expect(live.pauseOffsets).toEqual([undefined]);
1812
+ expect(harness.replayers.length).toBe(1);
1813
+
1814
+ harness.resolveFetch();
1815
+ await flush();
1816
+
1817
+ expect(live.added.length).toBeGreaterThan(0);
1818
+ expect(live.playOffsets[live.playOffsets.length - 1]).toBe(
1819
+ live.currentTimeMs,
1820
+ );
1821
+ expect(harness.snapshot().buffer).toBe("ok");
1822
+ expect(harness.snapshot().phase).toBe("playing");
1823
+ expect(harness.snapshot().bufferingSinceMs).toBeNull();
1824
+ /* No rebuild: the same Replayer carried on. */
1825
+ expect(harness.replayers.length).toBe(1);
1826
+ });
1827
+
1828
+ it("does not pre-empt when the recording genuinely ends", async () => {
1829
+ const harness: Harness = makeHarness({
1830
+ entries: [makeEntry(0, { hasFullSnapshot: true })],
1831
+ });
1832
+
1833
+ await loadAndFlush(harness, 0, 0);
1834
+ harness.engine.dispatch({ type: "PLAY" });
1835
+
1836
+ const live: FakeReplayer = harness.replayers[0]!;
1837
+
1838
+ live.emit("event-cast", live.initialEvents[live.initialEvents.length - 1]!);
1839
+ await flush();
1840
+
1841
+ /* (d) Only the build's own pause(0); nothing pre-empted the ending. */
1842
+ expect(live.pauseOffsets).toEqual([0]);
1843
+ expect(harness.snapshot().buffer).toBe("ok");
1844
+
1845
+ live.emit("finish");
1846
+
1847
+ expect(harness.snapshot().phase).toBe("ended");
1848
+ expect(harness.snapshot().intent).toBe("paused");
1849
+ });
1850
+
1851
+ it("ignores an event-cast that is not the last fed event, and one cast while paused", async () => {
1852
+ const harness: Harness = makeHarness({ entries: streamingEntries });
1853
+
1854
+ await loadAndFlush(harness, 0, 0);
1855
+ harness.engine.dispatch({ type: "PLAY" });
1856
+
1857
+ const live: FakeReplayer = harness.replayers[0]!;
1858
+
1859
+ live.emit("event-cast", live.initialEvents[0]!);
1860
+ await flush();
1861
+
1862
+ expect(live.pauseOffsets).toEqual([0]);
1863
+ expect(harness.snapshot().buffer).toBe("ok");
1864
+
1865
+ harness.engine.dispatch({ type: "PAUSE" });
1866
+
1867
+ const pausesWhilePaused: number = live.pauseOffsets.length;
1868
+
1869
+ live.emit("event-cast", live.initialEvents[live.initialEvents.length - 1]!);
1870
+ await flush();
1871
+
1872
+ /* A viewer who paused is not stalling: nothing to pre-empt. */
1873
+ expect(live.pauseOffsets.length).toBe(pausesWhilePaused);
1874
+ expect(harness.snapshot().buffer).toBe("ok");
1875
+ expect(harness.snapshot().phase).toBe("paused");
1876
+ });
1877
+
1878
+ it("ignores an event-cast from a Replayer that has been retired", async () => {
1879
+ const harness: Harness = makeHarness({
1880
+ entries: [
1881
+ makeEntry(0, { hasFullSnapshot: true }),
1882
+ makeEntry(1),
1883
+ makeEntry(2, { hasFullSnapshot: true }),
1884
+ ],
1885
+ });
1886
+
1887
+ await loadAndFlush(harness, 0, 0);
1888
+ harness.engine.dispatch({ type: "PLAY" });
1889
+
1890
+ const retired: FakeReplayer = harness.replayers[0]!;
1891
+
1892
+ harness.engine.dispatch({ type: "SEEK", offsetMs: 2 * CHUNK_MS, token: 1 });
1893
+ await flush();
1894
+
1895
+ expect(harness.replayers.length).toBe(2);
1896
+
1897
+ const live: FakeReplayer = harness.replayers[1]!;
1898
+ const pausesBefore: number = live.pauseOffsets.length;
1899
+ const bufferBefore: string = harness.snapshot().buffer;
1900
+
1901
+ retired.emit(
1902
+ "event-cast",
1903
+ retired.initialEvents[retired.initialEvents.length - 1]!,
1904
+ );
1905
+ await flush();
1906
+
1907
+ expect(live.pauseOffsets.length).toBe(pausesBefore);
1908
+ expect(harness.snapshot().buffer).toBe(bufferBefore);
1909
+ });
1910
+
1911
+ it("resumes incrementally after Play during a pre-empted stall", async () => {
1912
+ const harness: Harness = makeHarness({
1913
+ entries: streamingEntries,
1914
+ deferFetch: true,
1915
+ });
1916
+
1917
+ harness.engine.dispatch({ type: "LOAD", anchorChunkIndex: 0, targetMs: 0 });
1918
+ harness.engine.dispatch({ type: "PLAY" });
1919
+ harness.resolveFetch();
1920
+ await flush();
1921
+
1922
+ const live: FakeReplayer = harness.replayers[0]!;
1923
+
1924
+ live.emit("event-cast", live.initialEvents[live.initialEvents.length - 1]!);
1925
+ await flush();
1926
+
1927
+ expect(harness.snapshot().buffer).toBe("stalled");
1928
+
1929
+ harness.engine.dispatch({ type: "PAUSE" });
1930
+
1931
+ expect(harness.snapshot().phase).toBe("paused");
1932
+
1933
+ harness.engine.dispatch({ type: "PLAY" });
1934
+
1935
+ expect(harness.snapshot().phase).toBe("buffering");
1936
+
1937
+ harness.resolveFetch();
1938
+ await flush();
1939
+
1940
+ expect(harness.snapshot().phase).toBe("playing");
1941
+ expect(live.added.length).toBeGreaterThan(0);
1942
+ expect(harness.replayers.length).toBe(1);
1943
+ });
1944
+ });
1945
+
1485
1946
  describe("ReplayEngine seeks", () => {
1486
1947
  it("feeds forward into the live Replayer for a seek ahead of the fed range (case 2)", async () => {
1487
1948
  /*
@@ -1994,12 +2455,250 @@ describe("ReplayEngine hold-last-frame rebuilds", () => {
1994
2455
 
1995
2456
  expect(harness.replayers[0]!.isDestroyed).toBe(true);
1996
2457
  });
1997
- });
1998
2458
 
1999
- describe("ReplayEngine gaps", () => {
2000
- it("reports the hole it crossed and keeps playing after the jump", async () => {
2001
- const harness: Harness = makeHarness({
2002
- entries: [
2459
+ /*
2460
+ * rrweb rebuilds the ANCHOR's snapshot a millisecond after it is
2461
+ * constructed, all on its own. For a seek that still has to feed
2462
+ * forward to its target, that frame can be a whole checkout behind what
2463
+ * the viewer asked for - so releasing the held frame on the rebuild
2464
+ * alone showed the wrong moment at full opacity for the length of the
2465
+ * feed-forward, and then jumped. The handover needs BOTH halves.
2466
+ */
2467
+ const spanningEntries: Array<SessionReplayChunkManifestEntry> = Array.from(
2468
+ { length: 13 },
2469
+ (_unused: unknown, index: number): SessionReplayChunkManifestEntry => {
2470
+ return makeEntry(index, {
2471
+ hasFullSnapshot: index === 0 || index === 2 || index === 12,
2472
+ });
2473
+ },
2474
+ );
2475
+
2476
+ /* The target is past the page loadFirst reads, so build takes case 2. */
2477
+ const feedForwardTargetMs: number = 11 * CHUNK_MS;
2478
+
2479
+ async function loadThenSeekAcrossAnchors(
2480
+ harness: Harness,
2481
+ targetMs: number,
2482
+ ): Promise<void> {
2483
+ harness.engine.dispatch({ type: "LOAD", anchorChunkIndex: 0, targetMs: 0 });
2484
+ /* The priority pair only; the page behind it stays in flight. */
2485
+ harness.releaseFetch(0);
2486
+ await flush();
2487
+
2488
+ harness.engine.dispatch({ type: "SEEK", offsetMs: targetMs, token: 1 });
2489
+ await flush();
2490
+ }
2491
+
2492
+ it("keeps the held frame and hides the newcomer until a feed-forward landing", async () => {
2493
+ const harness: Harness = makeHarness({
2494
+ entries: spanningEntries,
2495
+ deferFetch: true,
2496
+ autoRebuild: false,
2497
+ });
2498
+
2499
+ await loadThenSeekAcrossAnchors(harness, feedForwardTargetMs);
2500
+
2501
+ /* The new anchor's own page: enough to build on, not enough to land. */
2502
+ harness.releaseFetch(2);
2503
+ await flush();
2504
+
2505
+ expect(harness.replayers.length).toBe(2);
2506
+ expect(harness.snapshot().phase).toBe("seeking");
2507
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("hidden");
2508
+ expect(harness.replayers[0]!.isDestroyed).toBe(false);
2509
+ expect(harness.engine.getDiagnostics().isHoldingLastFrame).toBe(true);
2510
+
2511
+ /* rrweb's own rebuild of the ANCHOR frame is not the landing. */
2512
+ harness.replayers[1]!.emit("fullsnapshot-rebuilded");
2513
+
2514
+ expect(harness.replayers[0]!.isDestroyed).toBe(false);
2515
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("hidden");
2516
+
2517
+ harness.releaseFetch(8);
2518
+ await flush();
2519
+
2520
+ expect(harness.replayers[1]!.pauseOffsets).toContain(
2521
+ feedForwardTargetMs - 2 * CHUNK_MS,
2522
+ );
2523
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("");
2524
+ expect(harness.replayers[0]!.isDestroyed).toBe(true);
2525
+ expect(harness.engine.getDiagnostics().replayersDestroyed).toBe(1);
2526
+ expect(harness.snapshot().currentTimeMs).toBe(feedForwardTargetMs);
2527
+ });
2528
+
2529
+ it("arms the 2s fallback only once a Replayer exists to reveal", async () => {
2530
+ const harness: Harness = makeHarness({
2531
+ entries: spanningEntries,
2532
+ deferFetch: true,
2533
+ autoRebuild: false,
2534
+ });
2535
+
2536
+ await loadThenSeekAcrossAnchors(harness, feedForwardTargetMs);
2537
+
2538
+ /* Still fetching the new anchor: nothing to show in the old frame's place. */
2539
+ expect(harness.replayers.length).toBe(1);
2540
+ expect(
2541
+ harness.scheduled.filter((entry: { delayMs: number }): boolean => {
2542
+ return entry.delayMs === 2000;
2543
+ }),
2544
+ ).toEqual([]);
2545
+ expect(harness.replayers[0]!.isDestroyed).toBe(false);
2546
+
2547
+ harness.releaseFetch(2);
2548
+ await flush();
2549
+
2550
+ const fallback: { callback: () => void; delayMs: number } | undefined =
2551
+ harness.scheduled.find((entry: { delayMs: number }): boolean => {
2552
+ return entry.delayMs === 2000;
2553
+ });
2554
+
2555
+ expect(fallback).toBeDefined();
2556
+
2557
+ fallback!.callback();
2558
+
2559
+ expect(harness.replayers[0]!.isDestroyed).toBe(true);
2560
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("");
2561
+ });
2562
+
2563
+ it("does not hide the newcomer when there is no held frame", async () => {
2564
+ const heavy: Array<SessionReplayChunkManifestEntry> = entries.map(
2565
+ (
2566
+ entry: SessionReplayChunkManifestEntry,
2567
+ ): SessionReplayChunkManifestEntry => {
2568
+ return entry.chunkIndex === 2
2569
+ ? { ...entry, payloadBytes: 5 * 1024 * 1024 }
2570
+ : entry;
2571
+ },
2572
+ );
2573
+ const harness: Harness = makeHarness({
2574
+ entries: heavy,
2575
+ autoRebuild: false,
2576
+ });
2577
+
2578
+ await loadAndFlush(harness, 0, 0);
2579
+ harness.engine.dispatch({ type: "SEEK", offsetMs: 2 * CHUNK_MS, token: 1 });
2580
+ await flush();
2581
+
2582
+ /* A blank stage would be worse than an early frame. */
2583
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("");
2584
+ });
2585
+
2586
+ it("reveals the newcomer and drops the held frame when the feed-forward halts", async () => {
2587
+ const harness: Harness = makeHarness({
2588
+ entries: spanningEntries,
2589
+ deferFetch: true,
2590
+ autoRebuild: false,
2591
+ failChunkIndexes: [8],
2592
+ });
2593
+
2594
+ await loadThenSeekAcrossAnchors(harness, feedForwardTargetMs);
2595
+ /*
2596
+ * The newcomer is built hidden (the test above pins that) and the
2597
+ * chunk its feed-forward needs never arrives, so the landing that
2598
+ * would reveal it never happens either.
2599
+ */
2600
+ harness.releaseFetch(2);
2601
+ await flush();
2602
+
2603
+ await waitUntil((): boolean => {
2604
+ return harness.snapshot().phase === "error";
2605
+ });
2606
+
2607
+ expect(harness.replayers.length).toBe(2);
2608
+
2609
+ /* An error must never sit over a frame nothing will ever reveal. */
2610
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("");
2611
+ expect(harness.replayers[0]!.isDestroyed).toBe(true);
2612
+ });
2613
+
2614
+ it("releases the held frame when the build halts before any Replayer exists", async () => {
2615
+ /*
2616
+ * REGRESSION. The 2s fallback that used to be armed the moment a
2617
+ * frame was held is now armed in build(), after createReplayer - so
2618
+ * a seek whose anchor page never arrives reaches halt() with no
2619
+ * newcomer at all. Nothing was left to reveal and nothing to clean
2620
+ * up: the previous frame stayed on screen dimmed under "Playback
2621
+ * stopped" for the rest of the session, and its Replayer - the
2622
+ * iframe plus every parsed event of the segment - was never freed.
2623
+ */
2624
+ /*
2625
+ * Chunk 12 is the last seek anchor and the only chunk its own page
2626
+ * carries, so failing it fails the whole build before createReplayer.
2627
+ */
2628
+ const harness: Harness = makeHarness({
2629
+ entries: spanningEntries,
2630
+ autoRebuild: false,
2631
+ failChunkIndexes: [12],
2632
+ });
2633
+
2634
+ await loadAndFlush(harness, 0, 0);
2635
+
2636
+ expect(harness.replayers.length).toBe(1);
2637
+
2638
+ harness.engine.dispatch({
2639
+ type: "SEEK",
2640
+ offsetMs: harness.loader.getEntry(12)!.chunkStartOffsetMs,
2641
+ token: 1,
2642
+ });
2643
+
2644
+ await waitUntil((): boolean => {
2645
+ return harness.snapshot().phase === "error";
2646
+ });
2647
+
2648
+ /* The failed build never got as far as a second Replayer. */
2649
+ expect(harness.replayers.length).toBe(1);
2650
+ /* And the one being held was released rather than left on screen. */
2651
+ expect(harness.replayers[0]!.isDestroyed).toBe(true);
2652
+ expect(harness.engine.getDiagnostics().isHoldingLastFrame).toBe(false);
2653
+ expect(harness.engine.getDiagnostics().replayersDestroyed).toBe(
2654
+ harness.engine.getDiagnostics().replayersCreated,
2655
+ );
2656
+ });
2657
+
2658
+ it("keeps the frame the viewer saw when a second seek retires a hidden newcomer", async () => {
2659
+ const harness: Harness = makeHarness({
2660
+ entries: spanningEntries,
2661
+ deferFetch: true,
2662
+ autoRebuild: false,
2663
+ });
2664
+
2665
+ await loadThenSeekAcrossAnchors(harness, feedForwardTargetMs);
2666
+ harness.releaseFetch(2);
2667
+ await flush();
2668
+
2669
+ expect(harness.replayers[1]!.wrapper.style.visibility).toBe("hidden");
2670
+
2671
+ /* A second seek, onto the anchor at chunk 12, before the first landed. */
2672
+ harness.engine.dispatch({
2673
+ type: "SEEK",
2674
+ offsetMs: 12 * CHUNK_MS,
2675
+ token: 2,
2676
+ });
2677
+ await flush();
2678
+
2679
+ /* The newcomer nobody ever saw goes; the frame on screen stays. */
2680
+ expect(harness.replayers[1]!.isDestroyed).toBe(true);
2681
+ expect(harness.replayers[0]!.isDestroyed).toBe(false);
2682
+ expect(harness.engine.getDiagnostics().isHoldingLastFrame).toBe(true);
2683
+
2684
+ harness.resolveFetch();
2685
+ await flush();
2686
+ harness.resolveFetch();
2687
+ await flush();
2688
+
2689
+ expect(harness.replayers.length).toBe(3);
2690
+
2691
+ harness.replayers[2]!.emit("fullsnapshot-rebuilded");
2692
+
2693
+ expect(harness.replayers[0]!.isDestroyed).toBe(true);
2694
+ expect(harness.replayers[2]!.wrapper.style.visibility).toBe("");
2695
+ });
2696
+ });
2697
+
2698
+ describe("ReplayEngine gaps", () => {
2699
+ it("reports the hole it crossed and keeps playing after the jump", async () => {
2700
+ const harness: Harness = makeHarness({
2701
+ entries: [
2003
2702
  makeEntry(0, { hasFullSnapshot: true }),
2004
2703
  makeEntry(1),
2005
2704
  makeEntry(4, { hasFullSnapshot: true }),
@@ -2231,6 +2930,150 @@ describe("ReplayEngine transport economy", () => {
2231
2930
  });
2232
2931
  });
2233
2932
 
2933
+ /*
2934
+ * A pass that feeds several chunks whose bytes are already in the cache
2935
+ * never awaits anything real: the whole burst - thousands of addEvent
2936
+ * calls, each queueing a microtask inside rrweb - lands in ONE task,
2937
+ * exactly at the moments a viewer is watching most closely (a speed
2938
+ * change, a long seek, a stall resume onto a warm page). The engine hands
2939
+ * the main thread back between those chunks when a yield is injected.
2940
+ */
2941
+ describe("ReplayEngine feeding in bursts", () => {
2942
+ const pageEntries: Array<SessionReplayChunkManifestEntry> = Array.from(
2943
+ { length: 12 },
2944
+ (_unused: unknown, index: number): SessionReplayChunkManifestEntry => {
2945
+ return makeEntry(index, { hasFullSnapshot: index === 0 });
2946
+ },
2947
+ );
2948
+
2949
+ it("yields between back-to-back cache-hit chunks, but never before the first", async () => {
2950
+ let yieldCount: number = 0;
2951
+
2952
+ const harness: Harness = makeHarness({
2953
+ entries: pageEntries,
2954
+ yieldToBrowser: (): Promise<void> => {
2955
+ yieldCount += 1;
2956
+
2957
+ return new Promise<void>((resolve: () => void): void => {
2958
+ setTimeout(resolve, 0);
2959
+ });
2960
+ },
2961
+ });
2962
+
2963
+ await loadAndFlush(harness, 0, 0);
2964
+
2965
+ /* The prefetch behind the first paint makes the whole pass a cache hit. */
2966
+ for (let index: number = 2; index <= 7; index++) {
2967
+ expect(harness.loader.isChunkDecoded(index)).toBe(true);
2968
+ }
2969
+
2970
+ expect(yieldCount).toBe(0);
2971
+
2972
+ const target: number = 5 * CHUNK_MS + 1000;
2973
+ harness.engine.dispatch({ type: "SEEK", offsetMs: target, token: 1 });
2974
+
2975
+ await waitUntil((): boolean => {
2976
+ return (
2977
+ harness.snapshot().phase === "paused" &&
2978
+ harness.engine.getDiagnostics().lastFedChunkIndex === 7
2979
+ );
2980
+ });
2981
+
2982
+ /* Chunks 2..7: six feeds, five task boundaries, none before feed one. */
2983
+ expect(yieldCount).toBe(5);
2984
+ expect(harness.snapshot().currentTimeMs).toBe(target);
2985
+ /* (c) The seek still landed exactly on its target, in one Replayer. */
2986
+ expect(harness.replayers.length).toBe(1);
2987
+ expect(harness.engine.getDiagnostics().watchdogFireCount).toBe(0);
2988
+ });
2989
+
2990
+ it("does not yield before the chunk a stalled resume is waiting on", async () => {
2991
+ let yieldCount: number = 0;
2992
+
2993
+ const harness: Harness = makeHarness({
2994
+ entries: [
2995
+ makeEntry(0, { hasFullSnapshot: true }),
2996
+ makeEntry(1),
2997
+ makeEntry(2),
2998
+ ],
2999
+ deferFetch: true,
3000
+ yieldToBrowser: (): Promise<void> => {
3001
+ yieldCount += 1;
3002
+
3003
+ return new Promise<void>((resolve: () => void): void => {
3004
+ setTimeout(resolve, 0);
3005
+ });
3006
+ },
3007
+ });
3008
+
3009
+ harness.engine.dispatch({ type: "LOAD", anchorChunkIndex: 0, targetMs: 0 });
3010
+ harness.engine.dispatch({ type: "PLAY" });
3011
+ harness.resolveFetch();
3012
+ await flush();
3013
+
3014
+ harness.replayers[0]!.emit("finish");
3015
+ harness.resolveFetch();
3016
+ await flush();
3017
+
3018
+ /* Nothing may stand between a stalled viewer and the resuming chunk. */
3019
+ expect(yieldCount).toBe(0);
3020
+ expect(harness.snapshot().buffer).toBe("ok");
3021
+ expect(harness.replayers[0]!.playOffsets.length).toBeGreaterThan(1);
3022
+ });
3023
+
3024
+ it("retires a feeding loop that is parked on a yield", async () => {
3025
+ const parked: Array<() => void> = [];
3026
+
3027
+ const harness: Harness = makeHarness({
3028
+ entries: Array.from(
3029
+ { length: 12 },
3030
+ (_unused: unknown, index: number): SessionReplayChunkManifestEntry => {
3031
+ return makeEntry(index, {
3032
+ hasFullSnapshot: index === 0 || index === 8,
3033
+ });
3034
+ },
3035
+ ),
3036
+ yieldToBrowser: (): Promise<void> => {
3037
+ return new Promise<void>((resolve: () => void): void => {
3038
+ parked.push(resolve);
3039
+ });
3040
+ },
3041
+ });
3042
+
3043
+ await loadAndFlush(harness, 0, 0);
3044
+ harness.engine.dispatch({
3045
+ type: "SEEK",
3046
+ offsetMs: 5 * CHUNK_MS + 1000,
3047
+ token: 1,
3048
+ });
3049
+ await flush();
3050
+
3051
+ /* Parked between the first and the second chunk of the pass. */
3052
+ expect(parked.length).toBe(1);
3053
+
3054
+ const retired: FakeReplayer = harness.replayers[0]!;
3055
+ const addedWhenParked: number = retired.added.length;
3056
+
3057
+ /* A seek onto the snapshot at chunk 8: a rebuild, and a new generation. */
3058
+ harness.engine.dispatch({ type: "SEEK", offsetMs: 9 * CHUNK_MS, token: 2 });
3059
+ await flush();
3060
+
3061
+ expect(harness.replayers.length).toBe(2);
3062
+
3063
+ const landedAt: number | null =
3064
+ harness.engine.getDiagnostics().lastFedChunkIndex;
3065
+
3066
+ parked[0]!();
3067
+ await flush();
3068
+
3069
+ /* (a) and (e): the retired loop fed nothing more, into either Replayer. */
3070
+ expect(retired.added.length).toBe(addedWhenParked);
3071
+ expect(harness.engine.getDiagnostics().lastFedChunkIndex).toBe(landedAt);
3072
+ expect(harness.snapshot().currentTimeMs).toBe(9 * CHUNK_MS);
3073
+ expect(harness.snapshot().phase).toBe("paused");
3074
+ });
3075
+ });
3076
+
2234
3077
  describe("ReplayEngine seeking past a hole", () => {
2235
3078
  it("recovers from a seek made while a gap jump is queued instead of buffering for ever", async () => {
2236
3079
  /*
@@ -2347,6 +3190,227 @@ describe("ReplayEngine in a hidden tab", () => {
2347
3190
 
2348
3191
  expect(harness.engine.getDiagnostics().watchdogFireCount).toBe(0);
2349
3192
  });
3193
+
3194
+ /*
3195
+ * rrweb measures elapsed time between requestAnimationFrame callbacks,
3196
+ * and a background tab suspends those. Left running, the first frame
3197
+ * after the viewer returns sees the entire hidden span as elapsed and
3198
+ * casts everything due in it in one synchronous loop: the page freezes,
3199
+ * the picture snaps minutes forward past a header clock that showed it
3200
+ * frozen, and the drained buffer then stalls and rebuilds. Suspending
3201
+ * the Replayer while nobody is looking is the whole fix.
3202
+ */
3203
+ const hiddenEntries: Array<SessionReplayChunkManifestEntry> = [
3204
+ makeEntry(0, { hasFullSnapshot: true }),
3205
+ makeEntry(1),
3206
+ makeEntry(2),
3207
+ makeEntry(3),
3208
+ makeEntry(4),
3209
+ ];
3210
+
3211
+ it("suspends the Replayer while the tab is hidden and resumes it on return", async () => {
3212
+ let hidden: boolean = false;
3213
+
3214
+ const harness: Harness = makeHarness({
3215
+ entries: hiddenEntries,
3216
+ isDocumentHidden: (): boolean => {
3217
+ return hidden;
3218
+ },
3219
+ });
3220
+
3221
+ await loadAndFlush(harness, 0, 0);
3222
+ harness.engine.dispatch({ type: "PLAY" });
3223
+ await harness.tick(16);
3224
+
3225
+ const live: FakeReplayer = harness.live();
3226
+ const playsBefore: number = live.playOffsets.length;
3227
+ const pausesBefore: number = live.pauseOffsets.length;
3228
+ const clockBefore: number = harness.snapshot().currentTimeMs;
3229
+
3230
+ hidden = true;
3231
+ harness.fireVisibilityChange();
3232
+
3233
+ /* Paused with NO offset, which keeps rrweb's memory of its last cast. */
3234
+ expect(live.pauseOffsets.length).toBe(pausesBefore + 1);
3235
+ expect(live.pauseOffsets[live.pauseOffsets.length - 1]).toBeUndefined();
3236
+ expect(live.isPlaying).toBe(false);
3237
+ /* The viewer's intent is untouched, so the UI still reads "playing". */
3238
+ expect(harness.snapshot().phase).toBe("playing");
3239
+ expect(harness.snapshot().intent).toBe("playing");
3240
+ expect(harness.snapshot().buffer).toBe("ok");
3241
+
3242
+ /* And the tick became the 10Hz timer that a hidden tab can still run. */
3243
+ const lastScheduled: { callback: () => void; delayMs: number } | undefined =
3244
+ harness.scheduled[harness.scheduled.length - 1];
3245
+
3246
+ expect(lastScheduled?.delayMs).toBe(REPLAY_HIDDEN_TICK_MS);
3247
+
3248
+ /* Six seconds away, with the level-triggered check running every tick. */
3249
+ for (let step: number = 0; step < 30; step++) {
3250
+ await harness.tick(200);
3251
+ }
3252
+
3253
+ expect(live.pauseOffsets.length).toBe(pausesBefore + 1);
3254
+ expect(live.playOffsets.length).toBe(playsBefore);
3255
+ expect(harness.snapshot().currentTimeMs).toBe(clockBefore);
3256
+
3257
+ hidden = false;
3258
+ harness.fireVisibilityChange();
3259
+
3260
+ expect(live.playOffsets.length).toBe(playsBefore + 1);
3261
+ expect(live.playOffsets[live.playOffsets.length - 1]).toBe(
3262
+ live.getCurrentTime(),
3263
+ );
3264
+ /* No rebuild, and no stall: the same Replayer simply carried on. */
3265
+ expect(harness.replayers.length).toBe(1);
3266
+ expect(harness.snapshot().buffer).toBe("ok");
3267
+
3268
+ await harness.tick(200);
3269
+
3270
+ expect(harness.snapshot().currentTimeMs).toBe(clockBefore + 200);
3271
+ });
3272
+
3273
+ it("returning to the tab does not cast the hidden span in one burst", async () => {
3274
+ let hidden: boolean = false;
3275
+
3276
+ const harness: Harness = makeHarness({
3277
+ entries: hiddenEntries,
3278
+ isDocumentHidden: (): boolean => {
3279
+ return hidden;
3280
+ },
3281
+ });
3282
+
3283
+ await loadAndFlush(harness, 0, 0);
3284
+ harness.engine.dispatch({ type: "PLAY" });
3285
+ await harness.tick(16);
3286
+
3287
+ const live: FakeReplayer = harness.live();
3288
+
3289
+ hidden = true;
3290
+ harness.fireVisibilityChange();
3291
+
3292
+ for (let step: number = 0; step < 30; step++) {
3293
+ await harness.tick(200);
3294
+ }
3295
+
3296
+ const before: number = harness.snapshot().currentTimeMs;
3297
+
3298
+ hidden = false;
3299
+ /*
3300
+ * What an unpaused rrweb does on its first frame back: one stale
3301
+ * timestamp turns six seconds of wall clock into six seconds of
3302
+ * playback. A suspended Replayer's clock does not move.
3303
+ */
3304
+ live.advance(6000, harness.snapshot().speed);
3305
+ harness.fireVisibilityChange();
3306
+ await flush();
3307
+
3308
+ expect(harness.snapshot().currentTimeMs).toBe(before);
3309
+ expect(harness.snapshot().buffer).toBe("ok");
3310
+ expect(harness.replayers.length).toBe(1);
3311
+
3312
+ await harness.tick(200);
3313
+
3314
+ expect(harness.snapshot().currentTimeMs - before).toBeLessThanOrEqual(200);
3315
+ });
3316
+
3317
+ it("leaves a viewer who paused while away paused when they return", async () => {
3318
+ let hidden: boolean = false;
3319
+
3320
+ const harness: Harness = makeHarness({
3321
+ entries: hiddenEntries,
3322
+ isDocumentHidden: (): boolean => {
3323
+ return hidden;
3324
+ },
3325
+ });
3326
+
3327
+ await loadAndFlush(harness, 0, 0);
3328
+ harness.engine.dispatch({ type: "PLAY" });
3329
+ await harness.tick(16);
3330
+
3331
+ const live: FakeReplayer = harness.live();
3332
+ const playsBefore: number = live.playOffsets.length;
3333
+ const pausesBefore: number = live.pauseOffsets.length;
3334
+
3335
+ hidden = true;
3336
+ harness.fireVisibilityChange();
3337
+ harness.engine.dispatch({ type: "PAUSE" });
3338
+
3339
+ /* One pause in total: the suspension already stopped the Replayer. */
3340
+ expect(live.pauseOffsets.length).toBe(pausesBefore + 1);
3341
+
3342
+ hidden = false;
3343
+ harness.fireVisibilityChange();
3344
+
3345
+ expect(live.playOffsets.length).toBe(playsBefore);
3346
+ expect(harness.snapshot().phase).toBe("paused");
3347
+ });
3348
+
3349
+ it("suspends a Play pressed while the tab is still hidden on the next tick", async () => {
3350
+ let hidden: boolean = false;
3351
+
3352
+ const harness: Harness = makeHarness({
3353
+ entries: hiddenEntries,
3354
+ isDocumentHidden: (): boolean => {
3355
+ return hidden;
3356
+ },
3357
+ });
3358
+
3359
+ await loadAndFlush(harness, 0, 0);
3360
+ harness.engine.dispatch({ type: "PLAY" });
3361
+ await harness.tick(16);
3362
+
3363
+ const live: FakeReplayer = harness.live();
3364
+
3365
+ hidden = true;
3366
+ harness.fireVisibilityChange();
3367
+ harness.engine.dispatch({ type: "PAUSE" });
3368
+ harness.engine.dispatch({ type: "PLAY" });
3369
+
3370
+ expect(live.isPlaying).toBe(true);
3371
+
3372
+ const playsAfterPlay: number = live.playOffsets.length;
3373
+
3374
+ await harness.tick(REPLAY_HIDDEN_TICK_MS);
3375
+
3376
+ /* The backstop: nothing else in the engine knows about visibility. */
3377
+ expect(live.isPlaying).toBe(false);
3378
+
3379
+ hidden = false;
3380
+ harness.fireVisibilityChange();
3381
+
3382
+ expect(live.playOffsets.length).toBe(playsAfterPlay + 1);
3383
+ expect(live.isPlaying).toBe(true);
3384
+ });
3385
+
3386
+ it("stops listening for visibility once the engine is disposed", async () => {
3387
+ let hidden: boolean = false;
3388
+
3389
+ const harness: Harness = makeHarness({
3390
+ entries: hiddenEntries,
3391
+ isDocumentHidden: (): boolean => {
3392
+ return hidden;
3393
+ },
3394
+ });
3395
+
3396
+ await loadAndFlush(harness, 0, 0);
3397
+ harness.engine.dispatch({ type: "PLAY" });
3398
+ await harness.tick(16);
3399
+
3400
+ const live: FakeReplayer = harness.live();
3401
+ const playsBefore: number = live.playOffsets.length;
3402
+ const pausesBefore: number = live.pauseOffsets.length;
3403
+
3404
+ harness.engine.dispatch({ type: "DISPOSE" });
3405
+
3406
+ hidden = true;
3407
+ harness.fireVisibilityChange();
3408
+ hidden = false;
3409
+ harness.fireVisibilityChange();
3410
+
3411
+ expect(live.playOffsets.length).toBe(playsBefore);
3412
+ expect(live.pauseOffsets.length).toBe(pausesBefore);
3413
+ });
2350
3414
  });
2351
3415
 
2352
3416
  describe("ReplayEngine idle skipping over decoded footage", () => {