@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.
- package/Models/AnalyticsModels/RumSession.ts +34 -0
- package/Models/DatabaseModels/AlertEpisodeLabelRule.ts +2 -2
- package/Models/DatabaseModels/AlertEpisodeOnCallRule.ts +2 -2
- package/Models/DatabaseModels/AlertEpisodeOwnerRule.ts +2 -2
- package/Models/DatabaseModels/AlertEpisodePrivacyRule.ts +2 -2
- package/Models/DatabaseModels/AlertGroupingRule.ts +2 -2
- package/Models/DatabaseModels/AlertLabelRule.ts +2 -2
- package/Models/DatabaseModels/AlertOnCallRule.ts +2 -2
- package/Models/DatabaseModels/AlertOwnerRule.ts +2 -2
- package/Models/DatabaseModels/AlertPrivacyRule.ts +2 -2
- package/Models/DatabaseModels/AlertReminderRule.ts +6 -4
- package/Models/DatabaseModels/AutoRemediationRule.ts +2 -2
- package/Models/DatabaseModels/CephClusterLabelRule.ts +2 -2
- package/Models/DatabaseModels/CephClusterOwnerRule.ts +2 -2
- package/Models/DatabaseModels/CloudResourceLabelRule.ts +2 -2
- package/Models/DatabaseModels/CloudResourceOwnerRule.ts +2 -2
- package/Models/DatabaseModels/DashboardLabelRule.ts +2 -2
- package/Models/DatabaseModels/DashboardOwnerRule.ts +2 -2
- package/Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel.ts +11 -0
- package/Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel.ts +44 -0
- package/Models/DatabaseModels/DockerHostLabelRule.ts +2 -2
- package/Models/DatabaseModels/DockerHostOwnerRule.ts +2 -2
- package/Models/DatabaseModels/DockerSwarmClusterLabelRule.ts +2 -2
- package/Models/DatabaseModels/DockerSwarmClusterOwnerRule.ts +2 -2
- package/Models/DatabaseModels/HostLabelRule.ts +2 -2
- package/Models/DatabaseModels/HostOwnerRule.ts +2 -2
- package/Models/DatabaseModels/IncidentEpisodeLabelRule.ts +2 -2
- package/Models/DatabaseModels/IncidentEpisodeOnCallRule.ts +2 -2
- package/Models/DatabaseModels/IncidentEpisodeOwnerRule.ts +2 -2
- package/Models/DatabaseModels/IncidentEpisodePrivacyRule.ts +2 -2
- package/Models/DatabaseModels/IncidentGroupingRule.ts +2 -2
- package/Models/DatabaseModels/IncidentLabelRule.ts +2 -2
- package/Models/DatabaseModels/IncidentOnCallRule.ts +2 -2
- package/Models/DatabaseModels/IncidentOwnerRule.ts +2 -2
- package/Models/DatabaseModels/IncidentPrivacyRule.ts +2 -2
- package/Models/DatabaseModels/IncidentReminderRule.ts +6 -4
- package/Models/DatabaseModels/IncidentSlaRule.ts +2 -2
- package/Models/DatabaseModels/IncomingCallPolicy.ts +3 -4
- package/Models/DatabaseModels/IncomingCallPolicyLabelRule.ts +2 -2
- package/Models/DatabaseModels/IncomingCallPolicyOwnerRule.ts +2 -2
- package/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.ts +254 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/IoTFleetLabelRule.ts +2 -2
- package/Models/DatabaseModels/IoTFleetOwnerRule.ts +2 -2
- package/Models/DatabaseModels/KubernetesClusterLabelRule.ts +2 -2
- package/Models/DatabaseModels/KubernetesClusterOwnerRule.ts +2 -2
- package/Models/DatabaseModels/MonitorLabelRule.ts +2 -2
- package/Models/DatabaseModels/MonitorOwnerRule.ts +2 -2
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +2 -2
- package/Models/DatabaseModels/NetworkDeviceLabelRule.ts +2 -2
- package/Models/DatabaseModels/NetworkDeviceOwnerRule.ts +2 -2
- package/Models/DatabaseModels/NetworkSiteAssignmentRule.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyLabelRule.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.ts +2 -2
- package/Models/DatabaseModels/PodmanHostLabelRule.ts +2 -2
- package/Models/DatabaseModels/PodmanHostOwnerRule.ts +2 -2
- package/Models/DatabaseModels/ProxmoxClusterLabelRule.ts +2 -2
- package/Models/DatabaseModels/ProxmoxClusterOwnerRule.ts +2 -2
- package/Models/DatabaseModels/RumApplicationLabelRule.ts +2 -2
- package/Models/DatabaseModels/RumApplicationOwnerRule.ts +2 -2
- package/Models/DatabaseModels/RunbookLabelRule.ts +2 -2
- package/Models/DatabaseModels/RunbookOwnerRule.ts +2 -2
- package/Models/DatabaseModels/RunbookRule.ts +2 -2
- package/Models/DatabaseModels/ScheduledMaintenanceLabelRule.ts +2 -2
- package/Models/DatabaseModels/ScheduledMaintenanceOwnerRule.ts +2 -2
- package/Models/DatabaseModels/ScheduledMaintenanceReminderRule.ts +6 -4
- package/Models/DatabaseModels/ServerlessFunctionLabelRule.ts +2 -2
- package/Models/DatabaseModels/ServerlessFunctionOwnerRule.ts +2 -2
- package/Models/DatabaseModels/ServiceLabelRule.ts +2 -2
- package/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.ts +283 -4
- package/Models/DatabaseModels/ServiceOwnerRule.ts +2 -2
- package/Models/DatabaseModels/StatusPageLabelRule.ts +2 -2
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +2 -2
- package/Models/DatabaseModels/StatusPageOwnerRule.ts +2 -2
- package/Models/DatabaseModels/VMwareVCenterLabelRule.ts +2 -2
- package/Models/DatabaseModels/VMwareVCenterOwnerRule.ts +2 -2
- package/Models/DatabaseModels/WorkflowLabelRule.ts +2 -2
- package/Models/DatabaseModels/WorkflowOwnerRule.ts +2 -2
- package/Server/API/TelemetryAPI.ts +323 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792400000000-AddSloBurnRateRuleIncidents.ts +111 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792500000000-AddConfigurableRuleCriteria.ts +644 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792600000000-AddIncomingCallPolicyPhoneNumbers.ts +72 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/MultipartFormData.ts +46 -21
- package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +21 -0
- package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +21 -0
- package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +21 -0
- package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +21 -0
- package/Server/Services/AlertGroupingEngineService.ts +27 -0
- package/Server/Services/AlertLabelRuleEngineService.ts +27 -0
- package/Server/Services/AlertOnCallRuleEngineService.ts +27 -0
- package/Server/Services/AlertOwnerRuleEngineService.ts +27 -0
- package/Server/Services/AlertPrivacyRuleEngineService.ts +27 -0
- package/Server/Services/AlertReminderRuleService.ts +65 -33
- package/Server/Services/AutoRemediationRuleEngineService.ts +90 -8
- package/Server/Services/CephClusterLabelRuleEngineService.ts +20 -0
- package/Server/Services/CephClusterOwnerRuleEngineService.ts +20 -0
- package/Server/Services/CloudResourceLabelRuleEngineService.ts +20 -0
- package/Server/Services/CloudResourceOwnerRuleEngineService.ts +20 -0
- package/Server/Services/DashboardLabelRuleEngineService.ts +20 -0
- package/Server/Services/DashboardOwnerRuleEngineService.ts +20 -0
- package/Server/Services/DatabaseService.ts +409 -6
- package/Server/Services/DockerHostLabelRuleEngineService.ts +20 -0
- package/Server/Services/DockerHostOwnerRuleEngineService.ts +20 -0
- package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +23 -0
- package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +23 -0
- package/Server/Services/HostLabelRuleEngineService.ts +13 -0
- package/Server/Services/HostOwnerRuleEngineService.ts +13 -0
- package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +21 -0
- package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +21 -0
- package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +21 -0
- package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +21 -0
- package/Server/Services/IncidentGroupingEngineService.ts +64 -13
- package/Server/Services/IncidentLabelRuleEngineService.ts +64 -9
- package/Server/Services/IncidentOnCallRuleEngineService.ts +64 -10
- package/Server/Services/IncidentOwnerRuleEngineService.ts +64 -10
- package/Server/Services/IncidentPrivacyRuleEngineService.ts +64 -10
- package/Server/Services/IncidentReminderRuleService.ts +66 -34
- package/Server/Services/IncidentSlaRuleService.ts +55 -12
- package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +20 -0
- package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +20 -0
- package/Server/Services/IncomingCallPolicyPhoneNumberService.ts +184 -0
- package/Server/Services/IncomingCallPolicyService.ts +276 -5
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetLabelRuleEngineService.ts +20 -0
- package/Server/Services/IoTFleetOwnerRuleEngineService.ts +20 -0
- package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +23 -0
- package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +23 -0
- package/Server/Services/LabelService.ts +50 -7
- package/Server/Services/MonitorLabelRuleEngineService.ts +20 -0
- package/Server/Services/MonitorOwnerRuleEngineService.ts +20 -0
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +2 -0
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +93 -1
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +26 -0
- package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +25 -0
- package/Server/Services/NetworkDeviceService.ts +2 -0
- package/Server/Services/NetworkSiteAssignmentRuleService.ts +90 -1
- package/Server/Services/NetworkSiteService.ts +242 -29
- package/Server/Services/NetworkSiteTypeService.ts +177 -77
- package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +20 -0
- package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +20 -0
- package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +22 -0
- package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +22 -0
- package/Server/Services/PodmanHostLabelRuleEngineService.ts +20 -0
- package/Server/Services/PodmanHostOwnerRuleEngineService.ts +20 -0
- package/Server/Services/ProjectCallSMSConfigService.ts +156 -7
- package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +25 -0
- package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +25 -0
- package/Server/Services/RumApplicationLabelRuleEngineService.ts +22 -0
- package/Server/Services/RumApplicationOwnerRuleEngineService.ts +22 -0
- package/Server/Services/RunbookLabelRuleEngineService.ts +20 -0
- package/Server/Services/RunbookOwnerRuleEngineService.ts +20 -0
- package/Server/Services/RunbookRuleEngineService.ts +18 -1
- package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +62 -10
- package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +62 -11
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +48 -18
- package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +25 -0
- package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +25 -0
- package/Server/Services/ServiceLabelRuleEngineService.ts +20 -0
- package/Server/Services/ServiceLevelObjectiveBurnRateRuleService.ts +675 -12
- package/Server/Services/ServiceLevelObjectiveService.ts +55 -13
- package/Server/Services/ServiceOwnerRuleEngineService.ts +20 -0
- package/Server/Services/StatusPageLabelRuleEngineService.ts +20 -0
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +31 -2
- package/Server/Services/StatusPageMonitorRuleService.ts +34 -1
- package/Server/Services/StatusPageOwnerRuleEngineService.ts +20 -0
- package/Server/Services/UserService.ts +23 -50
- package/Server/Services/VMwareVCenterLabelRuleEngineService.ts +25 -0
- package/Server/Services/VMwareVCenterOwnerRuleEngineService.ts +25 -0
- package/Server/Services/WorkflowLabelRuleEngineService.ts +20 -0
- package/Server/Services/WorkflowOwnerRuleEngineService.ts +20 -0
- package/Server/Static/Brand/README.md +10 -5
- package/Server/Static/Brand/favicons/oneuptime-up-v1.svg +8 -0
- package/Server/Types/Database/QueryHelper.ts +88 -0
- package/Server/Utils/AI/Toolbox/SloTools.ts +19 -2
- package/Server/Utils/IncomingCallPhoneNumber.ts +29 -16
- package/Server/Utils/Profiling.ts +13 -1
- package/Server/Utils/Rules/MonitorRuleCriteriaCache.ts +38 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +498 -0
- package/Server/Utils/Telemetry.ts +3 -1
- package/Server/Utils/VendorAssets.ts +3 -0
- package/Tests/App/Dashboard/AIInsightViewTriage.test.tsx +348 -0
- package/Tests/App/Dashboard/ArchiveResourceCard.test.tsx +476 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +25 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +449 -24
- package/Tests/App/Dashboard/DashboardPersistentSideMenuLayouts.test.tsx +702 -0
- package/Tests/App/Dashboard/ExceptionsLandingRedirect.test.tsx +63 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +150 -0
- package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +279 -100
- package/Tests/App/Dashboard/InventoryItemSideMenu.test.tsx +155 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +11 -0
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +215 -71
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +347 -0
- package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +263 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +413 -17
- package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +138 -0
- package/Tests/App/Dashboard/SloDashboardTemplateWidgets.test.tsx +940 -0
- package/Tests/App/Dashboard/TelemetryIngestionKeyEdit.test.tsx +378 -0
- package/Tests/App/Dashboard/TelemetryIngestionKeyForm.test.tsx +735 -0
- package/Tests/App/Dashboard/TwoFactorStatus.test.tsx +275 -108
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +13 -3
- package/Tests/Models/AnalyticsModels/RumSessionReplayColumns.test.ts +127 -4
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -4
- package/Tests/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.test.ts +362 -0
- package/Tests/Models/DatabaseModels/RuleBaseModel.test.ts +69 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +181 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +1303 -145
- package/Tests/Server/API/UserTwoFactorSelfService.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +611 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +61 -3
- package/Tests/Server/Middleware/TelemetryIngestionDisabled.test.ts +211 -0
- package/Tests/Server/Services/AlertIncidentRuleCriteriaEngineServices.test.ts +651 -0
- package/Tests/Server/Services/AutoRemediationRuleEngineService.test.ts +79 -0
- package/Tests/Server/Services/DatabaseServiceRelationOnlyRuleRollout.test.ts +330 -0
- package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +522 -0
- package/Tests/Server/Services/IncidentSlaRuleCriteriaCorrelation.test.ts +99 -0
- package/Tests/Server/Services/IncomingCallPolicyPhoneNumberService.test.ts +1540 -0
- package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +179 -2
- package/Tests/Server/Services/NetworkSiteAssignmentRuleService.test.ts +170 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +559 -53
- package/Tests/Server/Services/NetworkSiteTypeService.test.ts +122 -55
- package/Tests/Server/Services/ReminderRuleCriteria.test.ts +173 -0
- package/Tests/Server/Services/ResourceRuleCriteriaEngineServices.test.ts +170 -0
- package/Tests/Server/Services/ScheduledMaintenanceRuleCriteriaCorrelation.test.ts +154 -0
- package/Tests/Server/Services/ServiceLevelObjectiveBurnRateRuleService.test.ts +2450 -470
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +464 -121
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +281 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +132 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +159 -14
- package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +6 -8
- package/Tests/Server/Services/UserTwoFactorAuthAdmin.test.ts +203 -34
- package/Tests/Server/Types/Database/QueryHelperJsonArrayObjects.test.ts +128 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +190 -1
- package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +461 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +25 -0
- package/Tests/Server/Utils/Billing/BillingFailureNoticeThrottle.test.ts +359 -0
- package/Tests/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.test.ts +318 -0
- package/Tests/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.test.ts +220 -0
- package/Tests/Server/Utils/Dashboard/SloTemplatePublicDashboard.test.ts +1043 -0
- package/Tests/Server/Utils/Execute.test.ts +302 -0
- package/Tests/Server/Utils/GracefulShutdown.test.ts +565 -0
- package/Tests/Server/Utils/IncomingCallPhoneNumber.test.ts +36 -20
- package/Tests/Server/Utils/LoggerCore.test.ts +597 -0
- package/Tests/Server/Utils/Monitor/MonitorTemplateUtil.test.ts +1109 -0
- package/Tests/Server/Utils/Monitor/SloTemplateMetricEmitterContract.test.ts +750 -0
- package/Tests/Server/Utils/ProductAnalytics.test.ts +404 -0
- package/Tests/Server/Utils/Profiling.test.ts +366 -0
- package/Tests/Server/Utils/Rules/MonitorRuleCriteriaCache.test.ts +99 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +558 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +711 -0
- package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +522 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +17 -0
- package/Tests/Types/Dashboard/DashboardTemplateEditorialInvariants.test.ts +659 -0
- package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +97 -0
- package/Tests/Types/Dashboard/SloDashboardTemplate.test.ts +1179 -0
- package/Tests/Types/Database/AccessControl/OwnedThrough.test.ts +136 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilCorePaths.test.ts +1014 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +9 -6
- package/Tests/Types/Rules/RuleCriteriaFieldRegistry.test.ts +276 -0
- package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +180 -1
- package/Tests/Types/Telemetry/LlmConventions.test.ts +495 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +13 -6
- package/Tests/UI/Components/Alert.test.tsx +532 -93
- package/Tests/UI/Components/Feed.test.tsx +241 -0
- package/Tests/UI/Components/Forms/BasicFormStepNavigation.test.tsx +368 -0
- package/Tests/UI/Components/Forms/FormSummary.test.tsx +454 -0
- package/Tests/UI/Components/Forms/ModelFormJsonColumns.test.tsx +290 -0
- package/Tests/UI/Components/Forms/ModelFormRuleCriteria.test.tsx +331 -0
- package/Tests/UI/Components/IconOpticalSize.test.tsx +79 -31
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +6 -6
- package/Tests/UI/Components/NavBar.test.tsx +91 -2
- package/Tests/UI/Components/PencilButton.test.tsx +211 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaBuilder.test.tsx +510 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +350 -0
- package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +50 -0
- package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +329 -0
- package/Tests/UI/Components/useFeedItems.test.tsx +107 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +401 -0
- package/Tests/UI/Rum/RecordingHealthCard.test.tsx +1 -1
- package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +7 -1
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +2 -0
- package/Tests/UI/Rum/ReplayEngine.test.ts +1069 -5
- package/Tests/UI/Rum/ReplayHeader.test.tsx +579 -2
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +23 -5
- package/Tests/UI/Rum/ReplayRail.test.tsx +143 -1
- package/Tests/UI/Rum/ReplayRenderBudget.test.tsx +338 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +179 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +210 -3
- package/Tests/UI/Rum/ReplayTimeline.test.tsx +180 -1
- package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +405 -0
- package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +13 -16
- package/Tests/UI/Rum/SessionReplayFacets.test.tsx +199 -0
- package/Tests/UI/Rum/SessionReplaySearchBar.test.tsx +58 -21
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +2 -0
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +717 -4
- package/Tests/UI/Rum/SessionReplayUsersPage.test.tsx +435 -0
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +577 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +1 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -1
- package/Tests/UI/Utils/LabelRuleImportExport.test.ts +192 -0
- package/Tests/UI/Utils/Navigation.test.ts +30 -0
- package/Tests/Utils/Dashboard/SloTemplateVariableScoping.test.ts +880 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +158 -0
- package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +93 -0
- package/Tests/Utils/NetworkSite/CidrMatchUtil.test.ts +138 -0
- package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +174 -0
- package/Tests/Utils/Rules/RuleCriteriaMatcher.test.ts +1424 -0
- package/Tests/Utils/Rum/SessionIdentity.test.ts +72 -0
- package/Tests/Utils/Rum/SessionReplayHealth.test.ts +884 -0
- package/Tests/Utils/Slo/SloBurnRateRuleState.test.ts +374 -28
- package/Tests/Utils/Telemetry/OriginAllowList.test.ts +118 -0
- package/Types/Dashboard/DashboardTemplates.ts +465 -0
- package/Types/OnCallDutyPolicy/Layer.ts +12 -0
- package/Types/Rules/RuleCriteria.ts +51 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +449 -0
- package/Types/Rum/SessionReplay.ts +75 -0
- package/Types/Rum/SessionReplayApi.ts +93 -0
- package/UI/Components/Alerts/Alert.tsx +102 -72
- package/UI/Components/Button/Button.tsx +4 -1
- package/UI/Components/Dropdown/Dropdown.tsx +2 -0
- package/UI/Components/Feed/Feed.tsx +15 -0
- package/UI/Components/Feed/useFeedItems.ts +227 -0
- package/UI/Components/Forms/BasicForm.tsx +42 -21
- package/UI/Components/Forms/ModelForm.tsx +80 -1
- package/UI/Components/Icon/Icon.tsx +3 -35
- package/UI/Components/ModelTable/ModelTable.tsx +19 -0
- package/UI/Components/Navbar/NavBar.tsx +1 -0
- package/UI/Components/RuleCriteria/RuleCriteriaBuilder.tsx +735 -0
- package/UI/Components/RuleCriteria/RuleCriteriaModelForm.tsx +209 -0
- package/UI/Components/RuleCriteria/RuleCriteriaModelTable.tsx +205 -0
- package/UI/Components/RuleCriteria/RuleCriteriaSummary.tsx +123 -0
- package/UI/Components/Table/Table.tsx +3 -0
- package/UI/Components/Table/TableBody.tsx +3 -0
- package/UI/Components/Table/TableRow.tsx +8 -2
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +14 -2
- package/UI/Styles/Theme.css +1 -0
- package/UI/Utils/LabelRuleImportExport.ts +114 -6
- package/UI/Utils/Navigation.ts +1 -2
- package/UI/esbuild-config.js +46 -0
- package/Utils/Faker.ts +93 -6
- package/Utils/LabelRuleImportExport.ts +295 -2
- package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +108 -8
- package/Utils/NetworkSite/CidrMatchUtil.ts +102 -0
- package/Utils/NetworkSite/TypeHierarchyUtil.ts +152 -2
- package/Utils/Rules/RuleCriteriaMatcher.ts +931 -0
- package/Utils/Rum/SessionIdentity.ts +16 -0
- package/Utils/Slo/SloBurnRateRuleState.ts +87 -33
- package/Utils/Telemetry/OriginAllowList.ts +74 -0
- package/build/dist/Models/AnalyticsModels/RumSession.js +32 -0
- package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertEpisodeLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeOnCallRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertEpisodeOnCallRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodePrivacyRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertEpisodePrivacyRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertGroupingRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertGroupingRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertOnCallRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertOnCallRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertPrivacyRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertPrivacyRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertReminderRule.js +6 -5
- package/build/dist/Models/DatabaseModels/AlertReminderRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AutoRemediationRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AutoRemediationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResourceLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/CloudResourceLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResourceOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/CloudResourceOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DashboardLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/DashboardLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DashboardOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/DashboardOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RelationOnlyRuleBaseModel.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel.js +50 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/RuleBaseModel.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DockerHostLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/DockerHostLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHostOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/DockerHostOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmClusterLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/DockerSwarmClusterLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/HostLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/HostLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/HostOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/HostOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentEpisodeLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOnCallRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOnCallRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePrivacyRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentEpisodePrivacyRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentOnCallRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentOnCallRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPrivacyRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentPrivacyRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentReminderRule.js +6 -5
- package/build/dist/Models/DatabaseModels/IncidentReminderRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentSlaRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentSlaRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +3 -4
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.js +277 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyPhoneNumber.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleetLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IoTFleetLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleetOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IoTFleetOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesClusterLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/KubernetesClusterLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/MonitorLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/MonitorOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +2 -2
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js +2 -2
- package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanHostLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/PodmanHostLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanHostOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/PodmanHostOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplicationLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/RumApplicationLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplicationOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/RumApplicationOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunbookLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/RunbookLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunbookOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/RunbookOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunbookRule.js +2 -2
- package/build/dist/Models/DatabaseModels/RunbookRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js +6 -5
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunctionLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ServerlessFunctionLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ServiceLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js +281 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/ServiceOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/StatusPageLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +2 -2
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/StatusPageOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLabelRule.js +2 -2
- package/build/dist/Models/DatabaseModels/WorkflowLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowOwnerRule.js +2 -2
- package/build/dist/Models/DatabaseModels/WorkflowOwnerRule.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +211 -4
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792400000000-AddSloBurnRateRuleIncidents.js +60 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792400000000-AddSloBurnRateRuleIncidents.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792500000000-AddConfigurableRuleCriteria.js +301 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792500000000-AddConfigurableRuleCriteria.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792600000000-AddIncomingCallPolicyPhoneNumbers.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792600000000-AddIncomingCallPolicyPhoneNumbers.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MultipartFormData.js +38 -21
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +17 -0
- package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +17 -0
- package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +17 -0
- package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +17 -0
- package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertGroupingEngineService.js +21 -0
- package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertLabelRuleEngineService.js +21 -0
- package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +21 -0
- package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +21 -0
- package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +21 -0
- package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +44 -26
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +54 -11
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +235 -10
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/HostLabelRuleEngineService.js +12 -0
- package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/HostOwnerRuleEngineService.js +12 -0
- package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +17 -0
- package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +17 -0
- package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +17 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +17 -0
- package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +39 -13
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +39 -9
- package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +39 -10
- package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +39 -10
- package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +39 -10
- package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentReminderRuleService.js +44 -26
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSlaRuleService.js +33 -12
- package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyPhoneNumberService.js +127 -0
- package/build/dist/Server/Services/IncomingCallPolicyPhoneNumberService.js.map +1 -0
- package/build/dist/Server/Services/IncomingCallPolicyService.js +187 -4
- package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +39 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +2 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +57 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +17 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +2 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js +56 -1
- package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +187 -22
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteTypeService.js +126 -51
- package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ProjectCallSMSConfigService.js +115 -6
- package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookRuleEngineService.js +12 -0
- package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +38 -9
- package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +38 -10
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +32 -14
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js +554 -12
- package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +50 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +25 -2
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +19 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +14 -49
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +16 -0
- package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +16 -0
- package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +67 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +21 -4
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +9 -3
- package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -1
- package/build/dist/Server/Utils/Profiling.js +10 -1
- package/build/dist/Server/Utils/Profiling.js.map +1 -1
- package/build/dist/Server/Utils/Rules/MonitorRuleCriteriaCache.js +30 -0
- package/build/dist/Server/Utils/Rules/MonitorRuleCriteriaCache.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +344 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +1 -1
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +2 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +425 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +11 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteria.js +18 -0
- package/build/dist/Types/Rules/RuleCriteria.js.map +1 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +435 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplay.js +64 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
- package/build/dist/UI/Components/Alerts/Alert.js +63 -41
- package/build/dist/UI/Components/Alerts/Alert.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +3 -1
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Feed/Feed.js +4 -1
- package/build/dist/UI/Components/Feed/Feed.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedItems.js +137 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -0
- package/build/dist/UI/Components/Forms/BasicForm.js +38 -19
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +65 -1
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +3 -35
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +12 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaBuilder.js +383 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaBuilder.js.map +1 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelForm.js +124 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelForm.js.map +1 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelTable.js +126 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaModelTable.js.map +1 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaSummary.js +63 -0
- package/build/dist/UI/Components/RuleCriteria/RuleCriteriaSummary.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +1 -1
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +4 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +3 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +14 -2
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +1 -1
- package/build/dist/UI/Utils/LabelRuleImportExport.js +81 -5
- package/build/dist/UI/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Faker.js +76 -6
- package/build/dist/Utils/Faker.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +157 -3
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +81 -5
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
- package/build/dist/Utils/NetworkSite/CidrMatchUtil.js +77 -0
- package/build/dist/Utils/NetworkSite/CidrMatchUtil.js.map +1 -1
- package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +109 -2
- package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -1
- package/build/dist/Utils/Rules/RuleCriteriaMatcher.js +526 -0
- package/build/dist/Utils/Rules/RuleCriteriaMatcher.js.map +1 -0
- package/build/dist/Utils/Rum/SessionIdentity.js +13 -1
- package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
- package/build/dist/Utils/Slo/SloBurnRateRuleState.js +28 -15
- package/build/dist/Utils/Slo/SloBurnRateRuleState.js.map +1 -1
- package/build/dist/Utils/Telemetry/OriginAllowList.js +61 -0
- package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
- 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
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
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", () => {
|