@oneuptime/common 14.0.10 → 14.0.11
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/DatabaseModels/AIConversation.ts +127 -1
- package/Models/DatabaseModels/CephCluster.ts +12 -0
- package/Models/DatabaseModels/CloudResource.ts +12 -0
- package/Models/DatabaseModels/DatabaseServer.ts +12 -0
- package/Models/DatabaseModels/DockerHost.ts +12 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +12 -0
- package/Models/DatabaseModels/GlobalOidc.ts +0 -4
- package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
- package/Models/DatabaseModels/GlobalSso.ts +0 -4
- package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
- package/Models/DatabaseModels/Host.ts +12 -0
- package/Models/DatabaseModels/IncidentForm.ts +594 -0
- package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
- package/Models/DatabaseModels/Index.ts +14 -0
- package/Models/DatabaseModels/IoTFleet.ts +12 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
- package/Models/DatabaseModels/MessageQueue.ts +1102 -0
- package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
- package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
- package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
- package/Models/DatabaseModels/PodmanHost.ts +12 -0
- package/Models/DatabaseModels/Project.ts +59 -0
- package/Models/DatabaseModels/ProjectOidc.ts +0 -4
- package/Models/DatabaseModels/ProjectSso.ts +0 -4
- package/Models/DatabaseModels/ProxmoxCluster.ts +12 -0
- package/Models/DatabaseModels/RumApplication.ts +12 -0
- package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
- package/Models/DatabaseModels/Service.ts +12 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
- package/Models/DatabaseModels/StatusPageSso.ts +0 -4
- package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +12 -0
- package/Server/API/AIChatAPI.ts +13 -114
- package/Server/API/AIInvestigationAPI.ts +3 -3
- package/Server/API/AIInvestigationConversationAPI.ts +266 -0
- package/Server/API/IncidentFormAPI.ts +376 -0
- package/Server/API/ProjectOIDC.ts +16 -25
- package/Server/API/ProjectSSO.ts +16 -24
- package/Server/API/StatusPageAPI.ts +42 -77
- package/Server/Enterprise/EnterpriseEdition.ts +93 -28
- package/Server/Enterprise/EnterpriseFeature.ts +25 -3
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
- package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
- package/Server/EnvironmentConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
- package/Server/Middleware/UserAuthorization.ts +16 -38
- package/Server/Services/AIService.ts +10 -13
- package/Server/Services/IncidentCustomFieldService.ts +112 -2
- package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
- package/Server/Services/IncidentEpisodeService.ts +10 -1
- package/Server/Services/IncidentFormService.ts +1566 -0
- package/Server/Services/IncidentFormSubmissionService.ts +97 -0
- package/Server/Services/IncidentGroupingEngineService.ts +45 -8
- package/Server/Services/IncidentService.ts +123 -5
- package/Server/Services/IncidentTemplateService.ts +22 -0
- package/Server/Services/Index.ts +14 -0
- package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
- package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
- package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
- package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
- package/Server/Services/MessageQueueService.ts +1463 -0
- package/Server/Services/OpenTelemetryIngestService.ts +37 -4
- package/Server/Services/ProjectService.ts +0 -59
- package/Server/Services/StatusPagePrivateUserService.ts +1 -4
- package/Server/Services/StatusPageService.ts +1 -51
- package/Server/Services/TeamComplianceSettingService.ts +163 -42
- package/Server/Services/UserNotificationRuleService.ts +8 -1
- package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
- package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
- package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
- package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
- package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
- package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +7 -1
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +34 -14
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +69 -24
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +9 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +53 -10
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
- package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
- package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
- package/Server/Utils/EditionEnforcement.ts +19 -202
- package/Server/Utils/LLM/LLMService.ts +259 -63
- package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/SameOriginRequest.ts +203 -0
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
- package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
- package/Server/Utils/VendorAssets.ts +3 -3
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +10 -1
- package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
- package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
- package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
- package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
- package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
- package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
- package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
- package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
- package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +174 -12
- package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
- package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
- package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +5 -0
- package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
- package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
- package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
- package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
- package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
- package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
- package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
- package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
- package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
- package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
- package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
- package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
- package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
- package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
- package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
- package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
- package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
- package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
- package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
- package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
- package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
- package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
- package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
- package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
- package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
- package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
- package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
- package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
- package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
- package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
- package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
- package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
- package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
- package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
- package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
- package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
- package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
- package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
- package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
- package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
- package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
- package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
- package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
- package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
- package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
- package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +298 -9
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +453 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
- package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
- package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
- package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
- package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
- package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
- package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
- package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
- package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
- package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
- package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
- package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
- package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
- package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
- package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +28 -9
- package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
- package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
- package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
- package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
- package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
- package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
- package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
- package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
- package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
- package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
- package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
- package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
- package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
- package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
- package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
- package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
- package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
- package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
- package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
- package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
- package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
- package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
- package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
- package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
- package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
- package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
- package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
- package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
- package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
- package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
- package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
- package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
- package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
- package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
- package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
- package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
- package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
- package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +266 -25
- package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
- package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
- package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
- package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +297 -15
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +10 -6
- package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
- package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
- package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
- package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
- package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
- package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
- package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
- package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
- package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
- package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
- package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
- package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
- package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
- package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
- package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
- package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +5 -3
- package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
- package/Tests/Types/XML.test.ts +51 -0
- package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
- package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
- package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
- package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
- package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
- package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
- package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
- package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
- package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
- package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
- package/Tests/UI/Components/Icon.test.tsx +32 -0
- package/Tests/UI/Components/Input.test.tsx +31 -0
- package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
- package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
- package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
- package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
- package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
- package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
- package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
- package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
- package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
- package/Tests/UI/Components/TextArea.test.tsx +19 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
- package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
- package/Tests/UI/EsbuildConfig.test.ts +115 -39
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
- package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
- package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
- package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
- package/Types/AI/AIAgentRunLimits.ts +146 -0
- package/Types/Code/CodeType.ts +2 -0
- package/Types/Code/JsonSyntax.ts +923 -0
- package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
- package/Types/Icon/IconProp.ts +2 -0
- package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
- package/Types/Incident/IncidentFormPublic.ts +1145 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
- package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
- package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
- package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
- package/Types/MessageQueue/MessagingSystem.ts +518 -0
- package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
- package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
- package/Types/Permission.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +10 -4
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/Team/ComplianceNotificationChannel.ts +4 -3
- package/Types/Team/ComplianceRule.ts +71 -18
- package/Types/Team/ComplianceRuleType.ts +2 -2
- package/Types/Team/TeamComplianceStatus.ts +6 -2
- package/Types/Telemetry/EntityType.ts +16 -0
- package/Types/Telemetry/LockedFilterDetail.ts +22 -0
- package/Types/XML.ts +26 -0
- package/UI/Components/CodeEditor/CodeEditor.css +379 -0
- package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
- package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
- package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
- package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
- package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
- package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
- package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
- package/UI/Components/Forms/Fields/FormField.tsx +8 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Icon/Icon.tsx +18 -0
- package/UI/Components/Input/Input.tsx +11 -4
- package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
- package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
- package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
- package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
- package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
- package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
- package/UI/Components/Table/EmptyTableMessage.ts +81 -0
- package/UI/Components/Table/Table.tsx +50 -21
- package/UI/Components/Table/TableRow.tsx +9 -1
- package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
- package/UI/Components/TextArea/TextArea.tsx +9 -2
- package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
- package/UI/Styles/Theme.css +8 -0
- package/UI/esbuild-config.js +2 -32
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
- package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
- package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
- package/Utils/Telemetry/EntityKey.ts +48 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +12 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +14 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
- package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js +12 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +61 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +12 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +12 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -103
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
- package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentFormAPI.js +245 -0
- package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectOIDC.js +16 -24
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +16 -24
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +42 -75
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +10 -13
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFormService.js +1202 -0
- package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +106 -4
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +14 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueService.js +1140 -0
- package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +0 -54
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +0 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +7 -2
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +29 -14
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +38 -14
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +2 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +40 -10
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
- package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +3 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
- package/build/dist/Types/Code/CodeType.js +2 -0
- package/build/dist/Types/Code/CodeType.js.map +1 -1
- package/build/dist/Types/Code/JsonSyntax.js +604 -0
- package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
- package/build/dist/Types/Icon/IconProp.js +2 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +274 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +10 -4
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRule.js +48 -14
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
- package/build/dist/Types/Telemetry/EntityType.js +16 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/XML.js +23 -0
- package/build/dist/Types/XML.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +8 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +11 -3
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
- package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
- package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +30 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +9 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -3
- package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
- package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
- package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
- package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
- package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
- package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
- package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
- package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
- package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
|
@@ -0,0 +1,1180 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, jest } from "@jest/globals";
|
|
2
|
+
import type { SpyInstance } from "jest-mock";
|
|
3
|
+
import { marked, Token, Tokens } from "marked";
|
|
4
|
+
import {
|
|
5
|
+
htmlToMarkdown,
|
|
6
|
+
markdownToHtml,
|
|
7
|
+
} from "../../../UI/Components/Markdown.tsx/MarkdownConverters";
|
|
8
|
+
import {
|
|
9
|
+
clipboardToMarkdown,
|
|
10
|
+
isRichClipboardHtml,
|
|
11
|
+
normalizePlainTextListMarkers,
|
|
12
|
+
pastedHtmlToMarkdown,
|
|
13
|
+
PastedClipboard,
|
|
14
|
+
} from "../../../UI/Components/Markdown.tsx/MarkdownPaste";
|
|
15
|
+
import {
|
|
16
|
+
CHROME_CODE_VIEW_TABLE_COPY_HTML,
|
|
17
|
+
CHROME_VIEWER_CODE_LINES_COPY_HTML,
|
|
18
|
+
CHROME_VIEWER_CODE_PART_OF_LINE_COPY_HTML,
|
|
19
|
+
CHROME_VIEWER_CODE_WORD_COPY_HTML,
|
|
20
|
+
CHROME_VIEWER_COPY_HTML,
|
|
21
|
+
CHROME_VIEWER_COPY_TEXT,
|
|
22
|
+
CODE_VIEW_TABLE_COPY_TEXT,
|
|
23
|
+
FIREFOX_VIEWER_CODE_LINES_COPY_HTML,
|
|
24
|
+
FIREFOX_VIEWER_CODE_PART_OF_LINE_COPY_HTML,
|
|
25
|
+
FIREFOX_VIEWER_CODE_WORD_COPY_HTML,
|
|
26
|
+
FIREFOX_VIEWER_COPY_HTML,
|
|
27
|
+
FIREFOX_VIEWER_COPY_TEXT,
|
|
28
|
+
GOOGLE_DOCS_LIST_COPY_HTML,
|
|
29
|
+
GOOGLE_DOCS_LIST_COPY_MARKDOWN,
|
|
30
|
+
GOOGLE_DOCS_LIST_COPY_TEXT,
|
|
31
|
+
VIEWER_CODE_LINES_COPY_TEXT,
|
|
32
|
+
VIEWER_COPY_SOURCE_MARKDOWN,
|
|
33
|
+
WEBKIT_CODE_VIEW_TABLE_COPY_HTML,
|
|
34
|
+
WEBKIT_VIEWER_CODE_PART_OF_LINE_COPY_HTML,
|
|
35
|
+
WEBKIT_VIEWER_CODE_WORD_COPY_HTML,
|
|
36
|
+
WORD_OUTLOOK_ISSUE_4114_HTML,
|
|
37
|
+
} from "./fixtures/MarkdownPasteFixtures";
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* Clipboard -> markdown for the MarkdownEditor (issue #4114: a pasted bullet
|
|
41
|
+
* showed its dot but was not a list, nested lists came out flat, and copying
|
|
42
|
+
* a previous incident lost its formatting).
|
|
43
|
+
*
|
|
44
|
+
* Every conversion is checked three ways: the exact markdown written, the
|
|
45
|
+
* list nesting as marked (the email renderer) reads it, and that the result
|
|
46
|
+
* is a fixed point of the editor's render / serialize loop -- markdown the
|
|
47
|
+
* editor would rewrite on the next keystroke is not a finished paste.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
const NBSP: string = String.fromCharCode(0xa0);
|
|
51
|
+
const SYMBOL_FONT_BULLET: string = String.fromCharCode(0xf0b7);
|
|
52
|
+
const WINGDINGS_SQUARE: string = String.fromCharCode(0xf0a7);
|
|
53
|
+
|
|
54
|
+
// A list's nesting as marked sees it: "ul[li[ul[li]],li]".
|
|
55
|
+
const listShape: (markdown: string) => string = (markdown: string): string => {
|
|
56
|
+
const describe: (token: Token) => string = (token: Token): string => {
|
|
57
|
+
if (token.type !== "list") {
|
|
58
|
+
return token.type;
|
|
59
|
+
}
|
|
60
|
+
const list: Tokens.List = token as Tokens.List;
|
|
61
|
+
const items: Array<string> = list.items.map(
|
|
62
|
+
(item: Tokens.ListItem): string => {
|
|
63
|
+
const nested: Array<string> = item.tokens
|
|
64
|
+
.filter((child: Token): boolean => {
|
|
65
|
+
return child.type === "list";
|
|
66
|
+
})
|
|
67
|
+
.map(describe);
|
|
68
|
+
return nested.length > 0 ? `li[${nested.join("")}]` : "li";
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
return `${list.ordered ? "ol" : "ul"}[${items.join(",")}]`;
|
|
72
|
+
};
|
|
73
|
+
return marked
|
|
74
|
+
.lexer(markdown)
|
|
75
|
+
.filter((token: Token): boolean => {
|
|
76
|
+
return token.type !== "space";
|
|
77
|
+
})
|
|
78
|
+
.map(describe)
|
|
79
|
+
.join(" ");
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// What the editor saves after rendering `markdown` and serializing it back.
|
|
83
|
+
const afterEditorLoop: (markdown: string) => string = (
|
|
84
|
+
markdown: string,
|
|
85
|
+
): string => {
|
|
86
|
+
return htmlToMarkdown(markdownToHtml(markdown));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const clipboard: (
|
|
90
|
+
data: { [format: string]: string },
|
|
91
|
+
types?: ReadonlyArray<string>,
|
|
92
|
+
) => PastedClipboard = (
|
|
93
|
+
data: { [format: string]: string },
|
|
94
|
+
types?: ReadonlyArray<string>,
|
|
95
|
+
): PastedClipboard => {
|
|
96
|
+
return {
|
|
97
|
+
getData: (format: string): string => {
|
|
98
|
+
return data[format] ?? "";
|
|
99
|
+
},
|
|
100
|
+
types: types ?? Object.keys(data),
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
describe("normalizePlainTextListMarkers", () => {
|
|
105
|
+
/*
|
|
106
|
+
* Word and Outlook write a list into plain text as a bullet character and
|
|
107
|
+
* a tab, and mark the level with the character alone -- "•", then "o",
|
|
108
|
+
* then "§" -- without indenting anything.
|
|
109
|
+
*/
|
|
110
|
+
it("turns Word's plain-text bullets into a nested markdown list", () => {
|
|
111
|
+
const markdown: string = normalizePlainTextListMarkers(
|
|
112
|
+
"•\tService down\no\tUsers cannot log in\no\tError shown\n•\tInvestigating\no\tTeam notified\n§\tPaged",
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(markdown).toBe(
|
|
116
|
+
"- Service down\n - Users cannot log in\n - Error shown\n- Investigating\n - Team notified\n - Paged",
|
|
117
|
+
);
|
|
118
|
+
expect(listShape(markdown)).toBe("ul[li[ul[li,li]],li[ul[li[ul[li]]]]]");
|
|
119
|
+
expect(afterEditorLoop(markdown)).toBe(markdown);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("reads Word's Symbol and Wingdings bullets from the private use area", () => {
|
|
123
|
+
expect(
|
|
124
|
+
normalizePlainTextListMarkers(
|
|
125
|
+
`${SYMBOL_FONT_BULLET}\tA\no\tB\n${WINGDINGS_SQUARE}\tC`,
|
|
126
|
+
),
|
|
127
|
+
).toBe("- A\n - B\n - C");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("never opens a level more than one deeper than the item above", () => {
|
|
131
|
+
expect(normalizePlainTextListMarkers("•\ta\n§\tb")).toBe("- a\n - b");
|
|
132
|
+
expect(normalizePlainTextListMarkers("o\tfirst")).toBe("- first");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("nests by indentation when the text is indented, as Slack writes it", () => {
|
|
136
|
+
const markdown: string = normalizePlainTextListMarkers(
|
|
137
|
+
"• Main item\n ◦ Nested item\n ▪ Further nested\n• Next",
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect(markdown).toBe(
|
|
141
|
+
"- Main item\n - Nested item\n - Further nested\n- Next",
|
|
142
|
+
);
|
|
143
|
+
expect(listShape(markdown)).toBe("ul[li[ul[li[ul[li]]]],li]");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("counts a tab of indentation as four columns", () => {
|
|
147
|
+
expect(normalizePlainTextListMarkers("•\tA\n\t•\tB\n•\tC")).toBe(
|
|
148
|
+
"- A\n - B\n- C",
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* "●", "○", "■" are the bullets of three levels (the ones Google Docs
|
|
154
|
+
* draws, though its own plain text has no bullets at all); a dash or an
|
|
155
|
+
* arrow says nothing about the level, so it is a top-level item.
|
|
156
|
+
*/
|
|
157
|
+
it("reads the other bullets and dashes people use", () => {
|
|
158
|
+
expect(normalizePlainTextListMarkers("● one\n○ two\n■ three")).toBe(
|
|
159
|
+
"- one\n - two\n - three",
|
|
160
|
+
);
|
|
161
|
+
expect(normalizePlainTextListMarkers("– four\n— five")).toBe(
|
|
162
|
+
"- four\n- five",
|
|
163
|
+
);
|
|
164
|
+
expect(normalizePlainTextListMarkers("➤ go\n► on")).toBe("- go\n- on");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("takes a non-breaking space after the bullet", () => {
|
|
168
|
+
expect(normalizePlainTextListMarkers(`•${NBSP}item`)).toBe("- item");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("writes numbered lines with a parenthesis the way the editor reads them", () => {
|
|
172
|
+
expect(normalizePlainTextListMarkers("1) one\n2) two")).toBe(
|
|
173
|
+
"1. one\n2. two",
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("starts again at the top after a paragraph ends the list", () => {
|
|
178
|
+
expect(
|
|
179
|
+
normalizePlainTextListMarkers("•\ta\no\tb\nA paragraph.\no\tc"),
|
|
180
|
+
).toBe("- a\n - b\nA paragraph.\n- c");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("keeps a blank line between two bullets inside the same list", () => {
|
|
184
|
+
expect(normalizePlainTextListMarkers("•\ta\n\no\tb")).toBe("- a\n\n - b");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("leaves the inside of a fenced code block alone", () => {
|
|
188
|
+
expect(
|
|
189
|
+
normalizePlainTextListMarkers("```\n• not a list\n1) nor this\n```\n• a"),
|
|
190
|
+
).toBe("```\n• not a list\n1) nor this\n```\n- a");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("reads Windows line endings", () => {
|
|
194
|
+
expect(normalizePlainTextListMarkers("•\ta\r\n•\tb")).toBe("- a\n- b");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("returns text with no bullet lines exactly as it came", () => {
|
|
198
|
+
const untouched: Array<string> = [
|
|
199
|
+
"Price • quality",
|
|
200
|
+
"o no, not again",
|
|
201
|
+
"o gato\no cão",
|
|
202
|
+
"•item without a space",
|
|
203
|
+
"- already markdown\n - nested",
|
|
204
|
+
"Line one\r\nLine two",
|
|
205
|
+
"",
|
|
206
|
+
];
|
|
207
|
+
for (const text of untouched) {
|
|
208
|
+
it(JSON.stringify(text), () => {
|
|
209
|
+
expect(normalizePlainTextListMarkers(text)).toBe(text);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe("pastedHtmlToMarkdown", () => {
|
|
216
|
+
describe("Word and Outlook", () => {
|
|
217
|
+
/*
|
|
218
|
+
* The complaint in issue #4114 itself: its body is Outlook's clipboard
|
|
219
|
+
* HTML, with nested lists written as a <ul> directly inside a <ul> --
|
|
220
|
+
* which the serializer skipped, flattening every nested item away.
|
|
221
|
+
*/
|
|
222
|
+
it("keeps the nested lists of the issue's own Outlook HTML", () => {
|
|
223
|
+
const markdown: string = pastedHtmlToMarkdown(
|
|
224
|
+
WORD_OUTLOOK_ISSUE_4114_HTML,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
expect(markdown).toContain(
|
|
228
|
+
"For example, a previous incident might contain:\n\n- Service is currently unavailable\n - Users are unable to log in\n - Users are receiving an error message\n- Investigation is in progress\n - Technical team has been notified\n - Vendor has been contacted\n\nA user may want to copy",
|
|
229
|
+
);
|
|
230
|
+
expect(markdown).toContain(
|
|
231
|
+
"For example:\n\n- Main item\n - Nested item\n - Further nested item\n\nThe underlying Markdown",
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("writes the whole document as markdown the editor keeps as it is", () => {
|
|
236
|
+
const markdown: string = pastedHtmlToMarkdown(
|
|
237
|
+
WORD_OUTLOOK_ISSUE_4114_HTML,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
expect(afterEditorLoop(markdown)).toBe(markdown);
|
|
241
|
+
expect(
|
|
242
|
+
markdown.startsWith(
|
|
243
|
+
"**Is your feature request related to a problem? Please describe.**\n\nWith the new custom fields",
|
|
244
|
+
),
|
|
245
|
+
).toBe(true);
|
|
246
|
+
expect(markdown).toContain("\n\n---\n\n**Markdown Editor Enhancements**");
|
|
247
|
+
expect(markdown).toContain(
|
|
248
|
+
"- **Shown / Hidden**\n- **Required / Optional**",
|
|
249
|
+
);
|
|
250
|
+
expect(markdown).toContain(
|
|
251
|
+
"**Which fields are displayed → Which fields are required → What the message looks like**",
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("gives marked the same nesting", () => {
|
|
256
|
+
const markdown: string = pastedHtmlToMarkdown(
|
|
257
|
+
WORD_OUTLOOK_ISSUE_4114_HTML,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
expect(listShape(markdown)).toContain("ul[li[ul[li,li]],li[ul[li,li]]]");
|
|
261
|
+
expect(listShape(markdown)).toContain("ul[li[ul[li[ul[li]]]]]");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("joins Word's hard-wrapped lines and leaks none of its markup", () => {
|
|
265
|
+
const markdown: string = pastedHtmlToMarkdown(
|
|
266
|
+
WORD_OUTLOOK_ISSUE_4114_HTML,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
expect(markdown).toContain("- Affected Location\n");
|
|
270
|
+
expect(markdown).not.toMatch(/Service\s*\n\s+is currently/);
|
|
271
|
+
for (const leak of [
|
|
272
|
+
"<",
|
|
273
|
+
"mso",
|
|
274
|
+
"o:p",
|
|
275
|
+
"StartFragment",
|
|
276
|
+
"VML",
|
|
277
|
+
"WordDocument",
|
|
278
|
+
NBSP,
|
|
279
|
+
]) {
|
|
280
|
+
expect(markdown).not.toContain(leak);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
* Word itself (rather than Outlook) writes a list as paragraphs, each with
|
|
286
|
+
* its level in a mso-list style and its bullet or number as text between
|
|
287
|
+
* <![if !supportLists]> and <![endif]>. Without reading that, the bullet
|
|
288
|
+
* came through as "·" at the start of a plain paragraph.
|
|
289
|
+
*/
|
|
290
|
+
const wordParagraph: (
|
|
291
|
+
level: number,
|
|
292
|
+
marker: string,
|
|
293
|
+
text: string,
|
|
294
|
+
) => string = (level: number, marker: string, text: string): string => {
|
|
295
|
+
return `<p class=MsoListParagraphCxSpMiddle style='margin-left:${36 * level}.0pt;mso-add-space:auto;text-indent:-18.0pt;mso-list:l0 level${level} lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol'><span style='mso-list:Ignore'>${marker}<span style='font:7.0pt "Times New Roman"'> </span></span></span><![endif]>${text}<o:p></o:p></p>\n`;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
it("rebuilds Word's paragraph list, bullets removed, levels nested", () => {
|
|
299
|
+
const html: string =
|
|
300
|
+
"<p class=MsoNormal>Before<o:p></o:p></p>\n" +
|
|
301
|
+
wordParagraph(1, "·", "One") +
|
|
302
|
+
wordParagraph(2, "o", "Nested <b>bold</b>") +
|
|
303
|
+
wordParagraph(3, "§", "Deeper") +
|
|
304
|
+
wordParagraph(1, "·", "Two") +
|
|
305
|
+
"<p class=MsoNormal>After<o:p></o:p></p>";
|
|
306
|
+
|
|
307
|
+
const markdown: string = pastedHtmlToMarkdown(html);
|
|
308
|
+
|
|
309
|
+
expect(markdown).toBe(
|
|
310
|
+
"Before\n\n- One\n - Nested **bold**\n - Deeper\n- Two\n\nAfter",
|
|
311
|
+
);
|
|
312
|
+
expect(listShape(markdown)).toBe(
|
|
313
|
+
"paragraph ul[li[ul[li[ul[li]]]],li] paragraph",
|
|
314
|
+
);
|
|
315
|
+
expect(afterEditorLoop(markdown)).toBe(markdown);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("reads a numbered Word list as an ordered list", () => {
|
|
319
|
+
const html: string =
|
|
320
|
+
wordParagraph(1, "1.", "First") +
|
|
321
|
+
wordParagraph(2, "a.", "Sub step") +
|
|
322
|
+
wordParagraph(1, "2.", "Second");
|
|
323
|
+
|
|
324
|
+
const markdown: string = pastedHtmlToMarkdown(html);
|
|
325
|
+
|
|
326
|
+
expect(markdown).toBe("1. First\n 1. Sub step\n2. Second");
|
|
327
|
+
expect(listShape(markdown)).toBe("ol[li[ol[li]],li]");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("starts a new list where Word switches between bullets and numbers", () => {
|
|
331
|
+
const html: string =
|
|
332
|
+
wordParagraph(1, "·", "Bullet") + wordParagraph(1, "1.", "Number");
|
|
333
|
+
|
|
334
|
+
expect(pastedHtmlToMarkdown(html)).toBe("- Bullet\n\n1. Number");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("strips a marker Word wrote without the conditional comments", () => {
|
|
338
|
+
const html: string =
|
|
339
|
+
"<p style='mso-list:l0 level1 lfo1'><span style='mso-list:Ignore'>·<span> </span></span>Only item</p>";
|
|
340
|
+
|
|
341
|
+
expect(pastedHtmlToMarkdown(html)).toBe("- Only item");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("keeps an <o:p> that holds text, and drops an empty one", () => {
|
|
345
|
+
expect(
|
|
346
|
+
pastedHtmlToMarkdown(
|
|
347
|
+
"<p class=MsoNormal>a<o:p>b</o:p></p><p class=MsoNormal><o:p> </o:p></p><p>c</p>",
|
|
348
|
+
),
|
|
349
|
+
).toBe("ab\n\nc");
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe("Google Docs", () => {
|
|
354
|
+
/*
|
|
355
|
+
* The shape of a Google Docs copy: everything inside one
|
|
356
|
+
* <b style="font-weight:normal" id="docs-internal-guid-...">, formatting
|
|
357
|
+
* as span styles, each item's text in a <p>, and nested lists directly
|
|
358
|
+
* inside their parent list.
|
|
359
|
+
*/
|
|
360
|
+
const GOOGLE_DOCS_HTML: string =
|
|
361
|
+
'<meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-2f1b3c4d-7fff-a1b2-c3d4-e5f6a7b8c9d0"><ul style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:disc;font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Bold</span><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> and </span><span style="font-size:11pt;font-family:Arial,sans-serif;color:#000000;background-color:transparent;font-weight:400;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">italic</span></p></li><ul style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" aria-level="2"><p dir="ltr" role="presentation"><a href="https://example.com/runbook" style="text-decoration:none;"><span style="font-size:11pt;color:#1155cc;font-weight:400;text-decoration:underline;white-space:pre;white-space:pre-wrap;">runbook</span></a><span style="font-weight:400;white-space:pre;white-space:pre-wrap;"> </span><span style="font-weight:400;text-decoration:line-through;white-space:pre;white-space:pre-wrap;">gone</span></p></li></ul><li dir="ltr" aria-level="1"><p dir="ltr" role="presentation"><span style="font-weight:700;font-style:italic;white-space:pre;white-space:pre-wrap;">Both</span></p></li></ul><br></b>';
|
|
362
|
+
|
|
363
|
+
it("unwraps the whole-document bold and reads formatting from styles", () => {
|
|
364
|
+
const markdown: string = pastedHtmlToMarkdown(GOOGLE_DOCS_HTML);
|
|
365
|
+
|
|
366
|
+
expect(markdown).toBe(
|
|
367
|
+
"- **Bold** and *italic*\n - [runbook](https://example.com/runbook) ~~gone~~\n- ***Both***",
|
|
368
|
+
);
|
|
369
|
+
expect(listShape(markdown)).toBe("ul[li[ul[li]],li]");
|
|
370
|
+
expect(afterEditorLoop(markdown)).toBe(markdown);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("renders text that is bold and italic at once as both", () => {
|
|
374
|
+
expect(markdownToHtml(pastedHtmlToMarkdown(GOOGLE_DOCS_HTML))).toContain(
|
|
375
|
+
"<em><strong>Both</strong></em>",
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
/*
|
|
380
|
+
* Docs puts "white-space:pre" on every list item it copies, and the
|
|
381
|
+
* paste took that for a code editor's copy (VS Code's white-space:pre
|
|
382
|
+
* <div>) and used the plain text instead -- the lines alone, with no
|
|
383
|
+
* bullets -- so every Docs copy with a list lost its lists, links and
|
|
384
|
+
* formatting. These go through the editor's own entry point.
|
|
385
|
+
*/
|
|
386
|
+
it("counts a Docs copy with a list in it as rich", () => {
|
|
387
|
+
expect(isRichClipboardHtml(GOOGLE_DOCS_HTML)).toBe(true);
|
|
388
|
+
expect(isRichClipboardHtml(GOOGLE_DOCS_LIST_COPY_HTML)).toBe(true);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("pastes a Docs list as the list it is, not as its plain text", () => {
|
|
392
|
+
expect(
|
|
393
|
+
clipboardToMarkdown(
|
|
394
|
+
clipboard({
|
|
395
|
+
"text/html": GOOGLE_DOCS_HTML,
|
|
396
|
+
"text/plain": "Bold and italic\nrunbook gone\nBoth",
|
|
397
|
+
}),
|
|
398
|
+
),
|
|
399
|
+
).toBe(
|
|
400
|
+
"- **Bold** and *italic*\n - [runbook](https://example.com/runbook) ~~gone~~\n- ***Both***",
|
|
401
|
+
);
|
|
402
|
+
expect(
|
|
403
|
+
clipboardToMarkdown(
|
|
404
|
+
clipboard({
|
|
405
|
+
"text/html": GOOGLE_DOCS_LIST_COPY_HTML,
|
|
406
|
+
"text/plain": GOOGLE_DOCS_LIST_COPY_TEXT,
|
|
407
|
+
}),
|
|
408
|
+
),
|
|
409
|
+
).toBe(GOOGLE_DOCS_LIST_COPY_MARKDOWN);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Chromium, Safari and Docs write a tab as a white-space:pre <span>.
|
|
413
|
+
it("counts a Docs paragraph with a tab in it as rich", () => {
|
|
414
|
+
const html: string =
|
|
415
|
+
'<b style="font-weight:normal;" id="docs-internal-guid-2"><p dir="ltr"><span style="font-weight:700;white-space:pre;white-space:pre-wrap;">Owner:</span><span style="font-weight:400;white-space:pre;white-space:pre-wrap;"><span class="Apple-tab-span" style="white-space:pre;">\t</span>Jane</span></p></b>';
|
|
416
|
+
|
|
417
|
+
expect(isRichClipboardHtml(html)).toBe(true);
|
|
418
|
+
expect(
|
|
419
|
+
clipboardToMarkdown(
|
|
420
|
+
clipboard({ "text/html": html, "text/plain": "Owner:\tJane" }),
|
|
421
|
+
),
|
|
422
|
+
).toBe("**Owner:** Jane");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("counts plain Docs text as plain, so its plain text is pasted", () => {
|
|
426
|
+
expect(
|
|
427
|
+
isRichClipboardHtml(
|
|
428
|
+
'<b style="font-weight:normal;" id="docs-internal-guid-1"><span style="font-weight:400;white-space:pre-wrap;">Just text</span></b>',
|
|
429
|
+
),
|
|
430
|
+
).toBe(false);
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
describe("a note copied out of MarkdownViewer", () => {
|
|
435
|
+
/*
|
|
436
|
+
* Real copies of two lines of a viewer code block: neither browser keeps
|
|
437
|
+
* the block's <div>, only the <pre> around the text, which carries the
|
|
438
|
+
* viewer's hints -- so the lines come back as a fenced block in their
|
|
439
|
+
* language rather than as markdown ("- name: web" a list item).
|
|
440
|
+
*/
|
|
441
|
+
it("comes back as a fenced block when only some of a code block's lines were copied", () => {
|
|
442
|
+
for (const html of [
|
|
443
|
+
CHROME_VIEWER_CODE_LINES_COPY_HTML,
|
|
444
|
+
FIREFOX_VIEWER_CODE_LINES_COPY_HTML,
|
|
445
|
+
]) {
|
|
446
|
+
expect(
|
|
447
|
+
clipboardToMarkdown(
|
|
448
|
+
clipboard({
|
|
449
|
+
"text/html": html,
|
|
450
|
+
"text/plain": VIEWER_CODE_LINES_COPY_TEXT,
|
|
451
|
+
}),
|
|
452
|
+
),
|
|
453
|
+
).toBe("```yaml\nreplicas: 3\nimage: api_server:v2\n```");
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
/*
|
|
458
|
+
* Every browser keeps the viewer's hinted <pre> around even a word it
|
|
459
|
+
* copies. As a fenced block, a pod name copied out of a note split the
|
|
460
|
+
* sentence it was pasted into, and in the markdown source ran into the
|
|
461
|
+
* rest of the line.
|
|
462
|
+
*/
|
|
463
|
+
it("comes back as inline code when less than a line of a code block was copied", () => {
|
|
464
|
+
for (const html of [
|
|
465
|
+
CHROME_VIEWER_CODE_WORD_COPY_HTML,
|
|
466
|
+
FIREFOX_VIEWER_CODE_WORD_COPY_HTML,
|
|
467
|
+
WEBKIT_VIEWER_CODE_WORD_COPY_HTML,
|
|
468
|
+
]) {
|
|
469
|
+
expect(
|
|
470
|
+
clipboardToMarkdown(
|
|
471
|
+
clipboard({ "text/html": html, "text/plain": "api_server" }),
|
|
472
|
+
),
|
|
473
|
+
).toBe("`api_server`");
|
|
474
|
+
}
|
|
475
|
+
for (const html of [
|
|
476
|
+
CHROME_VIEWER_CODE_PART_OF_LINE_COPY_HTML,
|
|
477
|
+
FIREFOX_VIEWER_CODE_PART_OF_LINE_COPY_HTML,
|
|
478
|
+
WEBKIT_VIEWER_CODE_PART_OF_LINE_COPY_HTML,
|
|
479
|
+
]) {
|
|
480
|
+
expect(
|
|
481
|
+
clipboardToMarkdown(
|
|
482
|
+
clipboard({ "text/html": html, "text/plain": "kubectl get pods" }),
|
|
483
|
+
),
|
|
484
|
+
).toBe("`kubectl get pods`");
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
/*
|
|
489
|
+
* A line triple-clicked in Chromium or Safari is copied with its line
|
|
490
|
+
* break: a line of the block, which stays a fenced block of its own.
|
|
491
|
+
*/
|
|
492
|
+
it("keeps a line copied with its line break a fenced block", () => {
|
|
493
|
+
expect(
|
|
494
|
+
pastedHtmlToMarkdown(
|
|
495
|
+
'<pre data-markdown-code-block="true" data-language="bash"><code>npm install\n</code></pre>',
|
|
496
|
+
),
|
|
497
|
+
).toBe("```bash\nnpm install\n```");
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
/*
|
|
501
|
+
* A copy that carries the block's root -- all of a one-line block, or a
|
|
502
|
+
* line Chromium and Safari triple-click -- is the block, language and
|
|
503
|
+
* all.
|
|
504
|
+
*/
|
|
505
|
+
it("keeps a one-line block copied whole a fenced block", () => {
|
|
506
|
+
expect(
|
|
507
|
+
pastedHtmlToMarkdown(
|
|
508
|
+
'<div data-markdown-code-block="true" data-language="bash"><div data-markdown-ignore="true"><span>Bash</span><button>Copy</button></div><pre data-markdown-code-block="true" data-language="bash"><code>npm install</code></pre></div>',
|
|
509
|
+
),
|
|
510
|
+
).toBe("```bash\nnpm install\n```");
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Inline code has no way to hold a backtick of its own.
|
|
514
|
+
it("keeps less than a line holding a backtick a fenced block", () => {
|
|
515
|
+
expect(
|
|
516
|
+
pastedHtmlToMarkdown(
|
|
517
|
+
'<pre data-markdown-code-block="true" data-language="bash"><code>echo `date`</code></pre>',
|
|
518
|
+
),
|
|
519
|
+
).toBe("```bash\necho `date`\n```");
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
/*
|
|
523
|
+
* The fixtures are real copies of the real viewer: the code block comes
|
|
524
|
+
* back as a fence with its language (not the "Copy" button's text), the
|
|
525
|
+
* localized timestamp as the ISO value it was rendered from, and every
|
|
526
|
+
* list at its depth -- the same markdown the note was written in.
|
|
527
|
+
*/
|
|
528
|
+
it("comes back from Chromium's copy exactly as the note was written", () => {
|
|
529
|
+
const markdown: string = pastedHtmlToMarkdown(CHROME_VIEWER_COPY_HTML);
|
|
530
|
+
|
|
531
|
+
expect(markdown).toBe(VIEWER_COPY_SOURCE_MARKDOWN);
|
|
532
|
+
expect(afterEditorLoop(markdown)).toBe(markdown);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it("comes back from Firefox's copy exactly as the note was written", () => {
|
|
536
|
+
const markdown: string = pastedHtmlToMarkdown(FIREFOX_VIEWER_COPY_HTML);
|
|
537
|
+
|
|
538
|
+
expect(markdown).toBe(VIEWER_COPY_SOURCE_MARKDOWN);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("leaves out the code block's Copy button and language label", () => {
|
|
542
|
+
const markdown: string = pastedHtmlToMarkdown(CHROME_VIEWER_COPY_HTML);
|
|
543
|
+
|
|
544
|
+
expect(markdown).not.toMatch(/\bCopy\b/);
|
|
545
|
+
expect(markdown).not.toContain("TYPESCRIPT");
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("writes a code block without a language as a bare fence", () => {
|
|
549
|
+
expect(
|
|
550
|
+
pastedHtmlToMarkdown(
|
|
551
|
+
'<div data-markdown-code-block="true" data-language="text"><div data-markdown-ignore="true"><span>Text</span><button>Copy</button></div><div style="white-space: pre;"><code class="font-mono">line 1\nline 2</code></div></div>',
|
|
552
|
+
),
|
|
553
|
+
).toBe("```\nline 1\nline 2\n```");
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("ignores a language attribute that is not a language name", () => {
|
|
557
|
+
expect(
|
|
558
|
+
pastedHtmlToMarkdown(
|
|
559
|
+
'<div data-markdown-code-block="true" data-language="x`y"><code>a</code></div>',
|
|
560
|
+
),
|
|
561
|
+
).toBe("```\na\n```");
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("keeps the text of a timestamp that is not the viewer's", () => {
|
|
565
|
+
expect(
|
|
566
|
+
pastedHtmlToMarkdown(
|
|
567
|
+
'<p>Due <time datetime="2026-05-01">May 1</time></p>',
|
|
568
|
+
),
|
|
569
|
+
).toBe("Due May 1");
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
describe("lists", () => {
|
|
574
|
+
it("moves a list written directly inside a list into the item before it", () => {
|
|
575
|
+
expect(
|
|
576
|
+
pastedHtmlToMarkdown(
|
|
577
|
+
"<ul><li>a</li><ul><li>b</li><ol><li>c</li></ol></ul><li>d</li></ul>",
|
|
578
|
+
),
|
|
579
|
+
).toBe("- a\n - b\n 1. c\n- d");
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it("gives a list with no item before it an item of its own", () => {
|
|
583
|
+
expect(pastedHtmlToMarkdown("<ul><ul><li>a</li></ul></ul>")).toBe(
|
|
584
|
+
"- - a",
|
|
585
|
+
);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it("wraps loose content in a list in an item instead of dropping it", () => {
|
|
589
|
+
expect(
|
|
590
|
+
pastedHtmlToMarkdown("<ul><span>x</span> y<li>a</li><p>z</p></ul>"),
|
|
591
|
+
).toBe("- x y\n- a\n- z");
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it("keeps a task list's boxes", () => {
|
|
595
|
+
expect(
|
|
596
|
+
pastedHtmlToMarkdown(
|
|
597
|
+
'<ul><li><input type="checkbox" checked> done</li><li><input type="checkbox"> todo</li></ul>',
|
|
598
|
+
),
|
|
599
|
+
).toBe("- [x] done\n- [ ] todo");
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("keeps an item with two paragraphs loose", () => {
|
|
603
|
+
expect(pastedHtmlToMarkdown("<ul><li><p>a</p><p>b</p></li></ul>")).toBe(
|
|
604
|
+
"- a\n\n b",
|
|
605
|
+
);
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
describe("whitespace", () => {
|
|
610
|
+
it("collapses runs of whitespace and non-breaking spaces outside code", () => {
|
|
611
|
+
expect(pastedHtmlToMarkdown(`<p>a \n\t b c${NBSP}d</p>`)).toBe(
|
|
612
|
+
"a b c d",
|
|
613
|
+
);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it("keeps the whitespace of a code block", () => {
|
|
617
|
+
expect(
|
|
618
|
+
pastedHtmlToMarkdown("<pre><code> indented\n\n more</code></pre>"),
|
|
619
|
+
).toBe("```\n indented\n\n more\n```");
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("drops the whitespace between blocks and at their edges", () => {
|
|
623
|
+
expect(
|
|
624
|
+
pastedHtmlToMarkdown(
|
|
625
|
+
"\n<p>\n <span> one </span>\n</p>\n\n<p> two <br> three </p>\n",
|
|
626
|
+
),
|
|
627
|
+
).toBe("one\n\ntwo\nthree");
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("writes one space where spaces meet across inline elements", () => {
|
|
631
|
+
expect(
|
|
632
|
+
pastedHtmlToMarkdown("<p><span>a </span> <span> b</span></p>"),
|
|
633
|
+
).toBe("a b");
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("keeps the spaces on either side of an image", () => {
|
|
637
|
+
expect(
|
|
638
|
+
pastedHtmlToMarkdown('<p>a <img src="https://i.test/x.png"> b</p>'),
|
|
639
|
+
).toBe("a  b");
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it("keeps the space between two formatted words", () => {
|
|
643
|
+
expect(pastedHtmlToMarkdown("<p><b>a</b> <i>b</i></p>")).toBe(
|
|
644
|
+
"**a** *b*",
|
|
645
|
+
);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
/*
|
|
649
|
+
* "**bold **text" is not bold in CommonMark: marked (the email) and the
|
|
650
|
+
* viewer print the asterisks.
|
|
651
|
+
*/
|
|
652
|
+
it("moves the spaces at the edge of bold or italic text outside it", () => {
|
|
653
|
+
const markdown: string = pastedHtmlToMarkdown(
|
|
654
|
+
"<p><b>bold </b>text <i> italic</i> end</p>",
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
expect(markdown).toBe("**bold** text *italic* end");
|
|
658
|
+
expect(marked.parse(markdown)).toContain("<strong>bold</strong>");
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it("drops empty formatting and keeps the space it held", () => {
|
|
662
|
+
expect(pastedHtmlToMarkdown("<p>a<b> </b>b<i></i>c<s></s></p>")).toBe(
|
|
663
|
+
"a bc",
|
|
664
|
+
);
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
describe("links and images", () => {
|
|
669
|
+
it("keeps http, https, mailto, tel and relative links", () => {
|
|
670
|
+
expect(
|
|
671
|
+
pastedHtmlToMarkdown(
|
|
672
|
+
'<p><a href="https://a.test/x">a</a> <a href="http://b.test">b</a> <a href="mailto:ops@c.test">c</a> <a href="tel:+15550100">d</a> <a href="/dashboard/1">e</a> <a href="#part">f</a></p>',
|
|
673
|
+
),
|
|
674
|
+
).toBe(
|
|
675
|
+
"[a](https://a.test/x) [b](http://b.test) [c](mailto:ops@c.test) [d](tel:+15550100) [e](/dashboard/1) [f](#part)",
|
|
676
|
+
);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it("keeps only the text of a link with any other scheme", () => {
|
|
680
|
+
expect(
|
|
681
|
+
pastedHtmlToMarkdown(
|
|
682
|
+
'<p><a href="javascript:alert(1)">js</a> <a href="JAVASCRIPT:alert(1)">upper</a> <a href="data:text/html,x">data</a> <a href="vbscript:x">vb</a> <a href="file:///C:/x">file</a> <a href="">empty</a></p>',
|
|
683
|
+
),
|
|
684
|
+
).toBe("js upper data vb file empty");
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it("sees through a scheme hidden with whitespace, control characters or entities", () => {
|
|
688
|
+
const control: string = String.fromCharCode(1);
|
|
689
|
+
expect(
|
|
690
|
+
pastedHtmlToMarkdown(
|
|
691
|
+
`<p><a href="java\tscript:alert(1)">tab</a> <a href=" javascript:alert(1)">space</a> <a href="javascript:alert(1)">entity</a> <a href="${control}javascript:alert(1)">control</a></p>`,
|
|
692
|
+
),
|
|
693
|
+
).toBe("tab space entity control");
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("encodes the spaces and parentheses that would end a markdown link early", () => {
|
|
697
|
+
const markdown: string = pastedHtmlToMarkdown(
|
|
698
|
+
'<p><a href="https://w.test/wiki/Foo_(bar) baz">wiki</a></p>',
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
expect(markdown).toBe("[wiki](https://w.test/wiki/Foo_%28bar%29%20baz)");
|
|
702
|
+
expect(markdownToHtml(markdown)).toContain(
|
|
703
|
+
'href="https://w.test/wiki/Foo_%28bar%29%20baz"',
|
|
704
|
+
);
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
it("keeps http, https and relative images and drops every other kind", () => {
|
|
708
|
+
expect(
|
|
709
|
+
pastedHtmlToMarkdown(
|
|
710
|
+
'<p><img src="https://i.test/a.png" alt="a"><img src="/file/image/b.png" alt="b"><img src="data:image/png;base64,AAAA" alt="c"><img src="blob:https://x/1" alt="d"><img src="file:///C:/e.png" alt="e"><img src="cid:image001.png@01D" alt="f"><img alt="g"></p>',
|
|
711
|
+
),
|
|
712
|
+
).toBe("");
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it("drops the viewer's sessionRetry cache buster from an image", () => {
|
|
716
|
+
expect(
|
|
717
|
+
pastedHtmlToMarkdown(
|
|
718
|
+
'<img src="https://oneuptime.test/file/image/access-token/t1?sessionRetry=1714"><img src="https://oneuptime.test/file/image/access-token/t2?sessionRetry=1&v=2"><img src="https://oneuptime.test/x.png?v=2&sessionRetry=9">',
|
|
719
|
+
),
|
|
720
|
+
).toBe(
|
|
721
|
+
"",
|
|
722
|
+
);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
/*
|
|
726
|
+
* A link with nothing left to read once its icon is dropped came out
|
|
727
|
+
* with its URL as its text: every heading copied from a GitHub README
|
|
728
|
+
* gained a line of "[https://github.com/...#install](...)". Browsers
|
|
729
|
+
* make the href absolute on the clipboard, as here.
|
|
730
|
+
*/
|
|
731
|
+
it("drops the permalink GitHub puts beside a heading", () => {
|
|
732
|
+
expect(
|
|
733
|
+
pastedHtmlToMarkdown(
|
|
734
|
+
'<div class="markdown-heading"><h2 class="heading-element">Install</h2><a id="user-content-install" class="anchor" aria-label="Permalink: Install" href="https://github.com/o/r#install"><svg class="octicon octicon-link" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275"></path></svg></a></div><p>Run <code>npm i</code>.</p>',
|
|
735
|
+
),
|
|
736
|
+
).toBe("## Install\n\nRun `npm i`.");
|
|
737
|
+
expect(
|
|
738
|
+
pastedHtmlToMarkdown(
|
|
739
|
+
'<h2><a class="anchor" href="https://github.com/o/r#install"><svg><path d="m7"></path></svg></a>Install</h2><p>Run <code>npm i</code>.</p>',
|
|
740
|
+
),
|
|
741
|
+
).toBe("## Install\n\nRun `npm i`.");
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it("drops the empty and zero-width permalinks of GitLab and Docusaurus", () => {
|
|
745
|
+
expect(
|
|
746
|
+
pastedHtmlToMarkdown(
|
|
747
|
+
'<h2 id="setup"><a class="anchor" href="https://gitlab.test/g/p#setup" aria-hidden="true"></a>Setup</h2>',
|
|
748
|
+
),
|
|
749
|
+
).toBe("## Setup");
|
|
750
|
+
expect(
|
|
751
|
+
pastedHtmlToMarkdown(
|
|
752
|
+
'<h2>Install<a href="https://docs.test/intro#install" class="hash-link" aria-label="Direct link to Install">\u200b</a></h2>',
|
|
753
|
+
),
|
|
754
|
+
).toBe("## Install");
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it("drops an icon-only link without running the words around it together", () => {
|
|
758
|
+
expect(
|
|
759
|
+
pastedHtmlToMarkdown(
|
|
760
|
+
'<p>Follow us <a href="https://social.test/oneuptime"><svg><path d="m1"></path></svg></a> and <b>more</b></p>',
|
|
761
|
+
),
|
|
762
|
+
).toBe("Follow us and **more**");
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it("keeps a link with text, a linked image, and an icon beside text", () => {
|
|
766
|
+
expect(
|
|
767
|
+
pastedHtmlToMarkdown(
|
|
768
|
+
'<p><a href="https://a.test">text</a> <a href="https://b.test"><img src="https://i.test/b.png" alt="b"></a> <a href="https://c.test"><svg></svg>GitHub</a> a<a href="https://x.test"> </a>b</p>',
|
|
769
|
+
),
|
|
770
|
+
).toBe(
|
|
771
|
+
"[text](https://a.test) [](https://b.test) [GitHub](https://c.test) a b",
|
|
772
|
+
);
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it("drops a link whose only image was dropped", () => {
|
|
776
|
+
expect(
|
|
777
|
+
pastedHtmlToMarkdown(
|
|
778
|
+
'<p>see <a href="https://a.test"><img src="data:image/png;base64,AAAA"></a> here</p>',
|
|
779
|
+
),
|
|
780
|
+
).toBe("see here");
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
it("keeps an image's alt text from breaking the markdown around it", () => {
|
|
784
|
+
expect(
|
|
785
|
+
pastedHtmlToMarkdown(
|
|
786
|
+
'<img src="https://i.test/a.png" alt="a [b]\n c" title="has "quotes"">',
|
|
787
|
+
),
|
|
788
|
+
).toBe("");
|
|
789
|
+
});
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
describe("what is not content", () => {
|
|
793
|
+
it("drops scripts, styles, buttons, icons, frames and form controls", () => {
|
|
794
|
+
expect(
|
|
795
|
+
pastedHtmlToMarkdown(
|
|
796
|
+
'<style>p { color: red }</style><script>var x = 1;</script><p>kept<button>Copy</button><svg><text>icon</text></svg></p><iframe src="https://x.test"></iframe><select><option>opt</option></select><textarea>draft</textarea><input type="text" value="v">',
|
|
797
|
+
),
|
|
798
|
+
).toBe("kept");
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it("drops what is marked for the paste handler to ignore, or hidden", () => {
|
|
802
|
+
expect(
|
|
803
|
+
pastedHtmlToMarkdown(
|
|
804
|
+
'<p>a<span data-markdown-ignore="true">chrome</span><span style="display:none">hidden</span><span style="mso-hide:all">word hidden</span>b</p>',
|
|
805
|
+
),
|
|
806
|
+
).toBe("ab");
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it("keeps headings, quotes, rules and tables", () => {
|
|
810
|
+
expect(
|
|
811
|
+
pastedHtmlToMarkdown(
|
|
812
|
+
"<h2>Status</h2><blockquote><p>Quoted</p></blockquote><hr><table><tr><td>Host</td><td>State</td></tr><tr><td>db-1</td><td>down</td></tr></table>",
|
|
813
|
+
),
|
|
814
|
+
).toBe(
|
|
815
|
+
"## Status\n\n> Quoted\n\n---\n\n| Host | State |\n| ---- | ----- |\n| db-1 | down |",
|
|
816
|
+
);
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
/*
|
|
821
|
+
* The clipboard's HTML comes from anywhere. It is parsed with DOMParser,
|
|
822
|
+
* whose document is inert, and never assigned to innerHTML -- in Chromium
|
|
823
|
+
* an <img onerror> set through a detached element's innerHTML runs.
|
|
824
|
+
*/
|
|
825
|
+
describe("hostile HTML", () => {
|
|
826
|
+
afterEach(() => {
|
|
827
|
+
jest.restoreAllMocks();
|
|
828
|
+
delete (window as unknown as { __pasteRan?: number }).__pasteRan;
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
const HOSTILE: Array<string> = [
|
|
832
|
+
'<img src="x" onerror="window.__pasteRan=1">',
|
|
833
|
+
'<svg onload="window.__pasteRan=2"><circle r="1"></circle></svg>',
|
|
834
|
+
"<script>window.__pasteRan=3</script>",
|
|
835
|
+
'<body onload="window.__pasteRan=4"><p>body</p></body>',
|
|
836
|
+
'<iframe srcdoc="<script>parent.__pasteRan=5</script>"></iframe>',
|
|
837
|
+
'<a href="javascript:window.__pasteRan=6">link</a>',
|
|
838
|
+
'<details open ontoggle="window.__pasteRan=7"><summary>s</summary></details>',
|
|
839
|
+
'<math><mtext><table><mglyph><style><img src=x onerror="window.__pasteRan=8"></style></mglyph></table></mtext></math>',
|
|
840
|
+
];
|
|
841
|
+
|
|
842
|
+
it("runs nothing and lets no markup through", async () => {
|
|
843
|
+
const markdown: string = pastedHtmlToMarkdown(HOSTILE.join("<p>ok</p>"));
|
|
844
|
+
|
|
845
|
+
// Give a handler that was queued the chance to run.
|
|
846
|
+
await new Promise((resolve: (value: unknown) => void) => {
|
|
847
|
+
setTimeout(resolve, 20);
|
|
848
|
+
});
|
|
849
|
+
expect((window as unknown as { __pasteRan?: number }).__pasteRan).toBe(
|
|
850
|
+
undefined,
|
|
851
|
+
);
|
|
852
|
+
expect(markdown).not.toMatch(/<[a-z!/]/i);
|
|
853
|
+
expect(markdown).not.toMatch(/on(error|load|toggle)|javascript|pasteRan/);
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
it("never assigns clipboard HTML to innerHTML", () => {
|
|
857
|
+
const setter: SpyInstance<(value: string) => void> = jest.spyOn(
|
|
858
|
+
Element.prototype,
|
|
859
|
+
"innerHTML",
|
|
860
|
+
"set",
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
pastedHtmlToMarkdown(HOSTILE.join(""));
|
|
864
|
+
isRichClipboardHtml(HOSTILE.join(""));
|
|
865
|
+
clipboardToMarkdown(clipboard({ "text/html": HOSTILE.join("") }));
|
|
866
|
+
|
|
867
|
+
expect(setter).not.toHaveBeenCalled();
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it("returns an empty string for empty HTML", () => {
|
|
871
|
+
expect(pastedHtmlToMarkdown("")).toBe("");
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
describe("isRichClipboardHtml", () => {
|
|
877
|
+
const rich: Array<string> = [
|
|
878
|
+
"<ul><li>a</li></ul>",
|
|
879
|
+
"<ol><li>a</li></ol>",
|
|
880
|
+
"<h2>Title</h2>",
|
|
881
|
+
'<p>see <a href="https://x.test">x</a></p>',
|
|
882
|
+
"<p><strong>bold</strong></p>",
|
|
883
|
+
"<p><em>it</em></p>",
|
|
884
|
+
"<p><s>gone</s></p>",
|
|
885
|
+
"<table><tr><td>a</td></tr></table>",
|
|
886
|
+
"<pre><code>x</code></pre>",
|
|
887
|
+
"<p><code>x</code></p>",
|
|
888
|
+
'<img src="https://i.test/a.png">',
|
|
889
|
+
"<blockquote><p>q</p></blockquote>",
|
|
890
|
+
'<p style="mso-list:l0 level1 lfo1"><span style="mso-list:Ignore">·</span>Word item</p>',
|
|
891
|
+
'<span style="font-weight:700">docs bold</span>',
|
|
892
|
+
'<p>At <time datetime="2026-04-20T11:21:00.000Z">Apr 20</time></p>',
|
|
893
|
+
];
|
|
894
|
+
for (const html of rich) {
|
|
895
|
+
it(`converts ${html}`, () => {
|
|
896
|
+
expect(isRichClipboardHtml(html)).toBe(true);
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const plain: Array<string> = [
|
|
901
|
+
"",
|
|
902
|
+
"<p>hello</p>",
|
|
903
|
+
"<p>one</p><p>two<br>three</p>",
|
|
904
|
+
'<p style="font-weight: 400; white-space: normal;">Chrome copy of plain text</p>',
|
|
905
|
+
'<a name="_Toc1">bookmark only</a>',
|
|
906
|
+
'<p><a href="javascript:alert(1)">unsafe link</a></p>',
|
|
907
|
+
'<img src="data:image/png;base64,AAAA">',
|
|
908
|
+
];
|
|
909
|
+
for (const html of plain) {
|
|
910
|
+
it(`leaves ${JSON.stringify(html)} to its plain text`, () => {
|
|
911
|
+
expect(isRichClipboardHtml(html)).toBe(false);
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// An icon-only link is not a link once its icon is gone.
|
|
916
|
+
it("leaves a paragraph whose only link is an icon to its plain text", () => {
|
|
917
|
+
expect(
|
|
918
|
+
isRichClipboardHtml(
|
|
919
|
+
'<p>see <a href="https://x.test"><svg></svg></a> here</p>',
|
|
920
|
+
),
|
|
921
|
+
).toBe(false);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
/*
|
|
925
|
+
* VS Code (and other code editors) put coloured spans in a
|
|
926
|
+
* white-space:pre <div> on the clipboard -- bold ones too, for keywords.
|
|
927
|
+
* The plain text is the content, often markdown source itself.
|
|
928
|
+
*/
|
|
929
|
+
it("leaves a code editor's copy to its plain text", () => {
|
|
930
|
+
expect(
|
|
931
|
+
isRichClipboardHtml(
|
|
932
|
+
'<meta charset="utf-8"><div style="color: #cccccc;background-color: #1f1f1f;font-family: Menlo, monospace;font-weight: normal;font-size: 12px;line-height: 18px;white-space: pre;"><div><span style="color: #569cd6;font-weight: bold;"># Heading</span></div><div><span style="color: #cccccc;">- </span><span style="color: #ce9178;">item</span></div></div>',
|
|
933
|
+
),
|
|
934
|
+
).toBe(false);
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
/*
|
|
938
|
+
* A code view drawn as a table -- a row per line, as the Dashboard's
|
|
939
|
+
* Kubernetes YAML tab and exception stack frames are -- copied in Chromium
|
|
940
|
+
* or Safari, which write the cells' white-space:pre on each of them.
|
|
941
|
+
*/
|
|
942
|
+
it("leaves lines copied out of a code view drawn as a table to their plain text", () => {
|
|
943
|
+
expect(isRichClipboardHtml(CHROME_CODE_VIEW_TABLE_COPY_HTML)).toBe(false);
|
|
944
|
+
expect(isRichClipboardHtml(WEBKIT_CODE_VIEW_TABLE_COPY_HTML)).toBe(false);
|
|
945
|
+
expect(
|
|
946
|
+
isRichClipboardHtml(
|
|
947
|
+
'<table><tbody><tr><th style="white-space: pre;"> key: value</th></tr></tbody></table>',
|
|
948
|
+
),
|
|
949
|
+
).toBe(false);
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
// A table of prose keeps its rows and columns.
|
|
953
|
+
it("still takes a table whose cells wrap their text as a table", () => {
|
|
954
|
+
expect(
|
|
955
|
+
isRichClipboardHtml(
|
|
956
|
+
'<table><tbody><tr><td style="white-space: normal;">Region</td><td>Status</td></tr><tr><td>EU</td><td>Down</td></tr></tbody></table>',
|
|
957
|
+
),
|
|
958
|
+
).toBe(true);
|
|
959
|
+
});
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
describe("clipboardToMarkdown", () => {
|
|
963
|
+
it("prefers rich HTML over the plain text beside it", () => {
|
|
964
|
+
expect(
|
|
965
|
+
clipboardToMarkdown(
|
|
966
|
+
clipboard({
|
|
967
|
+
"text/html": "<ul><li>a<ul><li>b</li></ul></li></ul>",
|
|
968
|
+
"text/plain": "a\nb",
|
|
969
|
+
}),
|
|
970
|
+
),
|
|
971
|
+
).toBe("- a\n - b");
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it("takes the plain text, bullets made markdown, when the HTML is plain", () => {
|
|
975
|
+
expect(
|
|
976
|
+
clipboardToMarkdown(
|
|
977
|
+
clipboard({
|
|
978
|
+
"text/html": "<p>•\tService down<br>o\tUsers</p>",
|
|
979
|
+
"text/plain": "•\tService down\no\tUsers",
|
|
980
|
+
}),
|
|
981
|
+
),
|
|
982
|
+
).toBe("- Service down\n - Users");
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
/*
|
|
986
|
+
* A textarea holds "\n" line breaks. The editor's source mode compares
|
|
987
|
+
* this result with the clipboard's plain text to decide whether the
|
|
988
|
+
* browser's own paste would insert the same thing; Windows' "\r\n" made
|
|
989
|
+
* every multi-line paste look different, and took it off the undo stack.
|
|
990
|
+
*/
|
|
991
|
+
it("writes line breaks as a textarea holds them", () => {
|
|
992
|
+
expect(
|
|
993
|
+
clipboardToMarkdown(clipboard({ "text/plain": "one\r\ntwo\rthree" })),
|
|
994
|
+
).toBe("one\ntwo\nthree");
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
/*
|
|
998
|
+
* Words copied out of a line keep their space: as HTML it sits at the
|
|
999
|
+
* edge of the document and is trimmed, and "very" pasted before "world"
|
|
1000
|
+
* came out as "**very**world".
|
|
1001
|
+
*/
|
|
1002
|
+
it("keeps the space at either edge of a one-line rich paste", () => {
|
|
1003
|
+
expect(
|
|
1004
|
+
clipboardToMarkdown(
|
|
1005
|
+
clipboard({ "text/html": "<b>very</b> ", "text/plain": "very " }),
|
|
1006
|
+
),
|
|
1007
|
+
).toBe("**very** ");
|
|
1008
|
+
expect(
|
|
1009
|
+
clipboardToMarkdown(
|
|
1010
|
+
clipboard({ "text/html": " <i>so</i>", "text/plain": "\tso" }),
|
|
1011
|
+
),
|
|
1012
|
+
).toBe(" *so*");
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
it("adds no edge spaces to a paste of several lines, or for a trailing newline", () => {
|
|
1016
|
+
expect(
|
|
1017
|
+
clipboardToMarkdown(
|
|
1018
|
+
clipboard({
|
|
1019
|
+
"text/html": "<ul><li>a</li><li>b</li></ul>",
|
|
1020
|
+
"text/plain": " a\nb ",
|
|
1021
|
+
}),
|
|
1022
|
+
),
|
|
1023
|
+
).toBe("- a\n- b");
|
|
1024
|
+
expect(
|
|
1025
|
+
clipboardToMarkdown(
|
|
1026
|
+
clipboard({ "text/html": "<b>line</b>", "text/plain": "line\n" }),
|
|
1027
|
+
),
|
|
1028
|
+
).toBe("**line**");
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
it("takes plain text alone", () => {
|
|
1032
|
+
expect(clipboardToMarkdown(clipboard({ "text/plain": "just text" }))).toBe(
|
|
1033
|
+
"just text",
|
|
1034
|
+
);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
it("returns null when there is nothing to paste as text", () => {
|
|
1038
|
+
expect(clipboardToMarkdown(clipboard({}))).toBeNull();
|
|
1039
|
+
expect(clipboardToMarkdown(clipboard({ "text/html": "" }))).toBeNull();
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
/*
|
|
1043
|
+
* "Copy image" in a browser puts the image's <img> tag on the clipboard
|
|
1044
|
+
* beside the picture itself. Uploading the picture beats linking to
|
|
1045
|
+
* somebody else's server.
|
|
1046
|
+
*/
|
|
1047
|
+
it("leaves an image-only paste to the image files beside it", () => {
|
|
1048
|
+
const data: { [format: string]: string } = {
|
|
1049
|
+
"text/html": '<img src="https://i.test/a.png">',
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
expect(
|
|
1053
|
+
clipboardToMarkdown(clipboard(data, ["text/html", "Files"])),
|
|
1054
|
+
).toBeNull();
|
|
1055
|
+
expect(
|
|
1056
|
+
clipboardToMarkdown(clipboard(data), { hasImageFiles: true }),
|
|
1057
|
+
).toBeNull();
|
|
1058
|
+
expect(clipboardToMarkdown(clipboard(data), { hasImageFiles: false })).toBe(
|
|
1059
|
+
"",
|
|
1060
|
+
);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
/*
|
|
1064
|
+
* Word and Excel put a picture of the copied text on the clipboard as
|
|
1065
|
+
* well as the text itself. The text is what was meant.
|
|
1066
|
+
*/
|
|
1067
|
+
it("keeps rich text even when image files come with it", () => {
|
|
1068
|
+
expect(
|
|
1069
|
+
clipboardToMarkdown(
|
|
1070
|
+
clipboard({ "text/html": "<ul><li>a</li></ul>", "text/plain": "a" }, [
|
|
1071
|
+
"text/html",
|
|
1072
|
+
"text/plain",
|
|
1073
|
+
"Files",
|
|
1074
|
+
]),
|
|
1075
|
+
),
|
|
1076
|
+
).toBe("- a");
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
// A file copied in Finder or Explorer comes with its name as plain text.
|
|
1080
|
+
it("leaves plain text that comes with files to the files", () => {
|
|
1081
|
+
expect(
|
|
1082
|
+
clipboardToMarkdown(
|
|
1083
|
+
clipboard({ "text/plain": "Screenshot.png" }, ["text/plain", "Files"]),
|
|
1084
|
+
),
|
|
1085
|
+
).toBeNull();
|
|
1086
|
+
expect(
|
|
1087
|
+
clipboardToMarkdown(clipboard({ "text/plain": "Screenshot.png" }), {
|
|
1088
|
+
hasImageFiles: false,
|
|
1089
|
+
}),
|
|
1090
|
+
).toBe("Screenshot.png");
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
it("reads a DOMStringList-like list of types", () => {
|
|
1094
|
+
const types: ReadonlyArray<string> = {
|
|
1095
|
+
length: 2,
|
|
1096
|
+
0: "text/plain",
|
|
1097
|
+
1: "Files",
|
|
1098
|
+
} as unknown as ReadonlyArray<string>;
|
|
1099
|
+
|
|
1100
|
+
expect(
|
|
1101
|
+
clipboardToMarkdown({
|
|
1102
|
+
getData: (): string => {
|
|
1103
|
+
return "name.png";
|
|
1104
|
+
},
|
|
1105
|
+
types,
|
|
1106
|
+
}),
|
|
1107
|
+
).toBeNull();
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
it("treats a format the browser refuses to read as empty", () => {
|
|
1111
|
+
expect(
|
|
1112
|
+
clipboardToMarkdown({
|
|
1113
|
+
getData: (format: string): string => {
|
|
1114
|
+
if (format === "text/html") {
|
|
1115
|
+
throw new Error("not allowed");
|
|
1116
|
+
}
|
|
1117
|
+
return "plain";
|
|
1118
|
+
},
|
|
1119
|
+
}),
|
|
1120
|
+
).toBe("plain");
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it("uses the text of plain HTML when there is no plain text", () => {
|
|
1124
|
+
expect(
|
|
1125
|
+
clipboardToMarkdown(clipboard({ "text/html": "<p>only html</p>" })),
|
|
1126
|
+
).toBe("only html");
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
it("gives a code editor's copy as its plain text", () => {
|
|
1130
|
+
expect(
|
|
1131
|
+
clipboardToMarkdown(
|
|
1132
|
+
clipboard({
|
|
1133
|
+
"text/html":
|
|
1134
|
+
'<div style="white-space: pre;"><div><span style="font-weight: bold;"># Heading</span></div></div>',
|
|
1135
|
+
"text/plain": "# Heading",
|
|
1136
|
+
}),
|
|
1137
|
+
),
|
|
1138
|
+
).toBe("# Heading");
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
/*
|
|
1142
|
+
* Taken as rich HTML, the four YAML lines became a one-column table: the
|
|
1143
|
+
* first line its header, the others' indentation gone -- the nesting that
|
|
1144
|
+
* is the YAML's meaning.
|
|
1145
|
+
*/
|
|
1146
|
+
it("gives lines copied out of a code view drawn as a table as their plain text", () => {
|
|
1147
|
+
for (const html of [
|
|
1148
|
+
CHROME_CODE_VIEW_TABLE_COPY_HTML,
|
|
1149
|
+
WEBKIT_CODE_VIEW_TABLE_COPY_HTML,
|
|
1150
|
+
]) {
|
|
1151
|
+
expect(
|
|
1152
|
+
clipboardToMarkdown(
|
|
1153
|
+
clipboard({
|
|
1154
|
+
"text/html": html,
|
|
1155
|
+
"text/plain": CODE_VIEW_TABLE_COPY_TEXT,
|
|
1156
|
+
}),
|
|
1157
|
+
),
|
|
1158
|
+
).toBe("metadata:\n name: api\n labels:\n app: api");
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
it("turns both real viewer copies back into the note's markdown", () => {
|
|
1163
|
+
expect(
|
|
1164
|
+
clipboardToMarkdown(
|
|
1165
|
+
clipboard({
|
|
1166
|
+
"text/html": CHROME_VIEWER_COPY_HTML,
|
|
1167
|
+
"text/plain": CHROME_VIEWER_COPY_TEXT,
|
|
1168
|
+
}),
|
|
1169
|
+
),
|
|
1170
|
+
).toBe(VIEWER_COPY_SOURCE_MARKDOWN);
|
|
1171
|
+
expect(
|
|
1172
|
+
clipboardToMarkdown(
|
|
1173
|
+
clipboard({
|
|
1174
|
+
"text/html": FIREFOX_VIEWER_COPY_HTML,
|
|
1175
|
+
"text/plain": FIREFOX_VIEWER_COPY_TEXT,
|
|
1176
|
+
}),
|
|
1177
|
+
),
|
|
1178
|
+
).toBe(VIEWER_COPY_SOURCE_MARKDOWN);
|
|
1179
|
+
});
|
|
1180
|
+
});
|