@oneuptime/common 11.7.4 → 12.0.0
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/ExceptionInstance.ts +67 -0
- package/Models/AnalyticsModels/Index.ts +10 -0
- package/Models/AnalyticsModels/Log.ts +60 -0
- package/Models/AnalyticsModels/RumSession.ts +1401 -0
- package/Models/AnalyticsModels/RumSessionChunk.ts +798 -0
- package/Models/AnalyticsModels/Span.ts +60 -0
- package/Models/DatabaseModels/AIAgentTaskPullRequest.ts +52 -0
- package/Models/DatabaseModels/AIRun.ts +31 -0
- package/Models/DatabaseModels/Alert.ts +29 -0
- package/Models/DatabaseModels/AlertCustomField.ts +1 -1
- package/Models/DatabaseModels/AlertFeed.ts +1 -0
- package/Models/DatabaseModels/AutoRemediationRule.ts +989 -0
- package/Models/DatabaseModels/AutoRemediationSuggestion.ts +874 -0
- package/Models/DatabaseModels/CodeRepository.ts +129 -0
- package/Models/DatabaseModels/GlobalConfig.ts +312 -0
- package/Models/DatabaseModels/Incident.ts +29 -0
- package/Models/DatabaseModels/IncidentCustomField.ts +1 -1
- package/Models/DatabaseModels/IncidentFeed.ts +1 -0
- package/Models/DatabaseModels/Index.ts +21 -8
- package/Models/DatabaseModels/InstanceHealthLog.ts +8 -0
- package/Models/DatabaseModels/MonitorCustomField.ts +1 -1
- package/Models/DatabaseModels/MonitorStatus.ts +1 -1
- package/Models/DatabaseModels/NetworkDeviceLabelRule.ts +2 -2
- package/Models/DatabaseModels/NetworkDeviceOwnerRule.ts +2 -2
- package/Models/DatabaseModels/NetworkSiteAssignmentRule.ts +1 -1
- package/Models/DatabaseModels/OnCallDutyPolicyCustomField.ts +1 -1
- package/Models/DatabaseModels/Project.ts +231 -0
- package/Models/DatabaseModels/RumApplication.ts +906 -0
- package/Models/DatabaseModels/RumSessionErasureRequest.ts +561 -0
- package/Models/DatabaseModels/RumSessionPin.ts +457 -0
- package/Models/DatabaseModels/RumSessionReplayView.ts +406 -0
- package/Models/DatabaseModels/RunbookCredential.ts +673 -0
- package/Models/DatabaseModels/RunbookSecret.ts +7 -7
- package/Models/DatabaseModels/{RunbookAgent.ts → Runner.ts} +206 -46
- package/Models/DatabaseModels/{RunbookAgentJob.ts → RunnerJob.ts} +151 -18
- package/Models/DatabaseModels/{RunbookAgentOwnerTeam.ts → RunnerOwnerTeam.ts} +41 -43
- package/Models/DatabaseModels/{RunbookAgentOwnerUser.ts → RunnerOwnerUser.ts} +41 -43
- package/Models/DatabaseModels/ScheduledMaintenanceCustomField.ts +1 -1
- package/Models/DatabaseModels/StatusPageCustomField.ts +1 -1
- package/Models/DatabaseModels/TeamCustomField.ts +1 -1
- package/Models/DatabaseModels/TeamMemberCustomField.ts +1 -1
- package/Server/API/AIAgentDataAPI.ts +179 -48
- package/Server/API/AIAgentTaskAPI.ts +36 -37
- package/Server/API/AIAgentTaskLogAPI.ts +19 -33
- package/Server/API/AutoRemediationAPI.ts +557 -0
- package/Server/API/BaseAnalyticsAPI.ts +4 -1
- package/Server/API/CommonAPI.ts +35 -0
- package/Server/API/DashboardAPI.ts +340 -15
- package/Server/API/TelemetryAPI.ts +1622 -3
- package/Server/API/UserIncomingCallNumberAPI.ts +31 -0
- package/Server/DatabaseConfig.ts +26 -1
- package/Server/EnvironmentConfig.ts +13 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785417351021-AddSessionReplay.ts +293 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785491583874-EnableSessionReplayByDefault.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785496223183-AddMonitorSummaryToIncidentAndAlert.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785516996695-AddSessionReplayReliabilityColumns.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785533806494-AddSessionReplayCorrelationColumns.ts +54 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785749674203-AddRedisAndPostgresHealthNotificationColumns.ts +104 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785763818197-AddAutoRemediation.ts +349 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785768089408-AddRemediationVerification.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785776960660-AddRunnerCapabilities.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785790000000-AddIncidentInvestigationGating.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785800000000-AddRunbookCredential.ts +84 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785810000000-RenameRunbookAgentToRunner.ts +152 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785860000000-SessionReplayDefaultToSensitiveInputMasking.ts +81 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785870000000-AddAiCommandRemediation.ts +151 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785880000000-AlignRunnerSchemaWithEntities.ts +597 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785890000000-AddFixVerificationAndAutomaticCodeFixes.ts +71 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +32 -0
- package/Server/Services/AIRunService.ts +10 -0
- package/Server/Services/AIService.ts +30 -0
- package/Server/Services/AlertService.ts +41 -4
- package/Server/Services/AlertSeverityService.ts +25 -11
- package/Server/Services/AlertStateService.ts +25 -11
- package/Server/Services/AutoRemediationRuleEngineService.ts +1101 -0
- package/Server/Services/{RunbookAgentOwnerTeamService.ts → AutoRemediationRuleService.ts} +1 -1
- package/Server/Services/AutoRemediationSuggestionService.ts +183 -0
- package/Server/Services/BillingService.ts +50 -1
- package/Server/Services/DatabaseService.ts +92 -4
- package/Server/Services/IncidentService.ts +56 -4
- package/Server/Services/IncidentSeverityService.ts +25 -11
- package/Server/Services/IncidentStateService.ts +25 -11
- package/Server/Services/Index.ts +19 -0
- package/Server/Services/MonitorService.ts +24 -0
- package/Server/Services/MonitorStatusService.ts +25 -11
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +18 -19
- package/Server/Services/NetworkDeviceLabelRuleService.ts +39 -0
- package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +18 -19
- package/Server/Services/NetworkDeviceOwnerRuleService.ts +37 -0
- package/Server/Services/NetworkDeviceService.ts +172 -21
- package/Server/Services/NetworkSiteAssignmentRuleService.ts +92 -12
- package/Server/Services/NetworkSiteService.ts +27 -10
- package/Server/Services/ProjectService.ts +47 -0
- package/Server/Services/RumApplicationService.ts +240 -1
- package/Server/Services/RumSessionChunkService.ts +11 -0
- package/Server/Services/RumSessionErasureRequestService.ts +197 -0
- package/Server/Services/RumSessionPinService.ts +213 -0
- package/Server/Services/RumSessionReplayViewService.ts +201 -0
- package/Server/Services/RumSessionService.ts +123 -0
- package/Server/Services/RunbookCredentialService.ts +76 -0
- package/Server/Services/{RunbookAgentJobService.ts → RunnerJobService.ts} +202 -51
- package/Server/Services/{RunbookAgentOwnerUserService.ts → RunnerOwnerTeamService.ts} +1 -1
- package/Server/Services/RunnerOwnerUserService.ts +10 -0
- package/Server/Services/RunnerService.ts +172 -0
- package/Server/Services/ScheduledMaintenanceStateService.ts +25 -11
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +15 -1
- package/Server/Services/ServiceService.ts +8 -2
- package/Server/Services/StatusPageService.ts +240 -36
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +20 -1
- package/Server/Services/TelemetryExceptionService.ts +3 -16
- package/Server/Services/TelemetryUsageBillingService.ts +33 -2
- package/Server/Services/TraceAggregationService.ts +196 -32
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +17 -0
- package/Server/Types/Billing/MeteredPlan/ServerMeteredPlan.ts +10 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +63 -7
- package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +128 -0
- package/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.ts +54 -0
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +17 -10
- package/Server/Utils/AI/CodeFix/CodeFixRunQueue.ts +8 -6
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +943 -0
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +866 -0
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +823 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +103 -12
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +75 -17
- package/Server/Utils/AI/SRE/ConfidenceSignal.ts +28 -6
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +202 -12
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +264 -14
- package/Server/Utils/AI/SRE/Insights/Triage.ts +2 -7
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +5 -24
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +259 -20
- package/Server/Utils/AI/SRE/PostedRootCause.ts +82 -0
- package/Server/Utils/AI/SRE/README.md +3 -2
- package/Server/Utils/AI/SRE/RemediationHandoff.ts +82 -0
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +6 -19
- package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +2 -2
- package/Server/Utils/Alert/AlertPrivacyFilter.ts +58 -0
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +604 -0
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +619 -0
- package/Server/Utils/CorsOptions.ts +52 -0
- package/Server/Utils/Database/RelationIdUtil.ts +68 -0
- package/Server/Utils/Incident/IncidentPrivacyFilter.ts +60 -0
- package/Server/Utils/InstanceHealth/PostgresHealth.ts +286 -0
- package/Server/Utils/InstanceHealth/RedisHealth.ts +434 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +20 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +20 -0
- package/Server/Utils/Monitor/MonitorResource.ts +19 -0
- package/Server/Utils/Monitor/MonitorSummaryCapture.ts +104 -0
- package/Server/Utils/NetworkDevice/RulePatternValidator.ts +44 -0
- package/Server/Utils/OpenAPI.ts +2 -1
- package/Server/Utils/Runbook/RunbookExecutePermission.ts +94 -0
- package/Server/Utils/SessionReplay/SessionReplayErasureTombstone.ts +128 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +586 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +188 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +1449 -0
- package/Server/Utils/SessionReplay/SessionReplayRetention.ts +114 -0
- package/Server/Utils/SessionReplay/SessionReplayTargeting.ts +144 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +92 -0
- package/Server/Utils/StartServer.ts +47 -3
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +44 -10
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +557 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +479 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +343 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +320 -0
- package/Tests/Models/AnalyticsModels/RumSessionSchema.test.ts +455 -0
- package/Tests/Models/AnalyticsModels/SessionIdCorrelation.test.ts +158 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +447 -0
- package/Tests/Models/RunnerVersionLabel.test.ts +223 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +1287 -0
- package/Tests/Server/API/LegacyRunbookAgentRoutes.test.ts +117 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +2093 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +429 -0
- package/Tests/Server/API/UserSmsApi.test.ts +68 -2
- package/Tests/Server/DatabaseConfig.test.ts +150 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +2 -2
- package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +2 -3
- package/Tests/Server/Services/AIRunCodeFixClaimScoping.test.ts +100 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +8 -0
- package/Tests/Server/Services/AutoRemediationCommandRuleEngine.test.ts +334 -0
- package/Tests/Server/Services/AutoRemediationRuleEngineService.test.ts +517 -0
- package/Tests/Server/Services/BillingService.test.ts +158 -1
- package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +24 -2
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +17 -10
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +128 -0
- package/Tests/Server/Services/DatabaseServiceUpdateWriteRouting.test.ts +178 -0
- package/Tests/Server/Services/DatabaseServiceUpdateWriteRoutingMonitor.test.ts +206 -0
- package/Tests/Server/Services/MonitorServiceOperationalStatusSelection.test.ts +145 -0
- package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +726 -0
- package/Tests/Server/Services/NetworkDeviceSiteAssignment.test.ts +570 -9
- package/Tests/Server/Services/NetworkSiteAssignmentRuleService.test.ts +94 -1
- package/Tests/Server/Services/NetworkSiteService.test.ts +85 -0
- package/Tests/Server/Services/RumApplicationReplayHealth.test.ts +114 -0
- package/Tests/Server/Services/RunbookCredentialService.test.ts +657 -0
- package/Tests/Server/Services/SessionReplayCreatePath.test.ts +376 -0
- package/Tests/Server/Services/SessionReplayGateCacheInvalidation.test.ts +211 -0
- package/Tests/Server/Services/SessionReplayMetering.test.ts +174 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +555 -0
- package/Tests/Server/Services/TelemetryExceptionAIFixReadiness.test.ts +25 -4
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +17 -2
- package/Tests/Server/Services/TraceAggregationService.test.ts +48 -0
- package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +855 -0
- package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +38 -27
- package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +44 -0
- package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +475 -0
- package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +257 -0
- package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.test.ts +236 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +44 -6
- package/Tests/Server/Utils/AI/CodeFixRunQueue.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +2 -2
- package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +294 -0
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +47 -3
- package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +233 -0
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +463 -0
- package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
- package/Tests/Server/Utils/AI/RemediationAllowlistNormalization.test.ts +248 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +1008 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +887 -0
- package/Tests/Server/Utils/AI/RemediationHandoff.test.ts +306 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +745 -0
- package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +291 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1050 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +350 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifier.test.ts +341 -0
- package/Tests/Server/Utils/CorsOptions.test.ts +119 -0
- package/Tests/Server/Utils/Database/RelationIdUtil.test.ts +120 -0
- package/Tests/Server/Utils/Database/ResolveReferenceId.test.ts +60 -0
- package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +337 -0
- package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +671 -0
- package/Tests/Server/Utils/Monitor/MonitorSummaryCapture.test.ts +205 -0
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +313 -0
- package/Tests/Server/Utils/PushNotificationUtil.test.ts +228 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +177 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +108 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayRetention.test.ts +229 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayTargeting.test.ts +246 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +110 -0
- package/Tests/Types/AI/AIRunStatus.test.ts +25 -0
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +1063 -0
- package/Tests/Types/BaseDatabase/AggregationType.test.ts +56 -0
- package/Tests/Types/CustomField/CustomFieldDropdownOption.test.ts +227 -0
- package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +811 -0
- package/Tests/Types/Dashboard/RumDashboardTemplate.test.ts +1238 -0
- package/Tests/Types/Day/DayOfWeek.test.ts +46 -0
- package/Tests/Types/Docker/DockerInventoryExtractor.test.ts +249 -0
- package/Tests/Types/Kubernetes/KubernetesInventoryExtractor.test.ts +231 -0
- package/Tests/Types/Kubernetes/KubernetesRightSizing.test.ts +323 -0
- package/Tests/Types/Metrics/MetricDownsamplingRetentionDays.test.ts +77 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +42 -14
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +2 -2
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +3 -2
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +1 -1
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +6 -3
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +285 -0
- package/Tests/Types/Podman/PodmanInventoryExtractor.test.ts +159 -0
- package/Tests/Types/Runbook/RunbookExecutionDeadline.test.ts +268 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +242 -0
- package/Tests/Types/Telemetry/TelemetryRetentionConfig.test.ts +225 -0
- package/Tests/Types/Text.test.ts +60 -0
- package/Tests/UI/Components/AutocompleteTextInputMenu.test.tsx +555 -0
- package/Tests/UI/Components/BulkLabelActions.test.tsx +1502 -0
- package/Tests/UI/Components/BulkOwnerActions.test.tsx +1255 -0
- package/Tests/UI/Components/CustomFields/DropdownOptionsInput.test.tsx +228 -0
- package/Tests/UI/Components/Detail/DropdownColors.test.tsx +128 -0
- package/Tests/UI/Components/Dropdown/DropdownValueBadge.test.tsx +60 -0
- package/Tests/UI/Components/FilterModalOperatorMenu.test.tsx +674 -0
- package/Tests/UI/Components/Forms/ColorPicker.test.tsx +48 -0
- package/Tests/UI/Components/Forms/ColorPickerPopup.test.tsx +222 -0
- package/Tests/UI/Components/Forms/IconPickerPopup.test.tsx +270 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +125 -6
- package/Tests/UI/Components/OperatorSelectorFilterTypes.test.tsx +1595 -0
- package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +851 -0
- package/Tests/UI/Components/OperatorSelectorMenu.test.tsx +1229 -0
- package/Tests/UI/Components/Pagination.test.tsx +85 -0
- package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +248 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +790 -0
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +236 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +606 -0
- package/Tests/UI/Utils/Timezone.test.ts +86 -0
- package/Tests/UI/Utils/UseClockTick.test.tsx +276 -0
- package/Tests/Utils/AiRemediation/CommandPolicy.test.ts +1063 -0
- package/Tests/Utils/Dashboard/ClockWidgetFormat.test.ts +1374 -0
- package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +362 -0
- package/Tests/Utils/Dashboard/DashboardHtmlComponent.test.ts +217 -0
- package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +656 -0
- package/Tests/Utils/Dashboard/ValueWidgetLayout.test.ts +1007 -0
- package/Tests/Utils/Metrics/MetricSeriesScope.test.ts +1197 -0
- package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +839 -0
- package/Tests/Utils/NetworkSite/CidrMatchUtil.test.ts +69 -0
- package/Tests/Utils/Rules/RulePatternMatchUtil.test.ts +274 -0
- package/Tests/Utils/Rum/ChunkMath.test.ts +286 -0
- package/Tests/Utils/Rum/Masking.test.ts +186 -0
- package/Tests/Utils/Rum/SessionIdentity.test.ts +272 -0
- package/Tests/Utils/Rum/SessionSampling.test.ts +164 -0
- package/Tests/Utils/Rum/UrlScrubber.test.ts +175 -0
- package/Tests/Utils/StatusPage/Report.test.ts +708 -0
- package/Tests/Utils/ValueFormatter.test.ts +299 -1
- package/Types/AI/AIRunStatus.ts +18 -7
- package/Types/AI/AIRunType.ts +18 -0
- package/Types/AI/FixVerificationStatus.ts +21 -0
- package/Types/AnalyticsDatabase/AnalyticsTableName.ts +16 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +304 -0
- package/Types/AutoRemediation/AutoRemediationExecutionMode.ts +17 -0
- package/Types/AutoRemediation/AutoRemediationSuggestionStatus.ts +35 -0
- package/Types/AutoRemediation/AutoRemediationSuggestionType.ts +17 -0
- package/Types/AutoRemediation/AutoRemediationTriggerEntity.ts +11 -0
- package/Types/AutoRemediation/AutoRemediationVerificationStatus.ts +21 -0
- package/Types/CustomField/CustomFieldDropdownOption.ts +190 -0
- package/Types/Dashboard/DashboardComponentType.ts +2 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +4 -0
- package/Types/Dashboard/DashboardComponents/DashboardClockComponent.ts +45 -0
- package/Types/Dashboard/DashboardComponents/DashboardHtmlComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardValueComponent.ts +8 -0
- package/Types/Dashboard/DashboardTemplates.ts +516 -0
- package/Types/Date.ts +6 -0
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/Kubernetes/KubernetesRightSizing.ts +353 -0
- package/Types/MeteredPlan/ProductType.ts +1 -0
- package/Types/Monitor/MonitorStepMetricMonitor.ts +27 -1
- package/Types/Monitor/MonitorSteps.ts +5 -0
- package/Types/Monitor/MonitorSummarySnapshot.ts +88 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +52 -10
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +7 -5
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +101 -20
- package/Types/Monitor/RumAlertTemplates.ts +386 -0
- package/Types/Permission.ts +204 -24
- package/Types/Rum/SessionReplay.ts +452 -0
- package/Types/Rum/SessionReplayCaptureTrigger.ts +26 -0
- package/Types/Rum/SessionReplayConsentMode.ts +27 -0
- package/Types/Rum/SessionReplayMaskingMode.ts +86 -0
- package/Types/Rum/SessionReplayTriggerReason.ts +31 -0
- package/Types/Runbook/RunbookCredentialType.ts +6 -0
- package/Types/Runbook/RunbookStep.ts +66 -1
- package/Types/Runbook/RunbookStepType.ts +26 -0
- package/Types/Runbook/RunnerJobOrigin.ts +20 -0
- package/Types/Runbook/{RunbookAgentJobStatus.ts → RunnerJobStatus.ts} +2 -2
- package/Types/StatusPage/StatusPageReport.ts +99 -0
- package/Types/Telemetry/TelemetryRetentionConfig.ts +18 -1
- package/Types/Text.ts +33 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +260 -75
- package/UI/Components/BulkUpdate/BulkLabelActions.tsx +99 -9
- package/UI/Components/BulkUpdate/BulkOwnerActions.tsx +11 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +19 -14
- package/UI/Components/CustomFields/DropdownOptionsInput.tsx +144 -54
- package/UI/Components/Detail/Detail.tsx +9 -22
- package/UI/Components/Dropdown/Dropdown.tsx +2 -5
- package/UI/Components/Dropdown/DropdownMenuZIndex.ts +7 -0
- package/UI/Components/Dropdown/DropdownValueBadge.tsx +61 -0
- package/UI/Components/Filters/OperatorSelector.tsx +350 -39
- package/UI/Components/Forms/Fields/ColorPicker.tsx +60 -27
- package/UI/Components/Forms/Fields/IconPicker.tsx +89 -60
- package/UI/Components/Forms/Validation.ts +9 -4
- package/UI/Components/List/List.tsx +1 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +41 -32
- package/UI/Components/Pagination/Pagination.tsx +33 -10
- package/UI/Components/Table/Table.tsx +1 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +321 -0
- package/UI/Utils/Dropdown.ts +26 -0
- package/UI/Utils/Timezone.ts +18 -2
- package/UI/Utils/UseClockTick.ts +78 -0
- package/Utils/AiRemediation/CommandPolicy.ts +398 -0
- package/Utils/Dashboard/ClockWidgetFormat.ts +604 -0
- package/Utils/Dashboard/Components/DashboardClockComponent.ts +181 -0
- package/Utils/Dashboard/Components/DashboardHtmlComponent.ts +162 -0
- package/Utils/Dashboard/Components/DashboardValueComponent.ts +12 -1
- package/Utils/Dashboard/Components/Index.ts +14 -0
- package/Utils/Dashboard/HtmlWidgetDocument.ts +309 -0
- package/Utils/Dashboard/ValueWidgetLayout.ts +471 -0
- package/Utils/Metrics/MetricSeriesScope.ts +301 -0
- package/Utils/Monitor/MonitorSummarySnapshotUtil.ts +593 -0
- package/Utils/NetworkSite/CidrMatchUtil.ts +30 -17
- package/Utils/Rules/RulePatternMatchUtil.ts +103 -0
- package/Utils/Rum/ChunkMath.ts +211 -0
- package/Utils/Rum/Masking.ts +168 -0
- package/Utils/Rum/SessionIdentity.ts +230 -0
- package/Utils/Rum/SessionSampling.ts +108 -0
- package/Utils/Rum/UrlScrubber.ts +185 -0
- package/Utils/Schema/ModelSchema.ts +27 -3
- package/Utils/StatusPage/Report.ts +304 -0
- package/Utils/ValueFormatter.ts +300 -54
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +63 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Index.js +10 -0
- package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +56 -0
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/RumSession.js +1159 -0
- package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -0
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +670 -0
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -0
- package/build/dist/Models/AnalyticsModels/Span.js +56 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js +54 -0
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIRun.js +32 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Alert.js +30 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/AlertCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertFeed.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AutoRemediationRule.js +998 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +917 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -0
- package/build/dist/Models/DatabaseModels/CodeRepository.js +132 -0
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +327 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +30 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +20 -8
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InstanceHealthLog.js +8 -0
- package/build/dist/Models/DatabaseModels/InstanceHealthLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatus.js +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatus.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 +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +235 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +930 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumSessionErasureRequest.js +591 -0
- package/build/dist/Models/DatabaseModels/RumSessionErasureRequest.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RumSessionPin.js +483 -0
- package/build/dist/Models/DatabaseModels/RumSessionPin.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RumSessionReplayView.js +433 -0
- package/build/dist/Models/DatabaseModels/RumSessionReplayView.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RunbookCredential.js +693 -0
- package/build/dist/Models/DatabaseModels/RunbookCredential.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RunbookSecret.js +8 -8
- package/build/dist/Models/DatabaseModels/RunbookSecret.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{RunbookAgent.js → Runner.js} +235 -70
- package/build/dist/Models/DatabaseModels/Runner.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{RunbookAgentJob.js → RunnerJob.js} +173 -40
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{RunbookAgentOwnerTeam.js → RunnerOwnerTeam.js} +57 -57
- package/build/dist/Models/DatabaseModels/RunnerOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{RunbookAgentOwnerUser.js → RunnerOwnerUser.js} +57 -57
- package/build/dist/Models/DatabaseModels/RunnerOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/TeamCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js +1 -1
- package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +130 -45
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskAPI.js +26 -36
- package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskLogAPI.js +11 -28
- package/build/dist/Server/API/AIAgentTaskLogAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +416 -0
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -0
- package/build/dist/Server/API/BaseAnalyticsAPI.js +4 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +24 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +265 -6
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +940 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +18 -0
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/DatabaseConfig.js +23 -4
- package/build/dist/Server/DatabaseConfig.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +8 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785417351021-AddSessionReplay.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785417351021-AddSessionReplay.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785491583874-EnableSessionReplayByDefault.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785491583874-EnableSessionReplayByDefault.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785496223183-AddMonitorSummaryToIncidentAndAlert.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785496223183-AddMonitorSummaryToIncidentAndAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785516996695-AddSessionReplayReliabilityColumns.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785516996695-AddSessionReplayReliabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785533806494-AddSessionReplayCorrelationColumns.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785533806494-AddSessionReplayCorrelationColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785749674203-AddRedisAndPostgresHealthNotificationColumns.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785749674203-AddRedisAndPostgresHealthNotificationColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785763818197-AddAutoRemediation.js +128 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785763818197-AddAutoRemediation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785768089408-AddRemediationVerification.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785768089408-AddRemediationVerification.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785776960660-AddRunnerCapabilities.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785776960660-AddRunnerCapabilities.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785790000000-AddIncidentInvestigationGating.js +27 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785790000000-AddIncidentInvestigationGating.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785800000000-AddRunbookCredential.js +44 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785800000000-AddRunbookCredential.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785810000000-RenameRunbookAgentToRunner.js +88 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785810000000-RenameRunbookAgentToRunner.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785860000000-SessionReplayDefaultToSensitiveInputMasking.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785860000000-SessionReplayDefaultToSensitiveInputMasking.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785870000000-AddAiCommandRemediation.js +76 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785870000000-AddAiCommandRemediation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785880000000-AlignRunnerSchemaWithEntities.js +214 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785880000000-AlignRunnerSchemaWithEntities.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785890000000-AddFixVerificationAndAutomaticCodeFixes.js +44 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785890000000-AddFixVerificationAndAutomaticCodeFixes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +1 -4
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +26 -0
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +37 -4
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +24 -11
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AlertStateService.js +24 -11
- package/build/dist/Server/Services/AlertStateService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +917 -0
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/{RunbookAgentOwnerTeamService.js → AutoRemediationRuleService.js} +2 -2
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -0
- package/build/dist/Server/Services/AutoRemediationSuggestionService.js +127 -0
- package/build/dist/Server/Services/AutoRemediationSuggestionService.js.map +1 -0
- package/build/dist/Server/Services/BillingService.js +45 -1
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +72 -4
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +52 -4
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +24 -11
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateService.js +24 -11
- package/build/dist/Server/Services/IncidentStateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +18 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +24 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +25 -11
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +12 -11
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleService.js +43 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +12 -11
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleService.js +41 -0
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +129 -20
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js +67 -10
- package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +19 -8
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +43 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +202 -0
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionChunkService.js +9 -0
- package/build/dist/Server/Services/RumSessionChunkService.js.map +1 -0
- package/build/dist/Server/Services/RumSessionErasureRequestService.js +190 -0
- package/build/dist/Server/Services/RumSessionErasureRequestService.js.map +1 -0
- package/build/dist/Server/Services/RumSessionPinService.js +208 -0
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -0
- package/build/dist/Server/Services/RumSessionReplayViewService.js +180 -0
- package/build/dist/Server/Services/RumSessionReplayViewService.js.map +1 -0
- package/build/dist/Server/Services/RumSessionService.js +102 -0
- package/build/dist/Server/Services/RumSessionService.js.map +1 -0
- package/build/dist/Server/Services/RunbookCredentialService.js +67 -0
- package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -0
- package/build/dist/Server/Services/{RunbookAgentJobService.js → RunnerJobService.js} +158 -46
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -0
- package/build/dist/Server/Services/{RunbookAgentOwnerUserService.js → RunnerOwnerTeamService.js} +2 -2
- package/build/dist/Server/Services/RunnerOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/RunnerOwnerUserService.js +9 -0
- package/build/dist/Server/Services/RunnerOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/RunnerService.js +168 -0
- package/build/dist/Server/Services/RunnerService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateService.js +24 -11
- package/build/dist/Server/Services/ScheduledMaintenanceStateService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +15 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +8 -2
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +173 -16
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +3 -16
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +31 -3
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +106 -2
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +16 -0
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/ServerMeteredPlan.js +9 -0
- package/build/dist/Server/Types/Billing/MeteredPlan/ServerMeteredPlan.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +38 -6
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +104 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.js +54 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +11 -7
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixRunQueue.js +3 -4
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixRunQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +724 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +668 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +657 -0
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +52 -7
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +53 -5
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +25 -6
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +131 -17
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +200 -5
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Triage.js +2 -7
- package/build/dist/Server/Utils/AI/SRE/Insights/Triage.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +4 -17
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +203 -19
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +89 -0
- package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/RemediationHandoff.js +82 -0
- package/build/dist/Server/Utils/AI/SRE/RemediationHandoff.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +4 -19
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +2 -2
- package/build/dist/Server/Utils/Alert/AlertPrivacyFilter.js +36 -0
- package/build/dist/Server/Utils/Alert/AlertPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +498 -0
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -0
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +492 -0
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -0
- package/build/dist/Server/Utils/CorsOptions.js +47 -0
- package/build/dist/Server/Utils/CorsOptions.js.map +1 -0
- package/build/dist/Server/Utils/Database/RelationIdUtil.js +51 -0
- package/build/dist/Server/Utils/Database/RelationIdUtil.js.map +1 -0
- package/build/dist/Server/Utils/Incident/IncidentPrivacyFilter.js +38 -0
- package/build/dist/Server/Utils/Incident/IncidentPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +194 -0
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +292 -0
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +16 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorSummaryCapture.js +92 -0
- package/build/dist/Server/Utils/Monitor/MonitorSummaryCapture.js.map +1 -0
- package/build/dist/Server/Utils/NetworkDevice/RulePatternValidator.js +27 -0
- package/build/dist/Server/Utils/NetworkDevice/RulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/OpenAPI.js +2 -1
- package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
- package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +67 -0
- package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayErasureTombstone.js +98 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayErasureTombstone.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +378 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +137 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +998 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayRetention.js +59 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayRetention.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayTargeting.js +98 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayTargeting.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +69 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +40 -3
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +27 -6
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -1
- package/build/dist/Types/AI/AIRunStatus.js +17 -5
- package/build/dist/Types/AI/AIRunStatus.js.map +1 -1
- package/build/dist/Types/AI/AIRunType.js +18 -0
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AI/FixVerificationStatus.js +22 -0
- package/build/dist/Types/AI/FixVerificationStatus.js.map +1 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +16 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +192 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationExecutionMode.js +18 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationExecutionMode.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionStatus.js +31 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionStatus.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionType.js +18 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionType.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationTriggerEntity.js +12 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationTriggerEntity.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationVerificationStatus.js +22 -0
- package/build/dist/Types/AutoRemediation/AutoRemediationVerificationStatus.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldDropdownOption.js +110 -0
- package/build/dist/Types/CustomField/CustomFieldDropdownOption.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +4 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardClockComponent.js +19 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardClockComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardHtmlComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardHtmlComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +487 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Date.js +6 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +2 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesRightSizing.js +229 -0
- package/build/dist/Types/Kubernetes/KubernetesRightSizing.js.map +1 -0
- package/build/dist/Types/MeteredPlan/ProductType.js +1 -0
- package/build/dist/Types/MeteredPlan/ProductType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +12 -2
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorSteps.js +5 -0
- package/build/dist/Types/Monitor/MonitorSteps.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorSummarySnapshot.js +13 -0
- package/build/dist/Types/Monitor/MonitorSummarySnapshot.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +40 -8
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +4 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +50 -10
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/RumAlertTemplates.js +297 -0
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +184 -24
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +155 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js +26 -0
- package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayConsentMode.js +27 -0
- package/build/dist/Types/Rum/SessionReplayConsentMode.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayMaskingMode.js +79 -0
- package/build/dist/Types/Rum/SessionReplayMaskingMode.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayTriggerReason.js +28 -0
- package/build/dist/Types/Rum/SessionReplayTriggerReason.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookCredentialType.js +7 -0
- package/build/dist/Types/Runbook/RunbookCredentialType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStep.js +20 -1
- package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
- package/build/dist/Types/Runbook/RunbookStepType.js +24 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Runbook/RunnerJobOrigin.js +21 -0
- package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -0
- package/build/dist/Types/Runbook/RunnerJobStatus.js +22 -0
- package/build/dist/Types/Runbook/RunnerJobStatus.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageReport.js +22 -0
- package/build/dist/Types/StatusPage/StatusPageReport.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryRetentionConfig.js.map +1 -1
- package/build/dist/Types/Text.js +29 -0
- package/build/dist/Types/Text.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +143 -28
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +82 -9
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkOwnerActions.js +11 -0
- package/build/dist/UI/Components/BulkUpdate/BulkOwnerActions.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +11 -13
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/DropdownOptionsInput.js +76 -45
- package/build/dist/UI/Components/CustomFields/DropdownOptionsInput.js.map +1 -1
- package/build/dist/UI/Components/Detail/Detail.js +3 -8
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +2 -5
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/DropdownMenuZIndex.js +7 -0
- package/build/dist/UI/Components/Dropdown/DropdownMenuZIndex.js.map +1 -0
- package/build/dist/UI/Components/Dropdown/DropdownValueBadge.js +32 -0
- package/build/dist/UI/Components/Dropdown/DropdownValueBadge.js.map +1 -0
- package/build/dist/UI/Components/Filters/OperatorSelector.js +210 -20
- package/build/dist/UI/Components/Filters/OperatorSelector.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +26 -10
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -13
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +9 -5
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/List/List.js +1 -1
- package/build/dist/UI/Components/List/List.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +21 -19
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +14 -3
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- 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/Types/UseAnchoredFieldPopup.js +197 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -0
- package/build/dist/UI/Utils/Dropdown.js +18 -0
- package/build/dist/UI/Utils/Dropdown.js.map +1 -1
- package/build/dist/UI/Utils/Timezone.js +13 -2
- package/build/dist/UI/Utils/Timezone.js.map +1 -1
- package/build/dist/UI/Utils/UseClockTick.js +41 -0
- package/build/dist/UI/Utils/UseClockTick.js.map +1 -0
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +303 -0
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -0
- package/build/dist/Utils/Dashboard/ClockWidgetFormat.js +232 -0
- package/build/dist/Utils/Dashboard/ClockWidgetFormat.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +146 -0
- package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardHtmlComponent.js +133 -0
- package/build/dist/Utils/Dashboard/Components/DashboardHtmlComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardValueComponent.js +10 -1
- package/build/dist/Utils/Dashboard/Components/DashboardValueComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/Index.js +8 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +198 -0
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -0
- package/build/dist/Utils/Dashboard/ValueWidgetLayout.js +276 -0
- package/build/dist/Utils/Dashboard/ValueWidgetLayout.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricSeriesScope.js +203 -0
- package/build/dist/Utils/Metrics/MetricSeriesScope.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorSummarySnapshotUtil.js +382 -0
- package/build/dist/Utils/Monitor/MonitorSummarySnapshotUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/CidrMatchUtil.js +13 -7
- package/build/dist/Utils/NetworkSite/CidrMatchUtil.js.map +1 -1
- package/build/dist/Utils/Rules/RulePatternMatchUtil.js +87 -0
- package/build/dist/Utils/Rules/RulePatternMatchUtil.js.map +1 -0
- package/build/dist/Utils/Rum/ChunkMath.js +147 -0
- package/build/dist/Utils/Rum/ChunkMath.js.map +1 -0
- package/build/dist/Utils/Rum/Masking.js +143 -0
- package/build/dist/Utils/Rum/Masking.js.map +1 -0
- package/build/dist/Utils/Rum/SessionIdentity.js +161 -0
- package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -0
- package/build/dist/Utils/Rum/SessionSampling.js +91 -0
- package/build/dist/Utils/Rum/SessionSampling.js.map +1 -0
- package/build/dist/Utils/Rum/UrlScrubber.js +144 -0
- package/build/dist/Utils/Rum/UrlScrubber.js.map +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js +28 -3
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/build/dist/Utils/StatusPage/Report.js +194 -0
- package/build/dist/Utils/StatusPage/Report.js.map +1 -0
- package/build/dist/Utils/ValueFormatter.js +239 -46
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/jest.config.json +3 -0
- package/package.json +1 -1
- package/tsconfig.json +19 -1
- package/Server/Services/RunbookAgentService.ts +0 -93
- package/build/dist/Models/DatabaseModels/RunbookAgent.js.map +0 -1
- package/build/dist/Models/DatabaseModels/RunbookAgentJob.js.map +0 -1
- package/build/dist/Models/DatabaseModels/RunbookAgentOwnerTeam.js.map +0 -1
- package/build/dist/Models/DatabaseModels/RunbookAgentOwnerUser.js.map +0 -1
- package/build/dist/Server/Services/RunbookAgentJobService.js.map +0 -1
- package/build/dist/Server/Services/RunbookAgentOwnerTeamService.js.map +0 -1
- package/build/dist/Server/Services/RunbookAgentOwnerUserService.js.map +0 -1
- package/build/dist/Server/Services/RunbookAgentService.js +0 -91
- package/build/dist/Server/Services/RunbookAgentService.js.map +0 -1
- package/build/dist/Types/Runbook/RunbookAgentJobStatus.js +0 -22
- package/build/dist/Types/Runbook/RunbookAgentJobStatus.js.map +0 -1
|
@@ -0,0 +1,1449 @@
|
|
|
1
|
+
import { SQL, Statement } from "../AnalyticsDatabase/Statement";
|
|
2
|
+
import { getQuerySettings } from "../AnalyticsDatabase/QuerySettingsHelper";
|
|
3
|
+
import RumSessionService from "../../Services/RumSessionService";
|
|
4
|
+
import RumSessionChunkService from "../../Services/RumSessionChunkService";
|
|
5
|
+
import {
|
|
6
|
+
DbJSONResponse,
|
|
7
|
+
Results,
|
|
8
|
+
} from "../../Services/AnalyticsDatabaseService";
|
|
9
|
+
import AnalyticsTableName from "../../../Types/AnalyticsDatabase/AnalyticsTableName";
|
|
10
|
+
import TableColumnType from "../../../Types/AnalyticsDatabase/TableColumnType";
|
|
11
|
+
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
12
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
13
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
14
|
+
import ChunkMath from "../../../Utils/Rum/ChunkMath";
|
|
15
|
+
import {
|
|
16
|
+
MAX_SESSION_REPLAY_CHUNKS_PER_READ,
|
|
17
|
+
MAX_SESSION_REPLAY_READ_BYTES,
|
|
18
|
+
SessionReplayChunkManifestEntry,
|
|
19
|
+
SessionReplayGap,
|
|
20
|
+
} from "../../../Types/Rum/SessionReplay";
|
|
21
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
22
|
+
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Bespoke ClickHouse reads for session-replay playback.
|
|
26
|
+
*
|
|
27
|
+
* Why this exists at all instead of BaseAnalyticsAPI / the generic ORM
|
|
28
|
+
* read path:
|
|
29
|
+
*
|
|
30
|
+
* 1. There is NO `FINAL` support anywhere in this repo — neither
|
|
31
|
+
* StatementGenerator nor AnalyticsDatabaseService ever emits it. Both
|
|
32
|
+
* replay tables are ReplacingMergeTree, so until a background merge
|
|
33
|
+
* runs a session is physically several rows: a provisional header
|
|
34
|
+
* written on chunk 0 and a finalized header written minutes later,
|
|
35
|
+
* plus one extra chunk row per retried delivery. A naive SELECT shows
|
|
36
|
+
* every one of them. That is worst for the NEWEST sessions, which are
|
|
37
|
+
* exactly the rows a session list sorts first. Every read here
|
|
38
|
+
* therefore collapses duplicates itself: `argMax(col, version)` over
|
|
39
|
+
* the replace key for the header table (the convention
|
|
40
|
+
* SloHistoryService documents), and `ORDER BY ... version DESC LIMIT
|
|
41
|
+
* 1 BY ...` for the chunk table where whole rows, not aggregates, are
|
|
42
|
+
* wanted.
|
|
43
|
+
*
|
|
44
|
+
* 2. `toFindStatement` clamps limit to LIMIT_PER_PROJECT, has no cursor,
|
|
45
|
+
* and runs with `timeout_overflow_mode = 'break'`, which returns
|
|
46
|
+
* PARTIAL RESULTS WITHOUT ERRORING. A silently short session list is
|
|
47
|
+
* merely annoying; a silently short chunk page renders a DOM the user
|
|
48
|
+
* never saw. Every statement here uses 'throw' instead.
|
|
49
|
+
*
|
|
50
|
+
* 3. The manifest read must never name the `payload` column, so
|
|
51
|
+
* ClickHouse never touches (and never decompresses) the only column
|
|
52
|
+
* in the system that holds a recording of a real person's screen.
|
|
53
|
+
* The byte-cap pre-check is the one exception, and it measures
|
|
54
|
+
* `length(payload)` inside ClickHouse without ever shipping the
|
|
55
|
+
* bytes: the cap has to bound the size of what is actually returned,
|
|
56
|
+
* and the only honest measure of that is the stored column itself.
|
|
57
|
+
*
|
|
58
|
+
* NOTE on aliases: ClickHouse substitutes SELECT aliases into same-level
|
|
59
|
+
* unqualified WHERE references, and an aggregate alias there is an
|
|
60
|
+
* ILLEGAL_AGGREGATION error. Every aggregate below is therefore aliased
|
|
61
|
+
* to a name that does NOT collide with a real column, so WHERE keeps
|
|
62
|
+
* referring to the physical column and HAVING/ORDER BY can safely use the
|
|
63
|
+
* alias.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/* Both replay tables carry retentionDate, so both reads need the filter. */
|
|
67
|
+
const RETENTION_FILTER: string = " AND retentionDate >= now()";
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* Wall-clock cap. 'throw' rather than 'break': see (2) above. 30s is well
|
|
71
|
+
* inside the App pool's 58s request_timeout, so a query that blows the
|
|
72
|
+
* budget surfaces as an error the player can retry rather than as a
|
|
73
|
+
* truncated recording.
|
|
74
|
+
*/
|
|
75
|
+
const READ_QUERY_SETTINGS: string = getQuerySettings({
|
|
76
|
+
maxExecutionTimeInSeconds: 30,
|
|
77
|
+
timeoutOverflowMode: "throw",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/* Page sizes for the session list. */
|
|
81
|
+
export const DEFAULT_SESSION_REPLAY_LIST_LIMIT: number = 50;
|
|
82
|
+
export const MAX_SESSION_REPLAY_LIST_LIMIT: number = 200;
|
|
83
|
+
|
|
84
|
+
/* Sessions returned by the exception -> replay lookup. */
|
|
85
|
+
export const MAX_SESSION_REPLAY_FOR_EXCEPTION_LIMIT: number = 20;
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Row ceiling on one manifest. A session is capped at
|
|
89
|
+
* MAX_SESSION_REPLAY_CHUNKS_PER_SESSION (480) chunks PER TAB, and a
|
|
90
|
+
* session can legitimately span several tabs, so the manifest is bounded
|
|
91
|
+
* separately. Hitting it is reported rather than silently truncating the
|
|
92
|
+
* timeline.
|
|
93
|
+
*/
|
|
94
|
+
const MAX_MANIFEST_ROWS: number = 4096;
|
|
95
|
+
|
|
96
|
+
export interface SessionReplayListCursor {
|
|
97
|
+
/* Server-clamped session start of the last row of the previous page. */
|
|
98
|
+
startTimeUnixMs: number;
|
|
99
|
+
sessionId: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SessionReplayListFilters {
|
|
103
|
+
hasError?: boolean | undefined;
|
|
104
|
+
/*
|
|
105
|
+
* Any frustration signal (rage/dead/error clicks, refresh rage).
|
|
106
|
+
* Server-side, so "frustration" filters the whole table — the old
|
|
107
|
+
* client-side version filtered only the fetched page, silently showing
|
|
108
|
+
* an empty list for a project whose frustrated sessions sat on page 2.
|
|
109
|
+
*/
|
|
110
|
+
hasFrustration?: boolean | undefined;
|
|
111
|
+
isFinalized?: boolean | undefined;
|
|
112
|
+
triggerReasons?: Array<string> | undefined;
|
|
113
|
+
browserNames?: Array<string> | undefined;
|
|
114
|
+
osNames?: Array<string> | undefined;
|
|
115
|
+
deviceTypes?: Array<string> | undefined;
|
|
116
|
+
countryCodes?: Array<string> | undefined;
|
|
117
|
+
identifiedUserKey?: string | undefined;
|
|
118
|
+
/* "sessions that hit /checkout" - matches the routes array. */
|
|
119
|
+
route?: string | undefined;
|
|
120
|
+
minDurationMs?: number | undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface SessionReplayListRequest {
|
|
124
|
+
projectId: ObjectID;
|
|
125
|
+
rumApplicationId: ObjectID;
|
|
126
|
+
startTime: Date;
|
|
127
|
+
endTime: Date;
|
|
128
|
+
filters: SessionReplayListFilters;
|
|
129
|
+
limit: number;
|
|
130
|
+
cursor?: SessionReplayListCursor | undefined;
|
|
131
|
+
/*
|
|
132
|
+
* The raw end-user identifier has its own, narrower column ACL than the
|
|
133
|
+
* rest of the header row. This raw-SQL path never invokes
|
|
134
|
+
* ModelPermission, so the caller decides column-by-column and the
|
|
135
|
+
* column is simply not named in the SELECT when it is not permitted.
|
|
136
|
+
*/
|
|
137
|
+
includeIdentifiedUserLabel: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface SessionReplayListItem {
|
|
141
|
+
sessionId: string;
|
|
142
|
+
rumApplicationId: string;
|
|
143
|
+
startTime: Date;
|
|
144
|
+
endTime: Date;
|
|
145
|
+
durationMs: number;
|
|
146
|
+
isFinalized: boolean;
|
|
147
|
+
sealedReason: string;
|
|
148
|
+
chunkCount: number;
|
|
149
|
+
maxChunkIndex: number;
|
|
150
|
+
missingChunkCount: number;
|
|
151
|
+
eventCount: number;
|
|
152
|
+
payloadBytes: number;
|
|
153
|
+
hasError: boolean;
|
|
154
|
+
errorCount: number;
|
|
155
|
+
rageClickCount: number;
|
|
156
|
+
deadClickCount: number;
|
|
157
|
+
errorClickCount: number;
|
|
158
|
+
refreshRageCount: number;
|
|
159
|
+
pageCount: number;
|
|
160
|
+
triggerReason: string;
|
|
161
|
+
entryUrl: string;
|
|
162
|
+
exitUrl: string;
|
|
163
|
+
browserName: string;
|
|
164
|
+
browserVersion: string;
|
|
165
|
+
osName: string;
|
|
166
|
+
deviceType: string;
|
|
167
|
+
countryCode: string;
|
|
168
|
+
viewportWidth: number;
|
|
169
|
+
viewportHeight: number;
|
|
170
|
+
identifiedUserKey: string;
|
|
171
|
+
/* Present only when the caller holds the narrower identity permission. */
|
|
172
|
+
identifiedUserLabel?: string | undefined;
|
|
173
|
+
samplePercentageAtCapture: number;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface SessionReplayListResult {
|
|
177
|
+
sessions: Array<SessionReplayListItem>;
|
|
178
|
+
nextCursor: SessionReplayListCursor | null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface SessionReplaySessionHeader {
|
|
182
|
+
sessionId: string;
|
|
183
|
+
projectId: string;
|
|
184
|
+
rumApplicationId: string;
|
|
185
|
+
startTime: Date;
|
|
186
|
+
endTime: Date;
|
|
187
|
+
durationMs: number;
|
|
188
|
+
isFinalized: boolean;
|
|
189
|
+
sealedReason: string;
|
|
190
|
+
chunkCount: number;
|
|
191
|
+
maxChunkIndex: number;
|
|
192
|
+
missingChunkCount: number;
|
|
193
|
+
eventCount: number;
|
|
194
|
+
payloadBytes: number;
|
|
195
|
+
hasError: boolean;
|
|
196
|
+
errorCount: number;
|
|
197
|
+
rageClickCount: number;
|
|
198
|
+
deadClickCount: number;
|
|
199
|
+
errorClickCount: number;
|
|
200
|
+
refreshRageCount: number;
|
|
201
|
+
pageCount: number;
|
|
202
|
+
triggerReason: string;
|
|
203
|
+
maskingMode: string;
|
|
204
|
+
consentState: string;
|
|
205
|
+
recorderKind: string;
|
|
206
|
+
recorderVersion: string;
|
|
207
|
+
rrwebVersion: string;
|
|
208
|
+
schemaVersion: number;
|
|
209
|
+
wireVersion: number;
|
|
210
|
+
entryUrl: string;
|
|
211
|
+
exitUrl: string;
|
|
212
|
+
routes: Array<string>;
|
|
213
|
+
browserName: string;
|
|
214
|
+
browserVersion: string;
|
|
215
|
+
osName: string;
|
|
216
|
+
deviceType: string;
|
|
217
|
+
countryCode: string;
|
|
218
|
+
viewportWidth: number;
|
|
219
|
+
viewportHeight: number;
|
|
220
|
+
fidelityNotices: Array<string>;
|
|
221
|
+
fullSnapshotChunkIndexes: Array<number>;
|
|
222
|
+
traceIds: Array<string>;
|
|
223
|
+
exceptionFingerprints: Array<string>;
|
|
224
|
+
clockSkewMs: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/*
|
|
228
|
+
* One tab's slice of the manifest. chunkIndex is minted PER TAB by the
|
|
229
|
+
* recorder (sessionStorage is copied on tab duplication, so two live tabs
|
|
230
|
+
* legitimately both start at 0), which means gap detection and seek
|
|
231
|
+
* anchors are only meaningful within a tab.
|
|
232
|
+
*/
|
|
233
|
+
export interface SessionReplayManifestTab {
|
|
234
|
+
tabId: string;
|
|
235
|
+
chunks: Array<SessionReplayChunkManifestEntry>;
|
|
236
|
+
chunkIndexes: Array<number>;
|
|
237
|
+
fullSnapshotChunkIndexes: Array<number>;
|
|
238
|
+
gaps: Array<SessionReplayGap>;
|
|
239
|
+
maxChunkIndex: number;
|
|
240
|
+
totalPayloadBytes: number;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface SessionReplayManifest {
|
|
244
|
+
header: SessionReplaySessionHeader;
|
|
245
|
+
tabs: Array<SessionReplayManifestTab>;
|
|
246
|
+
/*
|
|
247
|
+
* True when the chunk index itself was cut short by MAX_MANIFEST_ROWS.
|
|
248
|
+
* Surfaced so the player can say the timeline is incomplete rather than
|
|
249
|
+
* presenting a short recording as a whole one.
|
|
250
|
+
*/
|
|
251
|
+
isChunkIndexTruncated: boolean;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface SessionReplayChunkPayload {
|
|
255
|
+
chunkIndex: number;
|
|
256
|
+
payload: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface SessionReplayExceptionSession {
|
|
260
|
+
sessionId: string;
|
|
261
|
+
rumApplicationId: string;
|
|
262
|
+
startTime: Date;
|
|
263
|
+
endTime: Date;
|
|
264
|
+
durationMs: number;
|
|
265
|
+
hasError: boolean;
|
|
266
|
+
errorCount: number;
|
|
267
|
+
rageClickCount: number;
|
|
268
|
+
deadClickCount: number;
|
|
269
|
+
errorClickCount: number;
|
|
270
|
+
refreshRageCount: number;
|
|
271
|
+
maskingMode: string;
|
|
272
|
+
triggerReason: string;
|
|
273
|
+
entryUrl: string;
|
|
274
|
+
browserName: string;
|
|
275
|
+
osName: string;
|
|
276
|
+
deviceType: string;
|
|
277
|
+
isFinalized: boolean;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* Header columns that are aggregated with argMax and their SELECT alias.
|
|
282
|
+
* Kept as data rather than a hand-written SELECT list so the list, header
|
|
283
|
+
* and exception-lookup queries cannot drift apart on which columns they
|
|
284
|
+
* de-duplicate.
|
|
285
|
+
*
|
|
286
|
+
* `expression` wraps 64-bit and 128-bit columns in toFloat64: the
|
|
287
|
+
* ClickHouse JSON format quotes Int64/UInt64/Int128 as strings by
|
|
288
|
+
* default, and unix-millisecond and byte-count magnitudes are far inside
|
|
289
|
+
* Float64's exact-integer range.
|
|
290
|
+
*/
|
|
291
|
+
interface AggregatedColumn {
|
|
292
|
+
alias: string;
|
|
293
|
+
expression: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function argMaxColumn(column: string): string {
|
|
297
|
+
return `argMax(${column}, version)`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function argMaxNumeric(column: string): string {
|
|
301
|
+
return `toFloat64(${argMaxColumn(column)})`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function argMaxDateTime(column: string): string {
|
|
305
|
+
return `toFloat64(toUnixTimestamp64Milli(${argMaxColumn(column)}))`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* Aliases deliberately differ from the physical column names. See the
|
|
310
|
+
* ILLEGAL_AGGREGATION note in the file header.
|
|
311
|
+
*/
|
|
312
|
+
const HEADER_AGGREGATES: Array<AggregatedColumn> = [
|
|
313
|
+
{ alias: "aggStartTime", expression: argMaxDateTime("startTime") },
|
|
314
|
+
{ alias: "aggEndTime", expression: argMaxDateTime("endTime") },
|
|
315
|
+
{ alias: "aggDurationMs", expression: argMaxNumeric("durationMs") },
|
|
316
|
+
{ alias: "aggIsFinalized", expression: argMaxColumn("isFinalized") },
|
|
317
|
+
{ alias: "aggSealedReason", expression: argMaxColumn("sealedReason") },
|
|
318
|
+
{ alias: "aggChunkCount", expression: argMaxNumeric("chunkCount") },
|
|
319
|
+
{ alias: "aggMaxChunkIndex", expression: argMaxNumeric("maxChunkIndex") },
|
|
320
|
+
{
|
|
321
|
+
alias: "aggMissingChunkCount",
|
|
322
|
+
expression: argMaxNumeric("missingChunkCount"),
|
|
323
|
+
},
|
|
324
|
+
{ alias: "aggEventCount", expression: argMaxNumeric("eventCount") },
|
|
325
|
+
{ alias: "aggPayloadBytes", expression: argMaxNumeric("payloadBytes") },
|
|
326
|
+
{ alias: "aggHasError", expression: argMaxColumn("hasError") },
|
|
327
|
+
{ alias: "aggErrorCount", expression: argMaxNumeric("errorCount") },
|
|
328
|
+
{ alias: "aggRageClickCount", expression: argMaxNumeric("rageClickCount") },
|
|
329
|
+
{ alias: "aggDeadClickCount", expression: argMaxNumeric("deadClickCount") },
|
|
330
|
+
{ alias: "aggErrorClickCount", expression: argMaxNumeric("errorClickCount") },
|
|
331
|
+
{
|
|
332
|
+
alias: "aggRefreshRageCount",
|
|
333
|
+
expression: argMaxNumeric("refreshRageCount"),
|
|
334
|
+
},
|
|
335
|
+
{ alias: "aggPageCount", expression: argMaxNumeric("pageCount") },
|
|
336
|
+
{ alias: "aggTriggerReason", expression: argMaxColumn("triggerReason") },
|
|
337
|
+
{ alias: "aggEntryUrl", expression: argMaxColumn("entryUrl") },
|
|
338
|
+
{ alias: "aggExitUrl", expression: argMaxColumn("exitUrl") },
|
|
339
|
+
{ alias: "aggBrowserName", expression: argMaxColumn("browserName") },
|
|
340
|
+
{ alias: "aggBrowserVersion", expression: argMaxColumn("browserVersion") },
|
|
341
|
+
{ alias: "aggOsName", expression: argMaxColumn("osName") },
|
|
342
|
+
{ alias: "aggDeviceType", expression: argMaxColumn("deviceType") },
|
|
343
|
+
{ alias: "aggCountryCode", expression: argMaxColumn("countryCode") },
|
|
344
|
+
{ alias: "aggViewportWidth", expression: argMaxNumeric("viewportWidth") },
|
|
345
|
+
{ alias: "aggViewportHeight", expression: argMaxNumeric("viewportHeight") },
|
|
346
|
+
{
|
|
347
|
+
alias: "aggIdentifiedUserKey",
|
|
348
|
+
expression: argMaxColumn("identifiedUserKey"),
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
alias: "aggSamplePercentage",
|
|
352
|
+
expression: argMaxNumeric("samplePercentageAtCapture"),
|
|
353
|
+
},
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
/* Only the manifest needs these; the list never renders them. */
|
|
357
|
+
const HEADER_DETAIL_AGGREGATES: Array<AggregatedColumn> = [
|
|
358
|
+
{ alias: "aggMaskingMode", expression: argMaxColumn("maskingMode") },
|
|
359
|
+
{ alias: "aggConsentState", expression: argMaxColumn("consentState") },
|
|
360
|
+
{ alias: "aggRecorderKind", expression: argMaxColumn("recorderKind") },
|
|
361
|
+
{ alias: "aggRecorderVersion", expression: argMaxColumn("recorderVersion") },
|
|
362
|
+
{ alias: "aggRrwebVersion", expression: argMaxColumn("rrwebVersion") },
|
|
363
|
+
{ alias: "aggSchemaVersion", expression: argMaxNumeric("schemaVersion") },
|
|
364
|
+
{ alias: "aggWireVersion", expression: argMaxNumeric("wireVersion") },
|
|
365
|
+
{ alias: "aggRoutes", expression: argMaxColumn("routes") },
|
|
366
|
+
{ alias: "aggFidelityNotices", expression: argMaxColumn("fidelityNotices") },
|
|
367
|
+
{
|
|
368
|
+
alias: "aggFullSnapshotChunkIndexes",
|
|
369
|
+
expression: argMaxColumn("fullSnapshotChunkIndexes"),
|
|
370
|
+
},
|
|
371
|
+
{ alias: "aggTraceIds", expression: argMaxColumn("traceIds") },
|
|
372
|
+
{
|
|
373
|
+
alias: "aggExceptionFingerprints",
|
|
374
|
+
expression: argMaxColumn("exceptionFingerprints"),
|
|
375
|
+
},
|
|
376
|
+
{ alias: "aggClockSkewMs", expression: argMaxNumeric("clockSkewMs") },
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
function toSelectList(columns: Array<AggregatedColumn>): string {
|
|
380
|
+
return columns
|
|
381
|
+
.map((column: AggregatedColumn): string => {
|
|
382
|
+
return `${column.expression} AS ${column.alias}`;
|
|
383
|
+
})
|
|
384
|
+
.join(",\n ");
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function readNumber(row: JSONObject, key: string): number {
|
|
388
|
+
const value: unknown = row[key];
|
|
389
|
+
|
|
390
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/*
|
|
395
|
+
* ClickHouse quotes 64-bit integers in JSON. Everything wide is wrapped
|
|
396
|
+
* in toFloat64 above, but parse defensively so one un-wrapped column
|
|
397
|
+
* added later degrades to a number rather than to NaN in the UI.
|
|
398
|
+
*/
|
|
399
|
+
if (typeof value === "string" && value.length > 0) {
|
|
400
|
+
const parsed: number = Number(value);
|
|
401
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function readBoolean(row: JSONObject, key: string): boolean {
|
|
408
|
+
const value: unknown = row[key];
|
|
409
|
+
|
|
410
|
+
if (typeof value === "boolean") {
|
|
411
|
+
return value;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return value === 1 || value === "1" || value === "true";
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function readString(row: JSONObject, key: string): string {
|
|
418
|
+
const value: unknown = row[key];
|
|
419
|
+
|
|
420
|
+
return typeof value === "string" ? value : "";
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function readStringArray(row: JSONObject, key: string): Array<string> {
|
|
424
|
+
const value: unknown = row[key];
|
|
425
|
+
|
|
426
|
+
if (!Array.isArray(value)) {
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return value
|
|
431
|
+
.filter((item: unknown): item is string => {
|
|
432
|
+
return typeof item === "string";
|
|
433
|
+
})
|
|
434
|
+
.map((item: string): string => {
|
|
435
|
+
return item;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function readNumberArray(row: JSONObject, key: string): Array<number> {
|
|
440
|
+
const value: unknown = row[key];
|
|
441
|
+
|
|
442
|
+
if (!Array.isArray(value)) {
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const numbers: Array<number> = [];
|
|
447
|
+
|
|
448
|
+
for (const item of value) {
|
|
449
|
+
const parsed: number = typeof item === "number" ? item : Number(item);
|
|
450
|
+
|
|
451
|
+
if (Number.isFinite(parsed)) {
|
|
452
|
+
numbers.push(parsed);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return numbers;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/*
|
|
460
|
+
* Unix millis -> Date. The queries return epoch milliseconds as a Float64
|
|
461
|
+
* precisely so no ClickHouse datetime string ever has to be re-parsed
|
|
462
|
+
* (its "YYYY-MM-DD hh:mm:ss.nnnnnnnnn" form has no timezone and is a
|
|
463
|
+
* long-standing source of off-by-hours bugs).
|
|
464
|
+
*/
|
|
465
|
+
function readDate(row: JSONObject, key: string): Date {
|
|
466
|
+
return new Date(readNumber(row, key));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export default class SessionReplayReadService {
|
|
470
|
+
/*
|
|
471
|
+
* Session list.
|
|
472
|
+
*
|
|
473
|
+
* projectId / rumApplicationId / startTime go in the WHERE because they
|
|
474
|
+
* are the first three elements of the sort key AND they are part of the
|
|
475
|
+
* ReplacingMergeTree replace key, so they are byte-identical on every
|
|
476
|
+
* duplicate row of a session. Filtering on them before the GROUP BY is
|
|
477
|
+
* therefore both index-friendly and safe.
|
|
478
|
+
*
|
|
479
|
+
* Everything else is filtered in HAVING against the argMax'd value.
|
|
480
|
+
* That is not a style choice: a provisional header (written on chunk 0,
|
|
481
|
+
* before anything is known) reports hasError = false and errorCount = 0
|
|
482
|
+
* for a session the finalizer later marks as errored. A WHERE on those
|
|
483
|
+
* columns would match the stale row and the argMax would then report
|
|
484
|
+
* the truth - or, worse, would drop the group entirely.
|
|
485
|
+
*/
|
|
486
|
+
@CaptureSpan()
|
|
487
|
+
public static async listSessions(
|
|
488
|
+
request: SessionReplayListRequest,
|
|
489
|
+
): Promise<SessionReplayListResult> {
|
|
490
|
+
const limit: number = Math.max(
|
|
491
|
+
1,
|
|
492
|
+
Math.min(request.limit, MAX_SESSION_REPLAY_LIST_LIMIT),
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const selectList: string = toSelectList(HEADER_AGGREGATES);
|
|
496
|
+
|
|
497
|
+
const statement: Statement = SQL`
|
|
498
|
+
SELECT
|
|
499
|
+
sessionId,
|
|
500
|
+
toString(rumApplicationId) AS applicationId,
|
|
501
|
+
`;
|
|
502
|
+
|
|
503
|
+
statement.append(` ${selectList}`);
|
|
504
|
+
|
|
505
|
+
if (request.includeIdentifiedUserLabel) {
|
|
506
|
+
statement.append(
|
|
507
|
+
`,\n ${argMaxColumn("identifiedUserLabel")} AS aggIdentifiedUserLabel`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
statement.append(SQL`
|
|
512
|
+
FROM ${AnalyticsTableName.RumSession}
|
|
513
|
+
WHERE projectId = ${{
|
|
514
|
+
type: TableColumnType.ObjectID,
|
|
515
|
+
value: request.projectId,
|
|
516
|
+
}}
|
|
517
|
+
AND rumApplicationId = ${{
|
|
518
|
+
type: TableColumnType.ObjectID,
|
|
519
|
+
value: request.rumApplicationId,
|
|
520
|
+
}}
|
|
521
|
+
AND startTime >= ${{
|
|
522
|
+
type: TableColumnType.DateTime64,
|
|
523
|
+
value: request.startTime,
|
|
524
|
+
}}
|
|
525
|
+
AND startTime <= ${{
|
|
526
|
+
type: TableColumnType.DateTime64,
|
|
527
|
+
value: request.endTime,
|
|
528
|
+
}}
|
|
529
|
+
`);
|
|
530
|
+
|
|
531
|
+
statement.append(RETENTION_FILTER);
|
|
532
|
+
|
|
533
|
+
/*
|
|
534
|
+
* Keyset cursor. The sort key is (projectId, rumApplicationId,
|
|
535
|
+
* startTime, sessionId) and the list is ordered by the same tuple
|
|
536
|
+
* descending, so the previous page's last startTime is a valid
|
|
537
|
+
* WHERE-level upper bound: it prunes granules instead of paging with
|
|
538
|
+
* OFFSET, which on a wide time window would re-read and re-aggregate
|
|
539
|
+
* everything already returned. The exact ties are removed by the
|
|
540
|
+
* HAVING tiebreak below - the WHERE bound is deliberately inclusive
|
|
541
|
+
* so a row sharing the boundary timestamp is not skipped.
|
|
542
|
+
*/
|
|
543
|
+
if (request.cursor) {
|
|
544
|
+
statement.append(
|
|
545
|
+
SQL` AND startTime <= ${{
|
|
546
|
+
type: TableColumnType.DateTime64,
|
|
547
|
+
value: new Date(request.cursor.startTimeUnixMs),
|
|
548
|
+
}}`,
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
statement.append(
|
|
553
|
+
" GROUP BY projectId, rumApplicationId, sessionId\n HAVING 1 = 1",
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
SessionReplayReadService.appendListHavingFilters(
|
|
557
|
+
statement,
|
|
558
|
+
request.filters,
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
if (request.cursor) {
|
|
562
|
+
statement.append(
|
|
563
|
+
SQL` AND (aggStartTime < ${{
|
|
564
|
+
type: TableColumnType.Decimal,
|
|
565
|
+
value: request.cursor.startTimeUnixMs,
|
|
566
|
+
}} OR (aggStartTime = ${{
|
|
567
|
+
type: TableColumnType.Decimal,
|
|
568
|
+
value: request.cursor.startTimeUnixMs,
|
|
569
|
+
}} AND sessionId < ${{
|
|
570
|
+
type: TableColumnType.Text,
|
|
571
|
+
value: request.cursor.sessionId,
|
|
572
|
+
}}))`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
statement.append(
|
|
577
|
+
SQL` ORDER BY aggStartTime DESC, sessionId DESC
|
|
578
|
+
LIMIT ${{
|
|
579
|
+
type: TableColumnType.Number,
|
|
580
|
+
value: limit + 1,
|
|
581
|
+
}}`,
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
statement.append(READ_QUERY_SETTINGS);
|
|
585
|
+
|
|
586
|
+
const dbResult: Results = await RumSessionService.executeQuery(statement);
|
|
587
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
588
|
+
data?: Array<JSONObject>;
|
|
589
|
+
}>();
|
|
590
|
+
|
|
591
|
+
const rows: Array<JSONObject> = response.data || [];
|
|
592
|
+
|
|
593
|
+
/*
|
|
594
|
+
* One row over the page size is fetched purely to learn whether a
|
|
595
|
+
* next page exists without a second COUNT query over the same
|
|
596
|
+
* aggregation.
|
|
597
|
+
*/
|
|
598
|
+
const hasMore: boolean = rows.length > limit;
|
|
599
|
+
const pageRows: Array<JSONObject> = hasMore ? rows.slice(0, limit) : rows;
|
|
600
|
+
|
|
601
|
+
const sessions: Array<SessionReplayListItem> = pageRows.map(
|
|
602
|
+
(row: JSONObject): SessionReplayListItem => {
|
|
603
|
+
const item: SessionReplayListItem = {
|
|
604
|
+
sessionId: readString(row, "sessionId"),
|
|
605
|
+
rumApplicationId: readString(row, "applicationId"),
|
|
606
|
+
startTime: readDate(row, "aggStartTime"),
|
|
607
|
+
endTime: readDate(row, "aggEndTime"),
|
|
608
|
+
durationMs: readNumber(row, "aggDurationMs"),
|
|
609
|
+
isFinalized: readBoolean(row, "aggIsFinalized"),
|
|
610
|
+
sealedReason: readString(row, "aggSealedReason"),
|
|
611
|
+
chunkCount: readNumber(row, "aggChunkCount"),
|
|
612
|
+
maxChunkIndex: readNumber(row, "aggMaxChunkIndex"),
|
|
613
|
+
missingChunkCount: readNumber(row, "aggMissingChunkCount"),
|
|
614
|
+
eventCount: readNumber(row, "aggEventCount"),
|
|
615
|
+
payloadBytes: readNumber(row, "aggPayloadBytes"),
|
|
616
|
+
hasError: readBoolean(row, "aggHasError"),
|
|
617
|
+
errorCount: readNumber(row, "aggErrorCount"),
|
|
618
|
+
rageClickCount: readNumber(row, "aggRageClickCount"),
|
|
619
|
+
deadClickCount: readNumber(row, "aggDeadClickCount"),
|
|
620
|
+
errorClickCount: readNumber(row, "aggErrorClickCount"),
|
|
621
|
+
refreshRageCount: readNumber(row, "aggRefreshRageCount"),
|
|
622
|
+
pageCount: readNumber(row, "aggPageCount"),
|
|
623
|
+
triggerReason: readString(row, "aggTriggerReason"),
|
|
624
|
+
entryUrl: readString(row, "aggEntryUrl"),
|
|
625
|
+
exitUrl: readString(row, "aggExitUrl"),
|
|
626
|
+
browserName: readString(row, "aggBrowserName"),
|
|
627
|
+
browserVersion: readString(row, "aggBrowserVersion"),
|
|
628
|
+
osName: readString(row, "aggOsName"),
|
|
629
|
+
deviceType: readString(row, "aggDeviceType"),
|
|
630
|
+
countryCode: readString(row, "aggCountryCode"),
|
|
631
|
+
viewportWidth: readNumber(row, "aggViewportWidth"),
|
|
632
|
+
viewportHeight: readNumber(row, "aggViewportHeight"),
|
|
633
|
+
identifiedUserKey: readString(row, "aggIdentifiedUserKey"),
|
|
634
|
+
samplePercentageAtCapture: readNumber(row, "aggSamplePercentage"),
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
if (request.includeIdentifiedUserLabel) {
|
|
638
|
+
item.identifiedUserLabel = readString(row, "aggIdentifiedUserLabel");
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return item;
|
|
642
|
+
},
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
const lastSession: SessionReplayListItem | undefined =
|
|
646
|
+
sessions[sessions.length - 1];
|
|
647
|
+
|
|
648
|
+
return {
|
|
649
|
+
sessions: sessions,
|
|
650
|
+
nextCursor:
|
|
651
|
+
hasMore && lastSession
|
|
652
|
+
? {
|
|
653
|
+
startTimeUnixMs: lastSession.startTime.getTime(),
|
|
654
|
+
sessionId: lastSession.sessionId,
|
|
655
|
+
}
|
|
656
|
+
: null,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/*
|
|
661
|
+
* The single header row for one session, de-duplicated the same way the
|
|
662
|
+
* list is.
|
|
663
|
+
*
|
|
664
|
+
* This is also what resolves a sessionId to its owning RUM application
|
|
665
|
+
* for the handler-level authorization check, which is why it is keyed
|
|
666
|
+
* on (projectId, sessionId) only and never accepts an application id
|
|
667
|
+
* from the caller: an application id supplied in the request body would
|
|
668
|
+
* make the check circular.
|
|
669
|
+
*/
|
|
670
|
+
@CaptureSpan()
|
|
671
|
+
public static async getSessionHeader(data: {
|
|
672
|
+
projectId: ObjectID;
|
|
673
|
+
sessionId: string;
|
|
674
|
+
}): Promise<SessionReplaySessionHeader | null> {
|
|
675
|
+
const selectList: string = toSelectList([
|
|
676
|
+
...HEADER_AGGREGATES,
|
|
677
|
+
...HEADER_DETAIL_AGGREGATES,
|
|
678
|
+
]);
|
|
679
|
+
|
|
680
|
+
const statement: Statement = SQL`
|
|
681
|
+
SELECT
|
|
682
|
+
sessionId,
|
|
683
|
+
toString(projectId) AS headerProjectId,
|
|
684
|
+
toString(rumApplicationId) AS applicationId,
|
|
685
|
+
`;
|
|
686
|
+
|
|
687
|
+
statement.append(` ${selectList}`);
|
|
688
|
+
|
|
689
|
+
statement.append(SQL`
|
|
690
|
+
FROM ${AnalyticsTableName.RumSession}
|
|
691
|
+
WHERE projectId = ${{
|
|
692
|
+
type: TableColumnType.ObjectID,
|
|
693
|
+
value: data.projectId,
|
|
694
|
+
}}
|
|
695
|
+
AND sessionId = ${{
|
|
696
|
+
type: TableColumnType.Text,
|
|
697
|
+
value: data.sessionId,
|
|
698
|
+
}}
|
|
699
|
+
`);
|
|
700
|
+
|
|
701
|
+
statement.append(RETENTION_FILTER);
|
|
702
|
+
|
|
703
|
+
/*
|
|
704
|
+
* Grouped by the full replace-key identity minus startTime, so one
|
|
705
|
+
* group per (application, session).
|
|
706
|
+
*
|
|
707
|
+
* LIMIT 2, not LIMIT 1. sessionId is minted by the browser and is
|
|
708
|
+
* therefore fully caller-controlled, while the chunk table's replace
|
|
709
|
+
* key is (projectId, sessionId, tabId, chunkIndex) with
|
|
710
|
+
* rumApplicationId a plain column - two applications sharing a
|
|
711
|
+
* sessionId share a key space. Picking the newest group would let
|
|
712
|
+
* anyone who can write to application A resolve a sessionId belonging
|
|
713
|
+
* to application B onto their own application and pass the label
|
|
714
|
+
* check. An ambiguous sessionId is refused outright instead: it is
|
|
715
|
+
* either an attack or a collision, and neither has a correct
|
|
716
|
+
* recording to return.
|
|
717
|
+
*/
|
|
718
|
+
statement.append(
|
|
719
|
+
" GROUP BY projectId, rumApplicationId, sessionId ORDER BY aggStartTime DESC LIMIT 2",
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
statement.append(READ_QUERY_SETTINGS);
|
|
723
|
+
|
|
724
|
+
const dbResult: Results = await RumSessionService.executeQuery(statement);
|
|
725
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
726
|
+
data?: Array<JSONObject>;
|
|
727
|
+
}>();
|
|
728
|
+
|
|
729
|
+
const rows: Array<JSONObject> = response.data || [];
|
|
730
|
+
|
|
731
|
+
if (rows.length > 1) {
|
|
732
|
+
throw new BadDataException(
|
|
733
|
+
"This session id resolves to more than one RUM application and cannot be played back.",
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const row: JSONObject | undefined = rows[0];
|
|
738
|
+
|
|
739
|
+
if (!row) {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return {
|
|
744
|
+
sessionId: readString(row, "sessionId"),
|
|
745
|
+
projectId: readString(row, "headerProjectId"),
|
|
746
|
+
rumApplicationId: readString(row, "applicationId"),
|
|
747
|
+
startTime: readDate(row, "aggStartTime"),
|
|
748
|
+
endTime: readDate(row, "aggEndTime"),
|
|
749
|
+
durationMs: readNumber(row, "aggDurationMs"),
|
|
750
|
+
isFinalized: readBoolean(row, "aggIsFinalized"),
|
|
751
|
+
sealedReason: readString(row, "aggSealedReason"),
|
|
752
|
+
chunkCount: readNumber(row, "aggChunkCount"),
|
|
753
|
+
maxChunkIndex: readNumber(row, "aggMaxChunkIndex"),
|
|
754
|
+
missingChunkCount: readNumber(row, "aggMissingChunkCount"),
|
|
755
|
+
eventCount: readNumber(row, "aggEventCount"),
|
|
756
|
+
payloadBytes: readNumber(row, "aggPayloadBytes"),
|
|
757
|
+
hasError: readBoolean(row, "aggHasError"),
|
|
758
|
+
errorCount: readNumber(row, "aggErrorCount"),
|
|
759
|
+
rageClickCount: readNumber(row, "aggRageClickCount"),
|
|
760
|
+
deadClickCount: readNumber(row, "aggDeadClickCount"),
|
|
761
|
+
errorClickCount: readNumber(row, "aggErrorClickCount"),
|
|
762
|
+
refreshRageCount: readNumber(row, "aggRefreshRageCount"),
|
|
763
|
+
pageCount: readNumber(row, "aggPageCount"),
|
|
764
|
+
triggerReason: readString(row, "aggTriggerReason"),
|
|
765
|
+
maskingMode: readString(row, "aggMaskingMode"),
|
|
766
|
+
consentState: readString(row, "aggConsentState"),
|
|
767
|
+
recorderKind: readString(row, "aggRecorderKind"),
|
|
768
|
+
recorderVersion: readString(row, "aggRecorderVersion"),
|
|
769
|
+
rrwebVersion: readString(row, "aggRrwebVersion"),
|
|
770
|
+
schemaVersion: readNumber(row, "aggSchemaVersion"),
|
|
771
|
+
wireVersion: readNumber(row, "aggWireVersion"),
|
|
772
|
+
entryUrl: readString(row, "aggEntryUrl"),
|
|
773
|
+
exitUrl: readString(row, "aggExitUrl"),
|
|
774
|
+
routes: readStringArray(row, "aggRoutes"),
|
|
775
|
+
browserName: readString(row, "aggBrowserName"),
|
|
776
|
+
browserVersion: readString(row, "aggBrowserVersion"),
|
|
777
|
+
osName: readString(row, "aggOsName"),
|
|
778
|
+
deviceType: readString(row, "aggDeviceType"),
|
|
779
|
+
countryCode: readString(row, "aggCountryCode"),
|
|
780
|
+
viewportWidth: readNumber(row, "aggViewportWidth"),
|
|
781
|
+
viewportHeight: readNumber(row, "aggViewportHeight"),
|
|
782
|
+
fidelityNotices: readStringArray(row, "aggFidelityNotices"),
|
|
783
|
+
fullSnapshotChunkIndexes: readNumberArray(
|
|
784
|
+
row,
|
|
785
|
+
"aggFullSnapshotChunkIndexes",
|
|
786
|
+
),
|
|
787
|
+
traceIds: readStringArray(row, "aggTraceIds"),
|
|
788
|
+
exceptionFingerprints: readStringArray(row, "aggExceptionFingerprints"),
|
|
789
|
+
clockSkewMs: readNumber(row, "aggClockSkewMs"),
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/*
|
|
794
|
+
* Playback manifest: everything the player needs to draw a complete,
|
|
795
|
+
* honest timeline without fetching one payload byte.
|
|
796
|
+
*
|
|
797
|
+
* The `payload` column is deliberately absent from this SELECT. That is
|
|
798
|
+
* the entire performance story of the feature: a 14-chunk session is
|
|
799
|
+
* one 128-row granule of a handful of narrow columns (~2 KB) instead of
|
|
800
|
+
* megabytes of decompressed recording.
|
|
801
|
+
*/
|
|
802
|
+
@CaptureSpan()
|
|
803
|
+
public static async getManifest(data: {
|
|
804
|
+
header: SessionReplaySessionHeader;
|
|
805
|
+
projectId: ObjectID;
|
|
806
|
+
/*
|
|
807
|
+
* The application the caller was actually authorized against, always
|
|
808
|
+
* the one resolved from the session header server-side. Every chunk
|
|
809
|
+
* read is pinned to it: the chunk table's replace key does not
|
|
810
|
+
* include rumApplicationId, so (projectId, sessionId) alone is not a
|
|
811
|
+
* tenant-safe key once a sessionId can be reused across
|
|
812
|
+
* applications.
|
|
813
|
+
*/
|
|
814
|
+
rumApplicationId: ObjectID;
|
|
815
|
+
sessionId: string;
|
|
816
|
+
}): Promise<SessionReplayManifest> {
|
|
817
|
+
/*
|
|
818
|
+
* LIMIT 1 BY (tabId, chunkIndex) after ORDER BY ... version DESC
|
|
819
|
+
* keeps exactly the highest-version row per chunk. tabId is part of
|
|
820
|
+
* the group because chunkIndex is minted per tab. Ordering by
|
|
821
|
+
* tabId/chunkIndex first (rather than by version alone) leaves the
|
|
822
|
+
* output already sorted for the caller - LIMIT BY runs after ORDER
|
|
823
|
+
* BY, so the version DESC tiebreak still selects the right row.
|
|
824
|
+
*/
|
|
825
|
+
const statement: Statement = SQL`
|
|
826
|
+
SELECT
|
|
827
|
+
tabId,
|
|
828
|
+
chunkIndex,
|
|
829
|
+
chunkStartOffsetMs,
|
|
830
|
+
chunkEndOffsetMs,
|
|
831
|
+
eventCount,
|
|
832
|
+
hasFullSnapshot,
|
|
833
|
+
toFloat64(payloadBytes) AS chunkPayloadBytes,
|
|
834
|
+
errorCount,
|
|
835
|
+
rageClickCount,
|
|
836
|
+
deadClickCount,
|
|
837
|
+
errorClickCount,
|
|
838
|
+
refreshRageCount,
|
|
839
|
+
routeCount
|
|
840
|
+
FROM ${AnalyticsTableName.RumSessionChunk}
|
|
841
|
+
WHERE projectId = ${{
|
|
842
|
+
type: TableColumnType.ObjectID,
|
|
843
|
+
value: data.projectId,
|
|
844
|
+
}}
|
|
845
|
+
AND rumApplicationId = ${{
|
|
846
|
+
type: TableColumnType.ObjectID,
|
|
847
|
+
value: data.rumApplicationId,
|
|
848
|
+
}}
|
|
849
|
+
AND sessionId = ${{
|
|
850
|
+
type: TableColumnType.Text,
|
|
851
|
+
value: data.sessionId,
|
|
852
|
+
}}
|
|
853
|
+
`;
|
|
854
|
+
|
|
855
|
+
statement.append(RETENTION_FILTER);
|
|
856
|
+
|
|
857
|
+
statement.append(
|
|
858
|
+
SQL` ORDER BY tabId ASC, chunkIndex ASC, version DESC
|
|
859
|
+
LIMIT 1 BY tabId, chunkIndex
|
|
860
|
+
LIMIT ${{
|
|
861
|
+
type: TableColumnType.Number,
|
|
862
|
+
value: MAX_MANIFEST_ROWS,
|
|
863
|
+
}}`,
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
statement.append(READ_QUERY_SETTINGS);
|
|
867
|
+
|
|
868
|
+
const dbResult: Results =
|
|
869
|
+
await RumSessionChunkService.executeQuery(statement);
|
|
870
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
871
|
+
data?: Array<JSONObject>;
|
|
872
|
+
}>();
|
|
873
|
+
|
|
874
|
+
const rows: Array<JSONObject> = response.data || [];
|
|
875
|
+
|
|
876
|
+
const tabsById: Map<
|
|
877
|
+
string,
|
|
878
|
+
Array<SessionReplayChunkManifestEntry>
|
|
879
|
+
> = new Map<string, Array<SessionReplayChunkManifestEntry>>();
|
|
880
|
+
|
|
881
|
+
for (const row of rows) {
|
|
882
|
+
const tabId: string = readString(row, "tabId");
|
|
883
|
+
|
|
884
|
+
const entry: SessionReplayChunkManifestEntry = {
|
|
885
|
+
chunkIndex: readNumber(row, "chunkIndex"),
|
|
886
|
+
tabId: tabId,
|
|
887
|
+
chunkStartOffsetMs: readNumber(row, "chunkStartOffsetMs"),
|
|
888
|
+
chunkEndOffsetMs: readNumber(row, "chunkEndOffsetMs"),
|
|
889
|
+
eventCount: readNumber(row, "eventCount"),
|
|
890
|
+
hasFullSnapshot: readBoolean(row, "hasFullSnapshot"),
|
|
891
|
+
payloadBytes: readNumber(row, "chunkPayloadBytes"),
|
|
892
|
+
errorCount: readNumber(row, "errorCount"),
|
|
893
|
+
rageClickCount: readNumber(row, "rageClickCount"),
|
|
894
|
+
deadClickCount: readNumber(row, "deadClickCount"),
|
|
895
|
+
errorClickCount: readNumber(row, "errorClickCount"),
|
|
896
|
+
refreshRageCount: readNumber(row, "refreshRageCount"),
|
|
897
|
+
routeCount: readNumber(row, "routeCount"),
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
const existing: Array<SessionReplayChunkManifestEntry> | undefined =
|
|
901
|
+
tabsById.get(tabId);
|
|
902
|
+
|
|
903
|
+
if (existing) {
|
|
904
|
+
existing.push(entry);
|
|
905
|
+
} else {
|
|
906
|
+
tabsById.set(tabId, [entry]);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const tabs: Array<SessionReplayManifestTab> = [];
|
|
911
|
+
|
|
912
|
+
for (const [tabId, entries] of tabsById) {
|
|
913
|
+
tabs.push({
|
|
914
|
+
tabId: tabId,
|
|
915
|
+
chunks: entries,
|
|
916
|
+
chunkIndexes: entries.map(
|
|
917
|
+
(entry: SessionReplayChunkManifestEntry): number => {
|
|
918
|
+
return entry.chunkIndex;
|
|
919
|
+
},
|
|
920
|
+
),
|
|
921
|
+
/*
|
|
922
|
+
* Seek anchors come from the chunk rows rather than from the
|
|
923
|
+
* header's fullSnapshotChunkIndexes: the header is only written
|
|
924
|
+
* by the finalizer, so a still-recording session would otherwise
|
|
925
|
+
* have no anchors at all and could not be scrubbed.
|
|
926
|
+
*/
|
|
927
|
+
fullSnapshotChunkIndexes: entries
|
|
928
|
+
.filter((entry: SessionReplayChunkManifestEntry): boolean => {
|
|
929
|
+
return entry.hasFullSnapshot;
|
|
930
|
+
})
|
|
931
|
+
.map((entry: SessionReplayChunkManifestEntry): number => {
|
|
932
|
+
return entry.chunkIndex;
|
|
933
|
+
}),
|
|
934
|
+
gaps: ChunkMath.detectGaps(entries),
|
|
935
|
+
maxChunkIndex: entries.reduce(
|
|
936
|
+
(max: number, entry: SessionReplayChunkManifestEntry): number => {
|
|
937
|
+
return Math.max(max, entry.chunkIndex);
|
|
938
|
+
},
|
|
939
|
+
0,
|
|
940
|
+
),
|
|
941
|
+
totalPayloadBytes: entries.reduce(
|
|
942
|
+
(total: number, entry: SessionReplayChunkManifestEntry): number => {
|
|
943
|
+
return total + entry.payloadBytes;
|
|
944
|
+
},
|
|
945
|
+
0,
|
|
946
|
+
),
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
return {
|
|
951
|
+
header: data.header,
|
|
952
|
+
tabs: tabs,
|
|
953
|
+
isChunkIndexTruncated: rows.length >= MAX_MANIFEST_ROWS,
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/*
|
|
958
|
+
* Total STORED bytes for a specific set of chunks, without shipping the
|
|
959
|
+
* payload column to the application.
|
|
960
|
+
*
|
|
961
|
+
* `length(payload)`, deliberately NOT `payloadBytes`. Those are two
|
|
962
|
+
* different quantities: payloadBytes is the post-gzip WIRE size the
|
|
963
|
+
* recorder uploaded (the metering signal), while the payload column
|
|
964
|
+
* holds the DECOMPRESSED JSON and is what this endpoint actually
|
|
965
|
+
* returns. rrweb JSON gzips 10-20x, so a cap applied to payloadBytes
|
|
966
|
+
* bounds a number an order of magnitude smaller than the response and
|
|
967
|
+
* therefore bounds nothing useful.
|
|
968
|
+
*
|
|
969
|
+
* The cost is that ClickHouse has to decompress the column to measure
|
|
970
|
+
* it, which is exactly what naming `payload` was meant to avoid. It is
|
|
971
|
+
* still worth doing before the read rather than after: the bytes are
|
|
972
|
+
* measured inside ClickHouse and never cross the wire, and the marks
|
|
973
|
+
* the following read needs are warm by the time it runs.
|
|
974
|
+
*/
|
|
975
|
+
@CaptureSpan()
|
|
976
|
+
public static async getChunkStoredBytes(data: {
|
|
977
|
+
projectId: ObjectID;
|
|
978
|
+
rumApplicationId: ObjectID;
|
|
979
|
+
sessionId: string;
|
|
980
|
+
tabId: string;
|
|
981
|
+
chunkIndexes: Array<number>;
|
|
982
|
+
}): Promise<number> {
|
|
983
|
+
if (data.chunkIndexes.length === 0) {
|
|
984
|
+
return 0;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const statement: Statement = SQL`
|
|
988
|
+
SELECT
|
|
989
|
+
chunkIndex,
|
|
990
|
+
toFloat64(length(payload)) AS chunkStoredBytes
|
|
991
|
+
FROM ${AnalyticsTableName.RumSessionChunk}
|
|
992
|
+
WHERE projectId = ${{
|
|
993
|
+
type: TableColumnType.ObjectID,
|
|
994
|
+
value: data.projectId,
|
|
995
|
+
}}
|
|
996
|
+
AND rumApplicationId = ${{
|
|
997
|
+
type: TableColumnType.ObjectID,
|
|
998
|
+
value: data.rumApplicationId,
|
|
999
|
+
}}
|
|
1000
|
+
AND sessionId = ${{
|
|
1001
|
+
type: TableColumnType.Text,
|
|
1002
|
+
value: data.sessionId,
|
|
1003
|
+
}}
|
|
1004
|
+
AND tabId = ${{
|
|
1005
|
+
type: TableColumnType.Text,
|
|
1006
|
+
value: data.tabId,
|
|
1007
|
+
}}
|
|
1008
|
+
AND chunkIndex IN (${{
|
|
1009
|
+
type: TableColumnType.Number,
|
|
1010
|
+
value: new Includes(data.chunkIndexes),
|
|
1011
|
+
}})
|
|
1012
|
+
`;
|
|
1013
|
+
|
|
1014
|
+
statement.append(RETENTION_FILTER);
|
|
1015
|
+
|
|
1016
|
+
/*
|
|
1017
|
+
* Deduplicated exactly like the payload read, so the pre-check and
|
|
1018
|
+
* the read it guards can never disagree about which rows count.
|
|
1019
|
+
* Summed in TypeScript rather than in SQL because the row count is
|
|
1020
|
+
* bounded by MAX_SESSION_REPLAY_CHUNKS_PER_READ, and a wrapping
|
|
1021
|
+
* aggregate would need a subquery whose LIMIT BY semantics are
|
|
1022
|
+
* easier to get subtly wrong than to read.
|
|
1023
|
+
*/
|
|
1024
|
+
statement.append(
|
|
1025
|
+
" ORDER BY chunkIndex ASC, version DESC LIMIT 1 BY chunkIndex",
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
statement.append(READ_QUERY_SETTINGS);
|
|
1029
|
+
|
|
1030
|
+
const dbResult: Results =
|
|
1031
|
+
await RumSessionChunkService.executeQuery(statement);
|
|
1032
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
1033
|
+
data?: Array<JSONObject>;
|
|
1034
|
+
}>();
|
|
1035
|
+
|
|
1036
|
+
return (response.data || []).reduce(
|
|
1037
|
+
(total: number, row: JSONObject): number => {
|
|
1038
|
+
return total + readNumber(row, "chunkStoredBytes");
|
|
1039
|
+
},
|
|
1040
|
+
0,
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/*
|
|
1045
|
+
* The payload read. The only query in the system that names the
|
|
1046
|
+
* `payload` column.
|
|
1047
|
+
*
|
|
1048
|
+
* Both caps are enforced here rather than only at the route so no
|
|
1049
|
+
* future caller can reach the payload column without them.
|
|
1050
|
+
*/
|
|
1051
|
+
@CaptureSpan()
|
|
1052
|
+
public static async getChunks(data: {
|
|
1053
|
+
projectId: ObjectID;
|
|
1054
|
+
rumApplicationId: ObjectID;
|
|
1055
|
+
sessionId: string;
|
|
1056
|
+
tabId: string;
|
|
1057
|
+
chunkIndexes: Array<number>;
|
|
1058
|
+
}): Promise<Array<SessionReplayChunkPayload>> {
|
|
1059
|
+
if (data.chunkIndexes.length === 0) {
|
|
1060
|
+
return [];
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (data.chunkIndexes.length > MAX_SESSION_REPLAY_CHUNKS_PER_READ) {
|
|
1064
|
+
throw new BadDataException(
|
|
1065
|
+
`A maximum of ${MAX_SESSION_REPLAY_CHUNKS_PER_READ} chunks may be requested at a time.`,
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
const totalBytes: number =
|
|
1070
|
+
await SessionReplayReadService.getChunkStoredBytes(data);
|
|
1071
|
+
|
|
1072
|
+
if (totalBytes > MAX_SESSION_REPLAY_READ_BYTES) {
|
|
1073
|
+
throw new BadDataException(
|
|
1074
|
+
`The requested chunks total ${totalBytes} bytes, which exceeds the ${MAX_SESSION_REPLAY_READ_BYTES} byte limit for a single read. Request fewer chunks.`,
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const statement: Statement = SQL`
|
|
1079
|
+
SELECT
|
|
1080
|
+
chunkIndex,
|
|
1081
|
+
payload
|
|
1082
|
+
FROM ${AnalyticsTableName.RumSessionChunk}
|
|
1083
|
+
WHERE projectId = ${{
|
|
1084
|
+
type: TableColumnType.ObjectID,
|
|
1085
|
+
value: data.projectId,
|
|
1086
|
+
}}
|
|
1087
|
+
AND rumApplicationId = ${{
|
|
1088
|
+
type: TableColumnType.ObjectID,
|
|
1089
|
+
value: data.rumApplicationId,
|
|
1090
|
+
}}
|
|
1091
|
+
AND sessionId = ${{
|
|
1092
|
+
type: TableColumnType.Text,
|
|
1093
|
+
value: data.sessionId,
|
|
1094
|
+
}}
|
|
1095
|
+
AND tabId = ${{
|
|
1096
|
+
type: TableColumnType.Text,
|
|
1097
|
+
value: data.tabId,
|
|
1098
|
+
}}
|
|
1099
|
+
AND chunkIndex IN (${{
|
|
1100
|
+
type: TableColumnType.Number,
|
|
1101
|
+
value: new Includes(data.chunkIndexes),
|
|
1102
|
+
}})
|
|
1103
|
+
`;
|
|
1104
|
+
|
|
1105
|
+
statement.append(RETENTION_FILTER);
|
|
1106
|
+
|
|
1107
|
+
/*
|
|
1108
|
+
* A retried delivery is two physically present rows on a
|
|
1109
|
+
* ReplacingMergeTree until a merge runs. Feeding both to the player
|
|
1110
|
+
* would replay the same mutations twice, which rrweb resolves against
|
|
1111
|
+
* node ids and would either throw or render a DOM that never existed.
|
|
1112
|
+
*/
|
|
1113
|
+
statement.append(
|
|
1114
|
+
" ORDER BY chunkIndex ASC, version DESC LIMIT 1 BY chunkIndex",
|
|
1115
|
+
);
|
|
1116
|
+
|
|
1117
|
+
statement.append(READ_QUERY_SETTINGS);
|
|
1118
|
+
|
|
1119
|
+
const dbResult: Results =
|
|
1120
|
+
await RumSessionChunkService.executeQuery(statement);
|
|
1121
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
1122
|
+
data?: Array<JSONObject>;
|
|
1123
|
+
}>();
|
|
1124
|
+
|
|
1125
|
+
/*
|
|
1126
|
+
* The cap is re-applied to the bytes actually being handed back, not
|
|
1127
|
+
* only to what the pre-check believed. The pre-check reads a
|
|
1128
|
+
* different snapshot of a ReplacingMergeTree than the read it guards,
|
|
1129
|
+
* and any future change to how stored size is derived would otherwise
|
|
1130
|
+
* silently unbound the response. Accumulated as the rows are mapped
|
|
1131
|
+
* so an oversized read fails before the caller ever holds the whole
|
|
1132
|
+
* set.
|
|
1133
|
+
*/
|
|
1134
|
+
let totalReturnedBytes: number = 0;
|
|
1135
|
+
const chunks: Array<SessionReplayChunkPayload> = [];
|
|
1136
|
+
|
|
1137
|
+
for (const row of response.data || []) {
|
|
1138
|
+
const payload: string = readString(row, "payload");
|
|
1139
|
+
|
|
1140
|
+
totalReturnedBytes += Buffer.byteLength(payload, "utf8");
|
|
1141
|
+
|
|
1142
|
+
if (totalReturnedBytes > MAX_SESSION_REPLAY_READ_BYTES) {
|
|
1143
|
+
throw new BadDataException(
|
|
1144
|
+
`The requested chunks exceed the ${MAX_SESSION_REPLAY_READ_BYTES} byte limit for a single read. Request fewer chunks.`,
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
chunks.push({
|
|
1149
|
+
chunkIndex: readNumber(row, "chunkIndex"),
|
|
1150
|
+
payload: payload,
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return chunks;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/*
|
|
1158
|
+
* Sessions that observed a given exception fingerprint.
|
|
1159
|
+
*
|
|
1160
|
+
* hasAny() appears twice on purpose. In the WHERE it is a bloom-pruned
|
|
1161
|
+
* pre-filter over physical rows; a group survives it if ANY of its rows
|
|
1162
|
+
* carries the fingerprint, which necessarily includes the case where
|
|
1163
|
+
* the winning (highest version) row does - so the pre-filter cannot
|
|
1164
|
+
* drop a true match. The HAVING then re-checks the argMax'd array so a
|
|
1165
|
+
* fingerprint present only on a superseded row does not produce a false
|
|
1166
|
+
* positive.
|
|
1167
|
+
*/
|
|
1168
|
+
@CaptureSpan()
|
|
1169
|
+
public static async getSessionsForException(data: {
|
|
1170
|
+
projectId: ObjectID;
|
|
1171
|
+
exceptionFingerprint: string;
|
|
1172
|
+
/*
|
|
1173
|
+
* null means "no label restriction". An EMPTY array means the caller
|
|
1174
|
+
* can reach no applications at all and must get no rows - the two are
|
|
1175
|
+
* not the same and collapsing them would leak every session in the
|
|
1176
|
+
* project.
|
|
1177
|
+
*/
|
|
1178
|
+
accessibleRumApplicationIds: Array<ObjectID> | null;
|
|
1179
|
+
startTime?: Date | undefined;
|
|
1180
|
+
endTime?: Date | undefined;
|
|
1181
|
+
limit: number;
|
|
1182
|
+
}): Promise<Array<SessionReplayExceptionSession>> {
|
|
1183
|
+
if (
|
|
1184
|
+
data.accessibleRumApplicationIds &&
|
|
1185
|
+
data.accessibleRumApplicationIds.length === 0
|
|
1186
|
+
) {
|
|
1187
|
+
return [];
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const limit: number = Math.max(
|
|
1191
|
+
1,
|
|
1192
|
+
Math.min(data.limit, MAX_SESSION_REPLAY_FOR_EXCEPTION_LIMIT),
|
|
1193
|
+
);
|
|
1194
|
+
|
|
1195
|
+
const selectList: string = toSelectList([
|
|
1196
|
+
{ alias: "aggStartTime", expression: argMaxDateTime("startTime") },
|
|
1197
|
+
{ alias: "aggEndTime", expression: argMaxDateTime("endTime") },
|
|
1198
|
+
{ alias: "aggDurationMs", expression: argMaxNumeric("durationMs") },
|
|
1199
|
+
{ alias: "aggHasError", expression: argMaxColumn("hasError") },
|
|
1200
|
+
{ alias: "aggErrorCount", expression: argMaxNumeric("errorCount") },
|
|
1201
|
+
/*
|
|
1202
|
+
* The frustration counters and masking mode feed the "Watch what the
|
|
1203
|
+
* user saw" card: the signals line ("2 rage clicks before the error")
|
|
1204
|
+
* and the up-front masking disclosure both come from here. Omitting
|
|
1205
|
+
* them renders the card with empty signals and "unknown" masking.
|
|
1206
|
+
*/
|
|
1207
|
+
{
|
|
1208
|
+
alias: "aggRageClickCount",
|
|
1209
|
+
expression: argMaxNumeric("rageClickCount"),
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
alias: "aggDeadClickCount",
|
|
1213
|
+
expression: argMaxNumeric("deadClickCount"),
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
alias: "aggErrorClickCount",
|
|
1217
|
+
expression: argMaxNumeric("errorClickCount"),
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
alias: "aggRefreshRageCount",
|
|
1221
|
+
expression: argMaxNumeric("refreshRageCount"),
|
|
1222
|
+
},
|
|
1223
|
+
{ alias: "aggMaskingMode", expression: argMaxColumn("maskingMode") },
|
|
1224
|
+
{ alias: "aggTriggerReason", expression: argMaxColumn("triggerReason") },
|
|
1225
|
+
{ alias: "aggEntryUrl", expression: argMaxColumn("entryUrl") },
|
|
1226
|
+
{ alias: "aggBrowserName", expression: argMaxColumn("browserName") },
|
|
1227
|
+
{ alias: "aggOsName", expression: argMaxColumn("osName") },
|
|
1228
|
+
{ alias: "aggDeviceType", expression: argMaxColumn("deviceType") },
|
|
1229
|
+
{ alias: "aggIsFinalized", expression: argMaxColumn("isFinalized") },
|
|
1230
|
+
{
|
|
1231
|
+
alias: "aggExceptionFingerprints",
|
|
1232
|
+
expression: argMaxColumn("exceptionFingerprints"),
|
|
1233
|
+
},
|
|
1234
|
+
]);
|
|
1235
|
+
|
|
1236
|
+
const statement: Statement = SQL`
|
|
1237
|
+
SELECT
|
|
1238
|
+
sessionId,
|
|
1239
|
+
toString(rumApplicationId) AS applicationId,
|
|
1240
|
+
`;
|
|
1241
|
+
|
|
1242
|
+
statement.append(` ${selectList}`);
|
|
1243
|
+
|
|
1244
|
+
statement.append(SQL`
|
|
1245
|
+
FROM ${AnalyticsTableName.RumSession}
|
|
1246
|
+
WHERE projectId = ${{
|
|
1247
|
+
type: TableColumnType.ObjectID,
|
|
1248
|
+
value: data.projectId,
|
|
1249
|
+
}}
|
|
1250
|
+
AND hasAny(exceptionFingerprints, [${{
|
|
1251
|
+
type: TableColumnType.Text,
|
|
1252
|
+
value: data.exceptionFingerprint,
|
|
1253
|
+
}}])
|
|
1254
|
+
`);
|
|
1255
|
+
|
|
1256
|
+
statement.append(RETENTION_FILTER);
|
|
1257
|
+
|
|
1258
|
+
if (data.accessibleRumApplicationIds) {
|
|
1259
|
+
statement.append(
|
|
1260
|
+
SQL` AND rumApplicationId IN (${{
|
|
1261
|
+
type: TableColumnType.ObjectID,
|
|
1262
|
+
value: new Includes(data.accessibleRumApplicationIds),
|
|
1263
|
+
}})`,
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
if (data.startTime) {
|
|
1268
|
+
statement.append(
|
|
1269
|
+
SQL` AND startTime >= ${{
|
|
1270
|
+
type: TableColumnType.DateTime64,
|
|
1271
|
+
value: data.startTime,
|
|
1272
|
+
}}`,
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
if (data.endTime) {
|
|
1277
|
+
statement.append(
|
|
1278
|
+
SQL` AND startTime <= ${{
|
|
1279
|
+
type: TableColumnType.DateTime64,
|
|
1280
|
+
value: data.endTime,
|
|
1281
|
+
}}`,
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
statement.append(
|
|
1286
|
+
SQL` GROUP BY projectId, rumApplicationId, sessionId
|
|
1287
|
+
HAVING hasAny(aggExceptionFingerprints, [${{
|
|
1288
|
+
type: TableColumnType.Text,
|
|
1289
|
+
value: data.exceptionFingerprint,
|
|
1290
|
+
}}])
|
|
1291
|
+
ORDER BY aggStartTime DESC
|
|
1292
|
+
LIMIT ${{
|
|
1293
|
+
type: TableColumnType.Number,
|
|
1294
|
+
value: limit,
|
|
1295
|
+
}}`,
|
|
1296
|
+
);
|
|
1297
|
+
|
|
1298
|
+
statement.append(READ_QUERY_SETTINGS);
|
|
1299
|
+
|
|
1300
|
+
const dbResult: Results = await RumSessionService.executeQuery(statement);
|
|
1301
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
1302
|
+
data?: Array<JSONObject>;
|
|
1303
|
+
}>();
|
|
1304
|
+
|
|
1305
|
+
return (response.data || []).map(
|
|
1306
|
+
(row: JSONObject): SessionReplayExceptionSession => {
|
|
1307
|
+
return {
|
|
1308
|
+
sessionId: readString(row, "sessionId"),
|
|
1309
|
+
rumApplicationId: readString(row, "applicationId"),
|
|
1310
|
+
startTime: readDate(row, "aggStartTime"),
|
|
1311
|
+
endTime: readDate(row, "aggEndTime"),
|
|
1312
|
+
durationMs: readNumber(row, "aggDurationMs"),
|
|
1313
|
+
hasError: readBoolean(row, "aggHasError"),
|
|
1314
|
+
errorCount: readNumber(row, "aggErrorCount"),
|
|
1315
|
+
rageClickCount: readNumber(row, "aggRageClickCount"),
|
|
1316
|
+
deadClickCount: readNumber(row, "aggDeadClickCount"),
|
|
1317
|
+
errorClickCount: readNumber(row, "aggErrorClickCount"),
|
|
1318
|
+
refreshRageCount: readNumber(row, "aggRefreshRageCount"),
|
|
1319
|
+
maskingMode: readString(row, "aggMaskingMode"),
|
|
1320
|
+
triggerReason: readString(row, "aggTriggerReason"),
|
|
1321
|
+
entryUrl: readString(row, "aggEntryUrl"),
|
|
1322
|
+
browserName: readString(row, "aggBrowserName"),
|
|
1323
|
+
osName: readString(row, "aggOsName"),
|
|
1324
|
+
deviceType: readString(row, "aggDeviceType"),
|
|
1325
|
+
isFinalized: readBoolean(row, "aggIsFinalized"),
|
|
1326
|
+
};
|
|
1327
|
+
},
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
private static appendListHavingFilters(
|
|
1332
|
+
statement: Statement,
|
|
1333
|
+
filters: SessionReplayListFilters,
|
|
1334
|
+
): void {
|
|
1335
|
+
if (filters.hasError !== undefined) {
|
|
1336
|
+
statement.append(
|
|
1337
|
+
SQL` AND aggHasError = ${{
|
|
1338
|
+
type: TableColumnType.Boolean,
|
|
1339
|
+
value: filters.hasError,
|
|
1340
|
+
}}`,
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
if (filters.hasFrustration === true) {
|
|
1345
|
+
/*
|
|
1346
|
+
* Over the argMax aliases, like every HAVING predicate here — the
|
|
1347
|
+
* raw columns would sum across ReplacingMergeTree versions.
|
|
1348
|
+
*/
|
|
1349
|
+
statement.append(
|
|
1350
|
+
" AND (aggRageClickCount + aggDeadClickCount + aggErrorClickCount + aggRefreshRageCount) > 0",
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
if (filters.isFinalized !== undefined) {
|
|
1355
|
+
statement.append(
|
|
1356
|
+
SQL` AND aggIsFinalized = ${{
|
|
1357
|
+
type: TableColumnType.Boolean,
|
|
1358
|
+
value: filters.isFinalized,
|
|
1359
|
+
}}`,
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if (filters.triggerReasons && filters.triggerReasons.length > 0) {
|
|
1364
|
+
statement.append(
|
|
1365
|
+
SQL` AND aggTriggerReason IN (${{
|
|
1366
|
+
type: TableColumnType.Text,
|
|
1367
|
+
value: new Includes(filters.triggerReasons),
|
|
1368
|
+
}})`,
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
if (filters.browserNames && filters.browserNames.length > 0) {
|
|
1373
|
+
statement.append(
|
|
1374
|
+
SQL` AND aggBrowserName IN (${{
|
|
1375
|
+
type: TableColumnType.Text,
|
|
1376
|
+
value: new Includes(filters.browserNames),
|
|
1377
|
+
}})`,
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
if (filters.osNames && filters.osNames.length > 0) {
|
|
1382
|
+
statement.append(
|
|
1383
|
+
SQL` AND aggOsName IN (${{
|
|
1384
|
+
type: TableColumnType.Text,
|
|
1385
|
+
value: new Includes(filters.osNames),
|
|
1386
|
+
}})`,
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
if (filters.deviceTypes && filters.deviceTypes.length > 0) {
|
|
1391
|
+
statement.append(
|
|
1392
|
+
SQL` AND aggDeviceType IN (${{
|
|
1393
|
+
type: TableColumnType.Text,
|
|
1394
|
+
value: new Includes(filters.deviceTypes),
|
|
1395
|
+
}})`,
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
if (filters.countryCodes && filters.countryCodes.length > 0) {
|
|
1400
|
+
statement.append(
|
|
1401
|
+
SQL` AND aggCountryCode IN (${{
|
|
1402
|
+
type: TableColumnType.Text,
|
|
1403
|
+
value: new Includes(filters.countryCodes),
|
|
1404
|
+
}})`,
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
if (filters.identifiedUserKey) {
|
|
1409
|
+
statement.append(
|
|
1410
|
+
SQL` AND aggIdentifiedUserKey = ${{
|
|
1411
|
+
type: TableColumnType.Text,
|
|
1412
|
+
value: filters.identifiedUserKey,
|
|
1413
|
+
}}`,
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
if (filters.route) {
|
|
1418
|
+
/*
|
|
1419
|
+
* `has`, not a Search/LIKE. StatementGenerator's own comment calls
|
|
1420
|
+
* restoring the exact-match array fast path "the single biggest
|
|
1421
|
+
* performance fix" - a lowerUTF8 arrayExists has no bloom
|
|
1422
|
+
* pre-filter and full-scans the table.
|
|
1423
|
+
*
|
|
1424
|
+
* The argMax expression is appended as raw SQL rather than
|
|
1425
|
+
* interpolated into the template: a plain string substituted into
|
|
1426
|
+
* an SQL`` literal is bound as an Identifier and would be quoted.
|
|
1427
|
+
*/
|
|
1428
|
+
statement.append(` AND has(${argMaxColumn("routes")}, `);
|
|
1429
|
+
statement.append(
|
|
1430
|
+
SQL`${{
|
|
1431
|
+
type: TableColumnType.Text,
|
|
1432
|
+
value: filters.route,
|
|
1433
|
+
}})`,
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
if (
|
|
1438
|
+
filters.minDurationMs !== undefined &&
|
|
1439
|
+
Number.isFinite(filters.minDurationMs)
|
|
1440
|
+
) {
|
|
1441
|
+
statement.append(
|
|
1442
|
+
SQL` AND aggDurationMs >= ${{
|
|
1443
|
+
type: TableColumnType.Decimal,
|
|
1444
|
+
value: filters.minDurationMs,
|
|
1445
|
+
}}`,
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|