@oneuptime/common 11.7.4 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (872) hide show
  1. package/Models/AnalyticsModels/ExceptionInstance.ts +67 -0
  2. package/Models/AnalyticsModels/Index.ts +10 -0
  3. package/Models/AnalyticsModels/Log.ts +60 -0
  4. package/Models/AnalyticsModels/RumSession.ts +1401 -0
  5. package/Models/AnalyticsModels/RumSessionChunk.ts +798 -0
  6. package/Models/AnalyticsModels/Span.ts +60 -0
  7. package/Models/DatabaseModels/AIAgentTaskPullRequest.ts +52 -0
  8. package/Models/DatabaseModels/AIRun.ts +31 -0
  9. package/Models/DatabaseModels/Alert.ts +29 -0
  10. package/Models/DatabaseModels/AlertCustomField.ts +1 -1
  11. package/Models/DatabaseModels/AlertFeed.ts +1 -0
  12. package/Models/DatabaseModels/AutoRemediationRule.ts +989 -0
  13. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +874 -0
  14. package/Models/DatabaseModels/CodeRepository.ts +129 -0
  15. package/Models/DatabaseModels/GlobalConfig.ts +312 -0
  16. package/Models/DatabaseModels/Incident.ts +29 -0
  17. package/Models/DatabaseModels/IncidentCustomField.ts +1 -1
  18. package/Models/DatabaseModels/IncidentFeed.ts +1 -0
  19. package/Models/DatabaseModels/Index.ts +21 -8
  20. package/Models/DatabaseModels/InstanceHealthLog.ts +8 -0
  21. package/Models/DatabaseModels/MonitorCustomField.ts +1 -1
  22. package/Models/DatabaseModels/MonitorStatus.ts +1 -1
  23. package/Models/DatabaseModels/NetworkDeviceLabelRule.ts +2 -2
  24. package/Models/DatabaseModels/NetworkDeviceOwnerRule.ts +2 -2
  25. package/Models/DatabaseModels/NetworkSiteAssignmentRule.ts +1 -1
  26. package/Models/DatabaseModels/OnCallDutyPolicyCustomField.ts +1 -1
  27. package/Models/DatabaseModels/Project.ts +231 -0
  28. package/Models/DatabaseModels/RumApplication.ts +906 -0
  29. package/Models/DatabaseModels/RumSessionErasureRequest.ts +561 -0
  30. package/Models/DatabaseModels/RumSessionPin.ts +457 -0
  31. package/Models/DatabaseModels/RumSessionReplayView.ts +406 -0
  32. package/Models/DatabaseModels/RunbookCredential.ts +673 -0
  33. package/Models/DatabaseModels/RunbookSecret.ts +7 -7
  34. package/Models/DatabaseModels/{RunbookAgent.ts → Runner.ts} +206 -46
  35. package/Models/DatabaseModels/{RunbookAgentJob.ts → RunnerJob.ts} +151 -18
  36. package/Models/DatabaseModels/{RunbookAgentOwnerTeam.ts → RunnerOwnerTeam.ts} +41 -43
  37. package/Models/DatabaseModels/{RunbookAgentOwnerUser.ts → RunnerOwnerUser.ts} +41 -43
  38. package/Models/DatabaseModels/ScheduledMaintenanceCustomField.ts +1 -1
  39. package/Models/DatabaseModels/StatusPageCustomField.ts +1 -1
  40. package/Models/DatabaseModels/TeamCustomField.ts +1 -1
  41. package/Models/DatabaseModels/TeamMemberCustomField.ts +1 -1
  42. package/Server/API/AIAgentDataAPI.ts +179 -48
  43. package/Server/API/AIAgentTaskAPI.ts +36 -37
  44. package/Server/API/AIAgentTaskLogAPI.ts +19 -33
  45. package/Server/API/AutoRemediationAPI.ts +557 -0
  46. package/Server/API/BaseAnalyticsAPI.ts +4 -1
  47. package/Server/API/CommonAPI.ts +35 -0
  48. package/Server/API/DashboardAPI.ts +340 -15
  49. package/Server/API/TelemetryAPI.ts +1622 -3
  50. package/Server/API/UserIncomingCallNumberAPI.ts +31 -0
  51. package/Server/DatabaseConfig.ts +26 -1
  52. package/Server/EnvironmentConfig.ts +13 -0
  53. package/Server/Infrastructure/Postgres/SchemaMigrations/1785417351021-AddSessionReplay.ts +293 -0
  54. package/Server/Infrastructure/Postgres/SchemaMigrations/1785491583874-EnableSessionReplayByDefault.ts +67 -0
  55. package/Server/Infrastructure/Postgres/SchemaMigrations/1785496223183-AddMonitorSummaryToIncidentAndAlert.ts +33 -0
  56. package/Server/Infrastructure/Postgres/SchemaMigrations/1785516996695-AddSessionReplayReliabilityColumns.ts +40 -0
  57. package/Server/Infrastructure/Postgres/SchemaMigrations/1785533806494-AddSessionReplayCorrelationColumns.ts +54 -0
  58. package/Server/Infrastructure/Postgres/SchemaMigrations/1785749674203-AddRedisAndPostgresHealthNotificationColumns.ts +104 -0
  59. package/Server/Infrastructure/Postgres/SchemaMigrations/1785763818197-AddAutoRemediation.ts +349 -0
  60. package/Server/Infrastructure/Postgres/SchemaMigrations/1785768089408-AddRemediationVerification.ts +67 -0
  61. package/Server/Infrastructure/Postgres/SchemaMigrations/1785776960660-AddRunnerCapabilities.ts +23 -0
  62. package/Server/Infrastructure/Postgres/SchemaMigrations/1785790000000-AddIncidentInvestigationGating.ts +42 -0
  63. package/Server/Infrastructure/Postgres/SchemaMigrations/1785800000000-AddRunbookCredential.ts +84 -0
  64. package/Server/Infrastructure/Postgres/SchemaMigrations/1785810000000-RenameRunbookAgentToRunner.ts +152 -0
  65. package/Server/Infrastructure/Postgres/SchemaMigrations/1785860000000-SessionReplayDefaultToSensitiveInputMasking.ts +81 -0
  66. package/Server/Infrastructure/Postgres/SchemaMigrations/1785870000000-AddAiCommandRemediation.ts +151 -0
  67. package/Server/Infrastructure/Postgres/SchemaMigrations/1785880000000-AlignRunnerSchemaWithEntities.ts +597 -0
  68. package/Server/Infrastructure/Postgres/SchemaMigrations/1785890000000-AddFixVerificationAndAutomaticCodeFixes.ts +71 -0
  69. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +32 -0
  70. package/Server/Services/AIRunService.ts +10 -0
  71. package/Server/Services/AIService.ts +30 -0
  72. package/Server/Services/AlertService.ts +41 -4
  73. package/Server/Services/AlertSeverityService.ts +25 -11
  74. package/Server/Services/AlertStateService.ts +25 -11
  75. package/Server/Services/AutoRemediationRuleEngineService.ts +1101 -0
  76. package/Server/Services/{RunbookAgentOwnerTeamService.ts → AutoRemediationRuleService.ts} +1 -1
  77. package/Server/Services/AutoRemediationSuggestionService.ts +183 -0
  78. package/Server/Services/BillingService.ts +50 -1
  79. package/Server/Services/DatabaseService.ts +92 -4
  80. package/Server/Services/IncidentService.ts +56 -4
  81. package/Server/Services/IncidentSeverityService.ts +25 -11
  82. package/Server/Services/IncidentStateService.ts +25 -11
  83. package/Server/Services/Index.ts +19 -0
  84. package/Server/Services/MonitorService.ts +24 -0
  85. package/Server/Services/MonitorStatusService.ts +25 -11
  86. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +18 -19
  87. package/Server/Services/NetworkDeviceLabelRuleService.ts +39 -0
  88. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +18 -19
  89. package/Server/Services/NetworkDeviceOwnerRuleService.ts +37 -0
  90. package/Server/Services/NetworkDeviceService.ts +172 -21
  91. package/Server/Services/NetworkSiteAssignmentRuleService.ts +92 -12
  92. package/Server/Services/NetworkSiteService.ts +27 -10
  93. package/Server/Services/ProjectService.ts +47 -0
  94. package/Server/Services/RumApplicationService.ts +240 -1
  95. package/Server/Services/RumSessionChunkService.ts +11 -0
  96. package/Server/Services/RumSessionErasureRequestService.ts +197 -0
  97. package/Server/Services/RumSessionPinService.ts +213 -0
  98. package/Server/Services/RumSessionReplayViewService.ts +201 -0
  99. package/Server/Services/RumSessionService.ts +123 -0
  100. package/Server/Services/RunbookCredentialService.ts +76 -0
  101. package/Server/Services/{RunbookAgentJobService.ts → RunnerJobService.ts} +202 -51
  102. package/Server/Services/{RunbookAgentOwnerUserService.ts → RunnerOwnerTeamService.ts} +1 -1
  103. package/Server/Services/RunnerOwnerUserService.ts +10 -0
  104. package/Server/Services/RunnerService.ts +172 -0
  105. package/Server/Services/ScheduledMaintenanceStateService.ts +25 -11
  106. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +15 -1
  107. package/Server/Services/ServiceService.ts +8 -2
  108. package/Server/Services/StatusPageService.ts +240 -36
  109. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +20 -1
  110. package/Server/Services/TelemetryExceptionService.ts +3 -16
  111. package/Server/Services/TelemetryUsageBillingService.ts +33 -2
  112. package/Server/Services/TraceAggregationService.ts +196 -32
  113. package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +17 -0
  114. package/Server/Types/Billing/MeteredPlan/ServerMeteredPlan.ts +10 -0
  115. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +63 -7
  116. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +128 -0
  117. package/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.ts +54 -0
  118. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +17 -10
  119. package/Server/Utils/AI/CodeFix/CodeFixRunQueue.ts +8 -6
  120. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +943 -0
  121. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +866 -0
  122. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +823 -0
  123. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +103 -12
  124. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +75 -17
  125. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +28 -6
  126. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +202 -12
  127. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +264 -14
  128. package/Server/Utils/AI/SRE/Insights/Triage.ts +2 -7
  129. package/Server/Utils/AI/SRE/InvestigationGrader.ts +5 -24
  130. package/Server/Utils/AI/SRE/InvestigationQueue.ts +259 -20
  131. package/Server/Utils/AI/SRE/PostedRootCause.ts +82 -0
  132. package/Server/Utils/AI/SRE/README.md +3 -2
  133. package/Server/Utils/AI/SRE/RemediationHandoff.ts +82 -0
  134. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +6 -19
  135. package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +2 -2
  136. package/Server/Utils/Alert/AlertPrivacyFilter.ts +58 -0
  137. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +604 -0
  138. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +619 -0
  139. package/Server/Utils/CorsOptions.ts +52 -0
  140. package/Server/Utils/Database/RelationIdUtil.ts +68 -0
  141. package/Server/Utils/Incident/IncidentPrivacyFilter.ts +60 -0
  142. package/Server/Utils/InstanceHealth/PostgresHealth.ts +286 -0
  143. package/Server/Utils/InstanceHealth/RedisHealth.ts +434 -0
  144. package/Server/Utils/Monitor/MonitorAlert.ts +20 -0
  145. package/Server/Utils/Monitor/MonitorIncident.ts +20 -0
  146. package/Server/Utils/Monitor/MonitorResource.ts +19 -0
  147. package/Server/Utils/Monitor/MonitorSummaryCapture.ts +104 -0
  148. package/Server/Utils/NetworkDevice/RulePatternValidator.ts +44 -0
  149. package/Server/Utils/OpenAPI.ts +2 -1
  150. package/Server/Utils/Runbook/RunbookExecutePermission.ts +94 -0
  151. package/Server/Utils/SessionReplay/SessionReplayErasureTombstone.ts +128 -0
  152. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +586 -0
  153. package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +188 -0
  154. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +1449 -0
  155. package/Server/Utils/SessionReplay/SessionReplayRetention.ts +114 -0
  156. package/Server/Utils/SessionReplay/SessionReplayTargeting.ts +144 -0
  157. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +92 -0
  158. package/Server/Utils/StartServer.ts +47 -3
  159. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +44 -10
  160. package/Tests/App/Dashboard/AddWidgetModal.test.tsx +557 -0
  161. package/Tests/App/Dashboard/WidgetCatalog.test.ts +479 -0
  162. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +343 -0
  163. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +320 -0
  164. package/Tests/Models/AnalyticsModels/RumSessionSchema.test.ts +455 -0
  165. package/Tests/Models/AnalyticsModels/SessionIdCorrelation.test.ts +158 -0
  166. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +447 -0
  167. package/Tests/Models/RunnerVersionLabel.test.ts +223 -0
  168. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +1287 -0
  169. package/Tests/Server/API/LegacyRunbookAgentRoutes.test.ts +117 -0
  170. package/Tests/Server/API/SessionReplayAPI.test.ts +2093 -0
  171. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +429 -0
  172. package/Tests/Server/API/UserSmsApi.test.ts +68 -2
  173. package/Tests/Server/DatabaseConfig.test.ts +150 -0
  174. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +2 -2
  175. package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +2 -3
  176. package/Tests/Server/Services/AIRunCodeFixClaimScoping.test.ts +100 -0
  177. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +8 -0
  178. package/Tests/Server/Services/AutoRemediationCommandRuleEngine.test.ts +334 -0
  179. package/Tests/Server/Services/AutoRemediationRuleEngineService.test.ts +517 -0
  180. package/Tests/Server/Services/BillingService.test.ts +158 -1
  181. package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +24 -2
  182. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +17 -10
  183. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +128 -0
  184. package/Tests/Server/Services/DatabaseServiceUpdateWriteRouting.test.ts +178 -0
  185. package/Tests/Server/Services/DatabaseServiceUpdateWriteRoutingMonitor.test.ts +206 -0
  186. package/Tests/Server/Services/MonitorServiceOperationalStatusSelection.test.ts +145 -0
  187. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +726 -0
  188. package/Tests/Server/Services/NetworkDeviceSiteAssignment.test.ts +570 -9
  189. package/Tests/Server/Services/NetworkSiteAssignmentRuleService.test.ts +94 -1
  190. package/Tests/Server/Services/NetworkSiteService.test.ts +85 -0
  191. package/Tests/Server/Services/RumApplicationReplayHealth.test.ts +114 -0
  192. package/Tests/Server/Services/RunbookCredentialService.test.ts +657 -0
  193. package/Tests/Server/Services/SessionReplayCreatePath.test.ts +376 -0
  194. package/Tests/Server/Services/SessionReplayGateCacheInvalidation.test.ts +211 -0
  195. package/Tests/Server/Services/SessionReplayMetering.test.ts +174 -0
  196. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +555 -0
  197. package/Tests/Server/Services/TelemetryExceptionAIFixReadiness.test.ts +25 -4
  198. package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +17 -2
  199. package/Tests/Server/Services/TraceAggregationService.test.ts +48 -0
  200. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +855 -0
  201. package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +38 -27
  202. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +44 -0
  203. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +475 -0
  204. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +257 -0
  205. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.test.ts +236 -0
  206. package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +44 -6
  207. package/Tests/Server/Utils/AI/CodeFixRunQueue.test.ts +47 -1
  208. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +2 -2
  209. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +294 -0
  210. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +2 -2
  211. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +47 -3
  212. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +233 -0
  213. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +463 -0
  214. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  215. package/Tests/Server/Utils/AI/RemediationAllowlistNormalization.test.ts +248 -0
  216. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +1008 -0
  217. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +887 -0
  218. package/Tests/Server/Utils/AI/RemediationHandoff.test.ts +306 -0
  219. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +745 -0
  220. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +291 -0
  221. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1050 -0
  222. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +350 -0
  223. package/Tests/Server/Utils/AutoRemediation/RemediationVerifier.test.ts +341 -0
  224. package/Tests/Server/Utils/CorsOptions.test.ts +119 -0
  225. package/Tests/Server/Utils/Database/RelationIdUtil.test.ts +120 -0
  226. package/Tests/Server/Utils/Database/ResolveReferenceId.test.ts +60 -0
  227. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +337 -0
  228. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +671 -0
  229. package/Tests/Server/Utils/Monitor/MonitorSummaryCapture.test.ts +205 -0
  230. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +313 -0
  231. package/Tests/Server/Utils/PushNotificationUtil.test.ts +228 -0
  232. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +177 -0
  233. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +108 -0
  234. package/Tests/Server/Utils/SessionReplay/SessionReplayRetention.test.ts +229 -0
  235. package/Tests/Server/Utils/SessionReplay/SessionReplayTargeting.test.ts +246 -0
  236. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +110 -0
  237. package/Tests/Types/AI/AIRunStatus.test.ts +25 -0
  238. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +1063 -0
  239. package/Tests/Types/BaseDatabase/AggregationType.test.ts +56 -0
  240. package/Tests/Types/CustomField/CustomFieldDropdownOption.test.ts +227 -0
  241. package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +811 -0
  242. package/Tests/Types/Dashboard/RumDashboardTemplate.test.ts +1238 -0
  243. package/Tests/Types/Day/DayOfWeek.test.ts +46 -0
  244. package/Tests/Types/Docker/DockerInventoryExtractor.test.ts +249 -0
  245. package/Tests/Types/Kubernetes/KubernetesInventoryExtractor.test.ts +231 -0
  246. package/Tests/Types/Kubernetes/KubernetesRightSizing.test.ts +323 -0
  247. package/Tests/Types/Metrics/MetricDownsamplingRetentionDays.test.ts +77 -0
  248. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +42 -14
  249. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +2 -2
  250. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +3 -2
  251. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +1 -1
  252. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +6 -3
  253. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +285 -0
  254. package/Tests/Types/Podman/PodmanInventoryExtractor.test.ts +159 -0
  255. package/Tests/Types/Runbook/RunbookExecutionDeadline.test.ts +268 -0
  256. package/Tests/Types/Runbook/RunbookStepType.test.ts +242 -0
  257. package/Tests/Types/Telemetry/TelemetryRetentionConfig.test.ts +225 -0
  258. package/Tests/Types/Text.test.ts +60 -0
  259. package/Tests/UI/Components/AutocompleteTextInputMenu.test.tsx +555 -0
  260. package/Tests/UI/Components/BulkLabelActions.test.tsx +1502 -0
  261. package/Tests/UI/Components/BulkOwnerActions.test.tsx +1255 -0
  262. package/Tests/UI/Components/CustomFields/DropdownOptionsInput.test.tsx +228 -0
  263. package/Tests/UI/Components/Detail/DropdownColors.test.tsx +128 -0
  264. package/Tests/UI/Components/Dropdown/DropdownValueBadge.test.tsx +60 -0
  265. package/Tests/UI/Components/FilterModalOperatorMenu.test.tsx +674 -0
  266. package/Tests/UI/Components/Forms/ColorPicker.test.tsx +48 -0
  267. package/Tests/UI/Components/Forms/ColorPickerPopup.test.tsx +222 -0
  268. package/Tests/UI/Components/Forms/IconPickerPopup.test.tsx +270 -0
  269. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +125 -6
  270. package/Tests/UI/Components/OperatorSelectorFilterTypes.test.tsx +1595 -0
  271. package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +851 -0
  272. package/Tests/UI/Components/OperatorSelectorMenu.test.tsx +1229 -0
  273. package/Tests/UI/Components/Pagination.test.tsx +85 -0
  274. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +248 -0
  275. package/Tests/UI/Rum/ChunkLoader.test.ts +790 -0
  276. package/Tests/UI/Rum/ReplayScrubber.test.tsx +236 -0
  277. package/Tests/UI/Rum/ReplayStage.test.tsx +606 -0
  278. package/Tests/UI/Utils/Timezone.test.ts +86 -0
  279. package/Tests/UI/Utils/UseClockTick.test.tsx +276 -0
  280. package/Tests/Utils/AiRemediation/CommandPolicy.test.ts +1063 -0
  281. package/Tests/Utils/Dashboard/ClockWidgetFormat.test.ts +1374 -0
  282. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +362 -0
  283. package/Tests/Utils/Dashboard/DashboardHtmlComponent.test.ts +217 -0
  284. package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +656 -0
  285. package/Tests/Utils/Dashboard/ValueWidgetLayout.test.ts +1007 -0
  286. package/Tests/Utils/Metrics/MetricSeriesScope.test.ts +1197 -0
  287. package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +839 -0
  288. package/Tests/Utils/NetworkSite/CidrMatchUtil.test.ts +69 -0
  289. package/Tests/Utils/Rules/RulePatternMatchUtil.test.ts +274 -0
  290. package/Tests/Utils/Rum/ChunkMath.test.ts +286 -0
  291. package/Tests/Utils/Rum/Masking.test.ts +186 -0
  292. package/Tests/Utils/Rum/SessionIdentity.test.ts +272 -0
  293. package/Tests/Utils/Rum/SessionSampling.test.ts +164 -0
  294. package/Tests/Utils/Rum/UrlScrubber.test.ts +175 -0
  295. package/Tests/Utils/StatusPage/Report.test.ts +708 -0
  296. package/Tests/Utils/ValueFormatter.test.ts +299 -1
  297. package/Types/AI/AIRunStatus.ts +18 -7
  298. package/Types/AI/AIRunType.ts +18 -0
  299. package/Types/AI/FixVerificationStatus.ts +21 -0
  300. package/Types/AnalyticsDatabase/AnalyticsTableName.ts +16 -0
  301. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +304 -0
  302. package/Types/AutoRemediation/AutoRemediationExecutionMode.ts +17 -0
  303. package/Types/AutoRemediation/AutoRemediationSuggestionStatus.ts +35 -0
  304. package/Types/AutoRemediation/AutoRemediationSuggestionType.ts +17 -0
  305. package/Types/AutoRemediation/AutoRemediationTriggerEntity.ts +11 -0
  306. package/Types/AutoRemediation/AutoRemediationVerificationStatus.ts +21 -0
  307. package/Types/CustomField/CustomFieldDropdownOption.ts +190 -0
  308. package/Types/Dashboard/DashboardComponentType.ts +2 -0
  309. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +4 -0
  310. package/Types/Dashboard/DashboardComponents/DashboardClockComponent.ts +45 -0
  311. package/Types/Dashboard/DashboardComponents/DashboardHtmlComponent.ts +27 -0
  312. package/Types/Dashboard/DashboardComponents/DashboardValueComponent.ts +8 -0
  313. package/Types/Dashboard/DashboardTemplates.ts +516 -0
  314. package/Types/Date.ts +6 -0
  315. package/Types/Email/EmailTemplateType.ts +2 -0
  316. package/Types/Kubernetes/KubernetesRightSizing.ts +353 -0
  317. package/Types/MeteredPlan/ProductType.ts +1 -0
  318. package/Types/Monitor/MonitorStepMetricMonitor.ts +27 -1
  319. package/Types/Monitor/MonitorSteps.ts +5 -0
  320. package/Types/Monitor/MonitorSummarySnapshot.ts +88 -0
  321. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +52 -10
  322. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +7 -5
  323. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +101 -20
  324. package/Types/Monitor/RumAlertTemplates.ts +386 -0
  325. package/Types/Permission.ts +204 -24
  326. package/Types/Rum/SessionReplay.ts +452 -0
  327. package/Types/Rum/SessionReplayCaptureTrigger.ts +26 -0
  328. package/Types/Rum/SessionReplayConsentMode.ts +27 -0
  329. package/Types/Rum/SessionReplayMaskingMode.ts +86 -0
  330. package/Types/Rum/SessionReplayTriggerReason.ts +31 -0
  331. package/Types/Runbook/RunbookCredentialType.ts +6 -0
  332. package/Types/Runbook/RunbookStep.ts +66 -1
  333. package/Types/Runbook/RunbookStepType.ts +26 -0
  334. package/Types/Runbook/RunnerJobOrigin.ts +20 -0
  335. package/Types/Runbook/{RunbookAgentJobStatus.ts → RunnerJobStatus.ts} +2 -2
  336. package/Types/StatusPage/StatusPageReport.ts +99 -0
  337. package/Types/Telemetry/TelemetryRetentionConfig.ts +18 -1
  338. package/Types/Text.ts +33 -0
  339. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +260 -75
  340. package/UI/Components/BulkUpdate/BulkLabelActions.tsx +99 -9
  341. package/UI/Components/BulkUpdate/BulkOwnerActions.tsx +11 -0
  342. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +19 -14
  343. package/UI/Components/CustomFields/DropdownOptionsInput.tsx +144 -54
  344. package/UI/Components/Detail/Detail.tsx +9 -22
  345. package/UI/Components/Dropdown/Dropdown.tsx +2 -5
  346. package/UI/Components/Dropdown/DropdownMenuZIndex.ts +7 -0
  347. package/UI/Components/Dropdown/DropdownValueBadge.tsx +61 -0
  348. package/UI/Components/Filters/OperatorSelector.tsx +350 -39
  349. package/UI/Components/Forms/Fields/ColorPicker.tsx +60 -27
  350. package/UI/Components/Forms/Fields/IconPicker.tsx +89 -60
  351. package/UI/Components/Forms/Validation.ts +9 -4
  352. package/UI/Components/List/List.tsx +1 -0
  353. package/UI/Components/ModelTable/CustomFieldColumns.tsx +41 -32
  354. package/UI/Components/Pagination/Pagination.tsx +33 -10
  355. package/UI/Components/Table/Table.tsx +1 -0
  356. package/UI/Types/UseAnchoredFieldPopup.ts +321 -0
  357. package/UI/Utils/Dropdown.ts +26 -0
  358. package/UI/Utils/Timezone.ts +18 -2
  359. package/UI/Utils/UseClockTick.ts +78 -0
  360. package/Utils/AiRemediation/CommandPolicy.ts +398 -0
  361. package/Utils/Dashboard/ClockWidgetFormat.ts +604 -0
  362. package/Utils/Dashboard/Components/DashboardClockComponent.ts +181 -0
  363. package/Utils/Dashboard/Components/DashboardHtmlComponent.ts +162 -0
  364. package/Utils/Dashboard/Components/DashboardValueComponent.ts +12 -1
  365. package/Utils/Dashboard/Components/Index.ts +14 -0
  366. package/Utils/Dashboard/HtmlWidgetDocument.ts +309 -0
  367. package/Utils/Dashboard/ValueWidgetLayout.ts +471 -0
  368. package/Utils/Metrics/MetricSeriesScope.ts +301 -0
  369. package/Utils/Monitor/MonitorSummarySnapshotUtil.ts +593 -0
  370. package/Utils/NetworkSite/CidrMatchUtil.ts +30 -17
  371. package/Utils/Rules/RulePatternMatchUtil.ts +103 -0
  372. package/Utils/Rum/ChunkMath.ts +211 -0
  373. package/Utils/Rum/Masking.ts +168 -0
  374. package/Utils/Rum/SessionIdentity.ts +230 -0
  375. package/Utils/Rum/SessionSampling.ts +108 -0
  376. package/Utils/Rum/UrlScrubber.ts +185 -0
  377. package/Utils/Schema/ModelSchema.ts +27 -3
  378. package/Utils/StatusPage/Report.ts +304 -0
  379. package/Utils/ValueFormatter.ts +300 -54
  380. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +63 -0
  381. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
  382. package/build/dist/Models/AnalyticsModels/Index.js +10 -0
  383. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  384. package/build/dist/Models/AnalyticsModels/Log.js +56 -0
  385. package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
  386. package/build/dist/Models/AnalyticsModels/RumSession.js +1159 -0
  387. package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -0
  388. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +670 -0
  389. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -0
  390. package/build/dist/Models/AnalyticsModels/Span.js +56 -0
  391. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js +54 -0
  393. package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/AIRun.js +32 -0
  395. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/Alert.js +30 -0
  397. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  398. package/build/dist/Models/DatabaseModels/AlertCustomField.js +1 -1
  399. package/build/dist/Models/DatabaseModels/AlertCustomField.js.map +1 -1
  400. package/build/dist/Models/DatabaseModels/AlertFeed.js +1 -0
  401. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  402. package/build/dist/Models/DatabaseModels/AutoRemediationRule.js +998 -0
  403. package/build/dist/Models/DatabaseModels/AutoRemediationRule.js.map +1 -0
  404. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +917 -0
  405. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -0
  406. package/build/dist/Models/DatabaseModels/CodeRepository.js +132 -0
  407. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  408. package/build/dist/Models/DatabaseModels/GlobalConfig.js +327 -0
  409. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  410. package/build/dist/Models/DatabaseModels/Incident.js +30 -0
  411. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  412. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +1 -1
  413. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  414. package/build/dist/Models/DatabaseModels/IncidentFeed.js +1 -0
  415. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  416. package/build/dist/Models/DatabaseModels/Index.js +20 -8
  417. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  418. package/build/dist/Models/DatabaseModels/InstanceHealthLog.js +8 -0
  419. package/build/dist/Models/DatabaseModels/InstanceHealthLog.js.map +1 -1
  420. package/build/dist/Models/DatabaseModels/MonitorCustomField.js +1 -1
  421. package/build/dist/Models/DatabaseModels/MonitorCustomField.js.map +1 -1
  422. package/build/dist/Models/DatabaseModels/MonitorStatus.js +1 -1
  423. package/build/dist/Models/DatabaseModels/MonitorStatus.js.map +1 -1
  424. package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js +2 -2
  425. package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js.map +1 -1
  426. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js +2 -2
  427. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js.map +1 -1
  428. package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js +1 -1
  429. package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js.map +1 -1
  430. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js +1 -1
  431. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js.map +1 -1
  432. package/build/dist/Models/DatabaseModels/Project.js +235 -0
  433. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  434. package/build/dist/Models/DatabaseModels/RumApplication.js +930 -0
  435. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  436. package/build/dist/Models/DatabaseModels/RumSessionErasureRequest.js +591 -0
  437. package/build/dist/Models/DatabaseModels/RumSessionErasureRequest.js.map +1 -0
  438. package/build/dist/Models/DatabaseModels/RumSessionPin.js +483 -0
  439. package/build/dist/Models/DatabaseModels/RumSessionPin.js.map +1 -0
  440. package/build/dist/Models/DatabaseModels/RumSessionReplayView.js +433 -0
  441. package/build/dist/Models/DatabaseModels/RumSessionReplayView.js.map +1 -0
  442. package/build/dist/Models/DatabaseModels/RunbookCredential.js +693 -0
  443. package/build/dist/Models/DatabaseModels/RunbookCredential.js.map +1 -0
  444. package/build/dist/Models/DatabaseModels/RunbookSecret.js +8 -8
  445. package/build/dist/Models/DatabaseModels/RunbookSecret.js.map +1 -1
  446. package/build/dist/Models/DatabaseModels/{RunbookAgent.js → Runner.js} +235 -70
  447. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -0
  448. package/build/dist/Models/DatabaseModels/{RunbookAgentJob.js → RunnerJob.js} +173 -40
  449. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -0
  450. package/build/dist/Models/DatabaseModels/{RunbookAgentOwnerTeam.js → RunnerOwnerTeam.js} +57 -57
  451. package/build/dist/Models/DatabaseModels/RunnerOwnerTeam.js.map +1 -0
  452. package/build/dist/Models/DatabaseModels/{RunbookAgentOwnerUser.js → RunnerOwnerUser.js} +57 -57
  453. package/build/dist/Models/DatabaseModels/RunnerOwnerUser.js.map +1 -0
  454. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js +1 -1
  455. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js.map +1 -1
  456. package/build/dist/Models/DatabaseModels/StatusPageCustomField.js +1 -1
  457. package/build/dist/Models/DatabaseModels/StatusPageCustomField.js.map +1 -1
  458. package/build/dist/Models/DatabaseModels/TeamCustomField.js +1 -1
  459. package/build/dist/Models/DatabaseModels/TeamCustomField.js.map +1 -1
  460. package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js +1 -1
  461. package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js.map +1 -1
  462. package/build/dist/Server/API/AIAgentDataAPI.js +130 -45
  463. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  464. package/build/dist/Server/API/AIAgentTaskAPI.js +26 -36
  465. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  466. package/build/dist/Server/API/AIAgentTaskLogAPI.js +11 -28
  467. package/build/dist/Server/API/AIAgentTaskLogAPI.js.map +1 -1
  468. package/build/dist/Server/API/AutoRemediationAPI.js +416 -0
  469. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -0
  470. package/build/dist/Server/API/BaseAnalyticsAPI.js +4 -1
  471. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  472. package/build/dist/Server/API/CommonAPI.js +24 -0
  473. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  474. package/build/dist/Server/API/DashboardAPI.js +265 -6
  475. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  476. package/build/dist/Server/API/TelemetryAPI.js +940 -3
  477. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  478. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +18 -0
  479. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  480. package/build/dist/Server/DatabaseConfig.js +23 -4
  481. package/build/dist/Server/DatabaseConfig.js.map +1 -1
  482. package/build/dist/Server/EnvironmentConfig.js +8 -0
  483. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  484. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785417351021-AddSessionReplay.js +106 -0
  485. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785417351021-AddSessionReplay.js.map +1 -0
  486. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785491583874-EnableSessionReplayByDefault.js +37 -0
  487. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785491583874-EnableSessionReplayByDefault.js.map +1 -0
  488. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785496223183-AddMonitorSummaryToIncidentAndAlert.js +18 -0
  489. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785496223183-AddMonitorSummaryToIncidentAndAlert.js.map +1 -0
  490. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785516996695-AddSessionReplayReliabilityColumns.js +29 -0
  491. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785516996695-AddSessionReplayReliabilityColumns.js.map +1 -0
  492. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785533806494-AddSessionReplayCorrelationColumns.js +35 -0
  493. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785533806494-AddSessionReplayCorrelationColumns.js.map +1 -0
  494. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785749674203-AddRedisAndPostgresHealthNotificationColumns.js +40 -0
  495. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785749674203-AddRedisAndPostgresHealthNotificationColumns.js.map +1 -0
  496. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785763818197-AddAutoRemediation.js +128 -0
  497. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785763818197-AddAutoRemediation.js.map +1 -0
  498. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785768089408-AddRemediationVerification.js +28 -0
  499. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785768089408-AddRemediationVerification.js.map +1 -0
  500. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785776960660-AddRunnerCapabilities.js +14 -0
  501. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785776960660-AddRunnerCapabilities.js.map +1 -0
  502. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785790000000-AddIncidentInvestigationGating.js +27 -0
  503. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785790000000-AddIncidentInvestigationGating.js.map +1 -0
  504. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785800000000-AddRunbookCredential.js +44 -0
  505. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785800000000-AddRunbookCredential.js.map +1 -0
  506. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785810000000-RenameRunbookAgentToRunner.js +88 -0
  507. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785810000000-RenameRunbookAgentToRunner.js.map +1 -0
  508. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785860000000-SessionReplayDefaultToSensitiveInputMasking.js +59 -0
  509. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785860000000-SessionReplayDefaultToSensitiveInputMasking.js.map +1 -0
  510. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785870000000-AddAiCommandRemediation.js +76 -0
  511. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785870000000-AddAiCommandRemediation.js.map +1 -0
  512. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785880000000-AlignRunnerSchemaWithEntities.js +214 -0
  513. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785880000000-AlignRunnerSchemaWithEntities.js.map +1 -0
  514. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785890000000-AddFixVerificationAndAutomaticCodeFixes.js +44 -0
  515. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785890000000-AddFixVerificationAndAutomaticCodeFixes.js.map +1 -0
  516. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +32 -0
  517. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  518. package/build/dist/Server/Services/AIRunService.js +1 -4
  519. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  520. package/build/dist/Server/Services/AIService.js +26 -0
  521. package/build/dist/Server/Services/AIService.js.map +1 -1
  522. package/build/dist/Server/Services/AlertService.js +37 -4
  523. package/build/dist/Server/Services/AlertService.js.map +1 -1
  524. package/build/dist/Server/Services/AlertSeverityService.js +24 -11
  525. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  526. package/build/dist/Server/Services/AlertStateService.js +24 -11
  527. package/build/dist/Server/Services/AlertStateService.js.map +1 -1
  528. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +917 -0
  529. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -0
  530. package/build/dist/Server/Services/{RunbookAgentOwnerTeamService.js → AutoRemediationRuleService.js} +2 -2
  531. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -0
  532. package/build/dist/Server/Services/AutoRemediationSuggestionService.js +127 -0
  533. package/build/dist/Server/Services/AutoRemediationSuggestionService.js.map +1 -0
  534. package/build/dist/Server/Services/BillingService.js +45 -1
  535. package/build/dist/Server/Services/BillingService.js.map +1 -1
  536. package/build/dist/Server/Services/DatabaseService.js +72 -4
  537. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  538. package/build/dist/Server/Services/IncidentService.js +52 -4
  539. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  540. package/build/dist/Server/Services/IncidentSeverityService.js +24 -11
  541. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  542. package/build/dist/Server/Services/IncidentStateService.js +24 -11
  543. package/build/dist/Server/Services/IncidentStateService.js.map +1 -1
  544. package/build/dist/Server/Services/Index.js +18 -0
  545. package/build/dist/Server/Services/Index.js.map +1 -1
  546. package/build/dist/Server/Services/MonitorService.js +24 -0
  547. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  548. package/build/dist/Server/Services/MonitorStatusService.js +25 -11
  549. package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
  550. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +12 -11
  551. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  552. package/build/dist/Server/Services/NetworkDeviceLabelRuleService.js +43 -0
  553. package/build/dist/Server/Services/NetworkDeviceLabelRuleService.js.map +1 -1
  554. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +12 -11
  555. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  556. package/build/dist/Server/Services/NetworkDeviceOwnerRuleService.js +41 -0
  557. package/build/dist/Server/Services/NetworkDeviceOwnerRuleService.js.map +1 -1
  558. package/build/dist/Server/Services/NetworkDeviceService.js +129 -20
  559. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  560. package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js +67 -10
  561. package/build/dist/Server/Services/NetworkSiteAssignmentRuleService.js.map +1 -1
  562. package/build/dist/Server/Services/NetworkSiteService.js +19 -8
  563. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  564. package/build/dist/Server/Services/ProjectService.js +43 -0
  565. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  566. package/build/dist/Server/Services/RumApplicationService.js +202 -0
  567. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  568. package/build/dist/Server/Services/RumSessionChunkService.js +9 -0
  569. package/build/dist/Server/Services/RumSessionChunkService.js.map +1 -0
  570. package/build/dist/Server/Services/RumSessionErasureRequestService.js +190 -0
  571. package/build/dist/Server/Services/RumSessionErasureRequestService.js.map +1 -0
  572. package/build/dist/Server/Services/RumSessionPinService.js +208 -0
  573. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -0
  574. package/build/dist/Server/Services/RumSessionReplayViewService.js +180 -0
  575. package/build/dist/Server/Services/RumSessionReplayViewService.js.map +1 -0
  576. package/build/dist/Server/Services/RumSessionService.js +102 -0
  577. package/build/dist/Server/Services/RumSessionService.js.map +1 -0
  578. package/build/dist/Server/Services/RunbookCredentialService.js +67 -0
  579. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -0
  580. package/build/dist/Server/Services/{RunbookAgentJobService.js → RunnerJobService.js} +158 -46
  581. package/build/dist/Server/Services/RunnerJobService.js.map +1 -0
  582. package/build/dist/Server/Services/{RunbookAgentOwnerUserService.js → RunnerOwnerTeamService.js} +2 -2
  583. package/build/dist/Server/Services/RunnerOwnerTeamService.js.map +1 -0
  584. package/build/dist/Server/Services/RunnerOwnerUserService.js +9 -0
  585. package/build/dist/Server/Services/RunnerOwnerUserService.js.map +1 -0
  586. package/build/dist/Server/Services/RunnerService.js +168 -0
  587. package/build/dist/Server/Services/RunnerService.js.map +1 -0
  588. package/build/dist/Server/Services/ScheduledMaintenanceStateService.js +24 -11
  589. package/build/dist/Server/Services/ScheduledMaintenanceStateService.js.map +1 -1
  590. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +15 -1
  591. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  592. package/build/dist/Server/Services/ServiceService.js +8 -2
  593. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  594. package/build/dist/Server/Services/StatusPageService.js +173 -16
  595. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  596. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
  597. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  598. package/build/dist/Server/Services/TelemetryExceptionService.js +3 -16
  599. package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
  600. package/build/dist/Server/Services/TelemetryUsageBillingService.js +31 -3
  601. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  602. package/build/dist/Server/Services/TraceAggregationService.js +106 -2
  603. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  604. package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +16 -0
  605. package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
  606. package/build/dist/Server/Types/Billing/MeteredPlan/ServerMeteredPlan.js +9 -0
  607. package/build/dist/Server/Types/Billing/MeteredPlan/ServerMeteredPlan.js.map +1 -1
  608. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +38 -6
  609. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  610. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +104 -0
  611. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -0
  612. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.js +54 -0
  613. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAvailability.js.map +1 -0
  614. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +11 -7
  615. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  616. package/build/dist/Server/Utils/AI/CodeFix/CodeFixRunQueue.js +3 -4
  617. package/build/dist/Server/Utils/AI/CodeFix/CodeFixRunQueue.js.map +1 -1
  618. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +724 -0
  619. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -0
  620. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +668 -0
  621. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -0
  622. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +657 -0
  623. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -0
  624. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +52 -7
  625. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  626. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +53 -5
  627. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  628. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +25 -6
  629. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  630. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +131 -17
  631. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  632. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +200 -5
  633. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  634. package/build/dist/Server/Utils/AI/SRE/Insights/Triage.js +2 -7
  635. package/build/dist/Server/Utils/AI/SRE/Insights/Triage.js.map +1 -1
  636. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +4 -17
  637. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  638. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +203 -19
  639. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  640. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +89 -0
  641. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -0
  642. package/build/dist/Server/Utils/AI/SRE/RemediationHandoff.js +82 -0
  643. package/build/dist/Server/Utils/AI/SRE/RemediationHandoff.js.map +1 -0
  644. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +4 -19
  645. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  646. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +2 -2
  647. package/build/dist/Server/Utils/Alert/AlertPrivacyFilter.js +36 -0
  648. package/build/dist/Server/Utils/Alert/AlertPrivacyFilter.js.map +1 -1
  649. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +498 -0
  650. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -0
  651. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +492 -0
  652. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -0
  653. package/build/dist/Server/Utils/CorsOptions.js +47 -0
  654. package/build/dist/Server/Utils/CorsOptions.js.map +1 -0
  655. package/build/dist/Server/Utils/Database/RelationIdUtil.js +51 -0
  656. package/build/dist/Server/Utils/Database/RelationIdUtil.js.map +1 -0
  657. package/build/dist/Server/Utils/Incident/IncidentPrivacyFilter.js +38 -0
  658. package/build/dist/Server/Utils/Incident/IncidentPrivacyFilter.js.map +1 -1
  659. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +194 -0
  660. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +1 -0
  661. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +292 -0
  662. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -0
  663. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +9 -0
  664. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  665. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +9 -0
  666. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  667. package/build/dist/Server/Utils/Monitor/MonitorResource.js +16 -0
  668. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  669. package/build/dist/Server/Utils/Monitor/MonitorSummaryCapture.js +92 -0
  670. package/build/dist/Server/Utils/Monitor/MonitorSummaryCapture.js.map +1 -0
  671. package/build/dist/Server/Utils/NetworkDevice/RulePatternValidator.js +27 -0
  672. package/build/dist/Server/Utils/NetworkDevice/RulePatternValidator.js.map +1 -0
  673. package/build/dist/Server/Utils/OpenAPI.js +2 -1
  674. package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
  675. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +67 -0
  676. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -0
  677. package/build/dist/Server/Utils/SessionReplay/SessionReplayErasureTombstone.js +98 -0
  678. package/build/dist/Server/Utils/SessionReplay/SessionReplayErasureTombstone.js.map +1 -0
  679. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +378 -0
  680. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -0
  681. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +137 -0
  682. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -0
  683. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +998 -0
  684. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -0
  685. package/build/dist/Server/Utils/SessionReplay/SessionReplayRetention.js +59 -0
  686. package/build/dist/Server/Utils/SessionReplay/SessionReplayRetention.js.map +1 -0
  687. package/build/dist/Server/Utils/SessionReplay/SessionReplayTargeting.js +98 -0
  688. package/build/dist/Server/Utils/SessionReplay/SessionReplayTargeting.js.map +1 -0
  689. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +69 -0
  690. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -0
  691. package/build/dist/Server/Utils/StartServer.js +40 -3
  692. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  693. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +27 -6
  694. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -1
  695. package/build/dist/Types/AI/AIRunStatus.js +17 -5
  696. package/build/dist/Types/AI/AIRunStatus.js.map +1 -1
  697. package/build/dist/Types/AI/AIRunType.js +18 -0
  698. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  699. package/build/dist/Types/AI/FixVerificationStatus.js +22 -0
  700. package/build/dist/Types/AI/FixVerificationStatus.js.map +1 -0
  701. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +16 -0
  702. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
  703. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +192 -0
  704. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -0
  705. package/build/dist/Types/AutoRemediation/AutoRemediationExecutionMode.js +18 -0
  706. package/build/dist/Types/AutoRemediation/AutoRemediationExecutionMode.js.map +1 -0
  707. package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionStatus.js +31 -0
  708. package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionStatus.js.map +1 -0
  709. package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionType.js +18 -0
  710. package/build/dist/Types/AutoRemediation/AutoRemediationSuggestionType.js.map +1 -0
  711. package/build/dist/Types/AutoRemediation/AutoRemediationTriggerEntity.js +12 -0
  712. package/build/dist/Types/AutoRemediation/AutoRemediationTriggerEntity.js.map +1 -0
  713. package/build/dist/Types/AutoRemediation/AutoRemediationVerificationStatus.js +22 -0
  714. package/build/dist/Types/AutoRemediation/AutoRemediationVerificationStatus.js.map +1 -0
  715. package/build/dist/Types/CustomField/CustomFieldDropdownOption.js +110 -0
  716. package/build/dist/Types/CustomField/CustomFieldDropdownOption.js.map +1 -0
  717. package/build/dist/Types/Dashboard/DashboardComponentType.js +2 -0
  718. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  719. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +4 -0
  720. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  721. package/build/dist/Types/Dashboard/DashboardComponents/DashboardClockComponent.js +19 -0
  722. package/build/dist/Types/Dashboard/DashboardComponents/DashboardClockComponent.js.map +1 -0
  723. package/build/dist/Types/Dashboard/DashboardComponents/DashboardHtmlComponent.js +2 -0
  724. package/build/dist/Types/Dashboard/DashboardComponents/DashboardHtmlComponent.js.map +1 -0
  725. package/build/dist/Types/Dashboard/DashboardTemplates.js +487 -0
  726. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  727. package/build/dist/Types/Date.js +6 -0
  728. package/build/dist/Types/Date.js.map +1 -1
  729. package/build/dist/Types/Email/EmailTemplateType.js +2 -0
  730. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  731. package/build/dist/Types/Kubernetes/KubernetesRightSizing.js +229 -0
  732. package/build/dist/Types/Kubernetes/KubernetesRightSizing.js.map +1 -0
  733. package/build/dist/Types/MeteredPlan/ProductType.js +1 -0
  734. package/build/dist/Types/MeteredPlan/ProductType.js.map +1 -1
  735. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +12 -2
  736. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
  737. package/build/dist/Types/Monitor/MonitorSteps.js +5 -0
  738. package/build/dist/Types/Monitor/MonitorSteps.js.map +1 -1
  739. package/build/dist/Types/Monitor/MonitorSummarySnapshot.js +13 -0
  740. package/build/dist/Types/Monitor/MonitorSummarySnapshot.js.map +1 -0
  741. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +40 -8
  742. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  743. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +4 -3
  744. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  745. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +50 -10
  746. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  747. package/build/dist/Types/Monitor/RumAlertTemplates.js +297 -0
  748. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -0
  749. package/build/dist/Types/Permission.js +184 -24
  750. package/build/dist/Types/Permission.js.map +1 -1
  751. package/build/dist/Types/Rum/SessionReplay.js +155 -0
  752. package/build/dist/Types/Rum/SessionReplay.js.map +1 -0
  753. package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js +26 -0
  754. package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js.map +1 -0
  755. package/build/dist/Types/Rum/SessionReplayConsentMode.js +27 -0
  756. package/build/dist/Types/Rum/SessionReplayConsentMode.js.map +1 -0
  757. package/build/dist/Types/Rum/SessionReplayMaskingMode.js +79 -0
  758. package/build/dist/Types/Rum/SessionReplayMaskingMode.js.map +1 -0
  759. package/build/dist/Types/Rum/SessionReplayTriggerReason.js +28 -0
  760. package/build/dist/Types/Rum/SessionReplayTriggerReason.js.map +1 -0
  761. package/build/dist/Types/Runbook/RunbookCredentialType.js +7 -0
  762. package/build/dist/Types/Runbook/RunbookCredentialType.js.map +1 -0
  763. package/build/dist/Types/Runbook/RunbookStep.js +20 -1
  764. package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
  765. package/build/dist/Types/Runbook/RunbookStepType.js +24 -0
  766. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  767. package/build/dist/Types/Runbook/RunnerJobOrigin.js +21 -0
  768. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -0
  769. package/build/dist/Types/Runbook/RunnerJobStatus.js +22 -0
  770. package/build/dist/Types/Runbook/RunnerJobStatus.js.map +1 -0
  771. package/build/dist/Types/StatusPage/StatusPageReport.js +22 -0
  772. package/build/dist/Types/StatusPage/StatusPageReport.js.map +1 -0
  773. package/build/dist/Types/Telemetry/TelemetryRetentionConfig.js.map +1 -1
  774. package/build/dist/Types/Text.js +29 -0
  775. package/build/dist/Types/Text.js.map +1 -1
  776. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +143 -28
  777. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  778. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +82 -9
  779. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
  780. package/build/dist/UI/Components/BulkUpdate/BulkOwnerActions.js +11 -0
  781. package/build/dist/UI/Components/BulkUpdate/BulkOwnerActions.js.map +1 -1
  782. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +11 -13
  783. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  784. package/build/dist/UI/Components/CustomFields/DropdownOptionsInput.js +76 -45
  785. package/build/dist/UI/Components/CustomFields/DropdownOptionsInput.js.map +1 -1
  786. package/build/dist/UI/Components/Detail/Detail.js +3 -8
  787. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  788. package/build/dist/UI/Components/Dropdown/Dropdown.js +2 -5
  789. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  790. package/build/dist/UI/Components/Dropdown/DropdownMenuZIndex.js +7 -0
  791. package/build/dist/UI/Components/Dropdown/DropdownMenuZIndex.js.map +1 -0
  792. package/build/dist/UI/Components/Dropdown/DropdownValueBadge.js +32 -0
  793. package/build/dist/UI/Components/Dropdown/DropdownValueBadge.js.map +1 -0
  794. package/build/dist/UI/Components/Filters/OperatorSelector.js +210 -20
  795. package/build/dist/UI/Components/Filters/OperatorSelector.js.map +1 -1
  796. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +26 -10
  797. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
  798. package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -13
  799. package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
  800. package/build/dist/UI/Components/Forms/Validation.js +9 -5
  801. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  802. package/build/dist/UI/Components/List/List.js +1 -1
  803. package/build/dist/UI/Components/List/List.js.map +1 -1
  804. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +21 -19
  805. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  806. package/build/dist/UI/Components/Pagination/Pagination.js +14 -3
  807. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  808. package/build/dist/UI/Components/Table/Table.js +1 -1
  809. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  810. package/build/dist/UI/Types/UseAnchoredFieldPopup.js +197 -0
  811. package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -0
  812. package/build/dist/UI/Utils/Dropdown.js +18 -0
  813. package/build/dist/UI/Utils/Dropdown.js.map +1 -1
  814. package/build/dist/UI/Utils/Timezone.js +13 -2
  815. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  816. package/build/dist/UI/Utils/UseClockTick.js +41 -0
  817. package/build/dist/UI/Utils/UseClockTick.js.map +1 -0
  818. package/build/dist/Utils/AiRemediation/CommandPolicy.js +303 -0
  819. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -0
  820. package/build/dist/Utils/Dashboard/ClockWidgetFormat.js +232 -0
  821. package/build/dist/Utils/Dashboard/ClockWidgetFormat.js.map +1 -0
  822. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +146 -0
  823. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -0
  824. package/build/dist/Utils/Dashboard/Components/DashboardHtmlComponent.js +133 -0
  825. package/build/dist/Utils/Dashboard/Components/DashboardHtmlComponent.js.map +1 -0
  826. package/build/dist/Utils/Dashboard/Components/DashboardValueComponent.js +10 -1
  827. package/build/dist/Utils/Dashboard/Components/DashboardValueComponent.js.map +1 -1
  828. package/build/dist/Utils/Dashboard/Components/Index.js +8 -0
  829. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  830. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +198 -0
  831. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -0
  832. package/build/dist/Utils/Dashboard/ValueWidgetLayout.js +276 -0
  833. package/build/dist/Utils/Dashboard/ValueWidgetLayout.js.map +1 -0
  834. package/build/dist/Utils/Metrics/MetricSeriesScope.js +203 -0
  835. package/build/dist/Utils/Metrics/MetricSeriesScope.js.map +1 -0
  836. package/build/dist/Utils/Monitor/MonitorSummarySnapshotUtil.js +382 -0
  837. package/build/dist/Utils/Monitor/MonitorSummarySnapshotUtil.js.map +1 -0
  838. package/build/dist/Utils/NetworkSite/CidrMatchUtil.js +13 -7
  839. package/build/dist/Utils/NetworkSite/CidrMatchUtil.js.map +1 -1
  840. package/build/dist/Utils/Rules/RulePatternMatchUtil.js +87 -0
  841. package/build/dist/Utils/Rules/RulePatternMatchUtil.js.map +1 -0
  842. package/build/dist/Utils/Rum/ChunkMath.js +147 -0
  843. package/build/dist/Utils/Rum/ChunkMath.js.map +1 -0
  844. package/build/dist/Utils/Rum/Masking.js +143 -0
  845. package/build/dist/Utils/Rum/Masking.js.map +1 -0
  846. package/build/dist/Utils/Rum/SessionIdentity.js +161 -0
  847. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -0
  848. package/build/dist/Utils/Rum/SessionSampling.js +91 -0
  849. package/build/dist/Utils/Rum/SessionSampling.js.map +1 -0
  850. package/build/dist/Utils/Rum/UrlScrubber.js +144 -0
  851. package/build/dist/Utils/Rum/UrlScrubber.js.map +1 -0
  852. package/build/dist/Utils/Schema/ModelSchema.js +28 -3
  853. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
  854. package/build/dist/Utils/StatusPage/Report.js +194 -0
  855. package/build/dist/Utils/StatusPage/Report.js.map +1 -0
  856. package/build/dist/Utils/ValueFormatter.js +239 -46
  857. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  858. package/jest.config.json +3 -0
  859. package/package.json +1 -1
  860. package/tsconfig.json +19 -1
  861. package/Server/Services/RunbookAgentService.ts +0 -93
  862. package/build/dist/Models/DatabaseModels/RunbookAgent.js.map +0 -1
  863. package/build/dist/Models/DatabaseModels/RunbookAgentJob.js.map +0 -1
  864. package/build/dist/Models/DatabaseModels/RunbookAgentOwnerTeam.js.map +0 -1
  865. package/build/dist/Models/DatabaseModels/RunbookAgentOwnerUser.js.map +0 -1
  866. package/build/dist/Server/Services/RunbookAgentJobService.js.map +0 -1
  867. package/build/dist/Server/Services/RunbookAgentOwnerTeamService.js.map +0 -1
  868. package/build/dist/Server/Services/RunbookAgentOwnerUserService.js.map +0 -1
  869. package/build/dist/Server/Services/RunbookAgentService.js +0 -91
  870. package/build/dist/Server/Services/RunbookAgentService.js.map +0 -1
  871. package/build/dist/Types/Runbook/RunbookAgentJobStatus.js +0 -22
  872. package/build/dist/Types/Runbook/RunbookAgentJobStatus.js.map +0 -1
@@ -0,0 +1,1502 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+ import "@testing-library/jest-dom";
10
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
11
+ import * as React from "react";
12
+ import { act, FunctionComponent, ReactElement } from "react";
13
+ import getJestMockFunction, { MockFunction } from "../../MockType";
14
+
15
+ /*
16
+ * useBulkLabelActions is the single mutator behind "Add Labels" and "Remove
17
+ * Labels" on every resource list in the product, and it had no test coverage at
18
+ * all before this change. Most of what follows is therefore baseline coverage
19
+ * of a previously untested shared hook - the merge/subtract arithmetic, the
20
+ * per-item failure isolation, and the progress accounting the bulk progress bar
21
+ * is drawn from - not coverage of the SLO tables that prompted the work. Its
22
+ * failure mode is silent: each write is a read-modify-write of the item's own
23
+ * label array, so anything that goes wrong in the middle overwrites labels the
24
+ * user never selected and still reports the item as a success.
25
+ *
26
+ * The tests that pin the fixes actually made in this change are:
27
+ * - "does not fetch labels at all when there is no current project"
28
+ * (projectId: null went out as a filter matching nothing)
29
+ * - "drops a dropdown option that came back without an id before merging"
30
+ * (a blank id in the labels array failed the whole item)
31
+ * - "reads the item's current label ids before merging" (the select now asks
32
+ * for _id, which the guard below depends on)
33
+ * - "fails an item whose current read carries no id ..." and
34
+ * "... comes back as null ..." (a deleted or filtered row answers 200 with
35
+ * {}, which hydrates truthy, so add mode used to clobber its whole label
36
+ * set)
37
+ * - "starts nothing and reports nothing when every submitted label id is
38
+ * blank" (that path used to report every item as a success)
39
+ * - "keeps the newest selection's remove options when an older fetch settles
40
+ * last" (the stale response used to win)
41
+ * - the two "opening X closes an open Y modal" tests (stacked modals share
42
+ * one bulkActionProps and the loser goes silently inert)
43
+ */
44
+
45
+ /*
46
+ * react-i18next is not initialized in the test environment. Mock the hook so
47
+ * translate helpers echo their input and the component renders synchronously.
48
+ */
49
+ jest.mock("react-i18next", () => {
50
+ return {
51
+ useTranslation: () => {
52
+ return {
53
+ t: (key: string, opts?: { defaultValue?: string }): string => {
54
+ return opts?.defaultValue ?? key;
55
+ },
56
+ };
57
+ },
58
+ };
59
+ });
60
+
61
+ const getListMock: MockFunction = getJestMockFunction();
62
+ const getItemMock: MockFunction = getJestMockFunction();
63
+ const updateByIdMock: MockFunction = getJestMockFunction();
64
+
65
+ /*
66
+ * The arrow wrappers are load bearing: jest.mock is hoisted above the compiled
67
+ * requires, so naming the mocks directly would capture them before their
68
+ * initializers have run.
69
+ */
70
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
71
+ return {
72
+ __esModule: true,
73
+ default: {
74
+ getList: (...args: Array<any>) => {
75
+ return getListMock(...args);
76
+ },
77
+ getItem: (...args: Array<any>) => {
78
+ return getItemMock(...args);
79
+ },
80
+ updateById: (...args: Array<any>) => {
81
+ return updateByIdMock(...args);
82
+ },
83
+ },
84
+ };
85
+ });
86
+
87
+ import useBulkLabelActions, {
88
+ BulkLabelActionsResult,
89
+ } from "../../../UI/Components/BulkUpdate/BulkLabelActions";
90
+ import {
91
+ BulkActionButtonSchema,
92
+ BulkActionFailed,
93
+ BulkActionOnClickProps,
94
+ ProgressInfo,
95
+ } from "../../../UI/Components/BulkUpdate/BulkUpdateForm";
96
+ import { ButtonStyleType } from "../../../UI/Components/Button/Button";
97
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
98
+ import Label from "../../../Models/DatabaseModels/Label";
99
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
100
+ import API from "../../../UI/Utils/API/API";
101
+ import Includes from "../../../Types/BaseDatabase/Includes";
102
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
103
+ import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
104
+ import IconProp from "../../../Types/Icon/IconProp";
105
+ import { Red } from "../../../Types/BrandColors";
106
+
107
+ /*
108
+ * These tests drive real Formik + react-select trees behind a modal, so the
109
+ * testing-library default of 1s flakes on a loaded box.
110
+ */
111
+ const WAIT_TIMEOUT: number = 20000;
112
+
113
+ const PROJECT_ID: string = "11111111-1111-4111-8111-111111111111";
114
+ const ITEM_ONE_ID: string = "22222222-2222-4222-8222-222222222221";
115
+ const ITEM_TWO_ID: string = "22222222-2222-4222-8222-222222222222";
116
+ const ITEM_THREE_ID: string = "22222222-2222-4222-8222-222222222223";
117
+ const LABEL_ALPHA_ID: string = "33333333-3333-4333-8333-333333333331";
118
+ const LABEL_BETA_ID: string = "33333333-3333-4333-8333-333333333332";
119
+ const LABEL_GAMMA_ID: string = "33333333-3333-4333-8333-333333333333";
120
+
121
+ type MakeLabelFunction = (id: string, name: string) => Label;
122
+
123
+ const makeLabel: MakeLabelFunction = (id: string, name: string): Label => {
124
+ const label: Label = new Label();
125
+ label._id = id;
126
+ label.name = name;
127
+ return label;
128
+ };
129
+
130
+ type MakeItemFunction = (id: string) => Monitor;
131
+
132
+ const makeItem: MakeItemFunction = (id: string): Monitor => {
133
+ const item: Monitor = new Monitor();
134
+ item._id = id;
135
+ return item;
136
+ };
137
+
138
+ type MakeItemWithLabelsFunction = (id: string, labels: Array<Label>) => Monitor;
139
+
140
+ const makeItemWithLabels: MakeItemWithLabelsFunction = (
141
+ id: string,
142
+ labels: Array<Label>,
143
+ ): Monitor => {
144
+ const item: Monitor = makeItem(id);
145
+ item.labels = labels;
146
+ return item;
147
+ };
148
+
149
+ interface ProgressSnapshot {
150
+ totalIds: Array<string>;
151
+ inProgressIds: Array<string>;
152
+ successIds: Array<string>;
153
+ failedIds: Array<string>;
154
+ failedMessages: Array<string>;
155
+ }
156
+
157
+ type IdsOfFunction = (items: Array<Monitor>) => Array<string>;
158
+
159
+ const idsOf: IdsOfFunction = (items: Array<Monitor>): Array<string> => {
160
+ return items.map((item: Monitor) => {
161
+ return item._id || "";
162
+ });
163
+ };
164
+
165
+ type SnapshotProgressFunction = (
166
+ progressInfo: ProgressInfo<Monitor>,
167
+ ) => ProgressSnapshot;
168
+
169
+ /*
170
+ * The hook allocates the four progress arrays once and hands the same
171
+ * references to every onProgressInfo call, so anything held by reference
172
+ * mutates retroactively into the final state. Copy out of them on arrival or
173
+ * the per-step assertions below are meaningless.
174
+ */
175
+ const snapshotProgress: SnapshotProgressFunction = (
176
+ progressInfo: ProgressInfo<Monitor>,
177
+ ): ProgressSnapshot => {
178
+ return {
179
+ totalIds: idsOf(progressInfo.totalItems),
180
+ inProgressIds: idsOf(progressInfo.inProgressItems),
181
+ successIds: idsOf(progressInfo.successItems),
182
+ failedIds: idsOf(
183
+ progressInfo.failed.map((failure: BulkActionFailed<Monitor>) => {
184
+ return failure.item;
185
+ }),
186
+ ),
187
+ failedMessages: progressInfo.failed.map(
188
+ (failure: BulkActionFailed<Monitor>) => {
189
+ return String(failure.failedMessage);
190
+ },
191
+ ),
192
+ };
193
+ };
194
+
195
+ const onProgressInfoMock: MockFunction = getJestMockFunction();
196
+ const onBulkActionStartMock: MockFunction = getJestMockFunction();
197
+ const onBulkActionEndMock: MockFunction = getJestMockFunction();
198
+
199
+ let progressSnapshots: Array<ProgressSnapshot> = [];
200
+ let callOrder: Array<string> = [];
201
+ let capturedActions: Array<BulkActionButtonSchema<Monitor>> = [];
202
+ let capturedModals: ReactElement | null = null;
203
+
204
+ type MakeActionPropsFunction = (
205
+ items: Array<Monitor>,
206
+ ) => BulkActionOnClickProps<Monitor>;
207
+
208
+ const makeActionProps: MakeActionPropsFunction = (
209
+ items: Array<Monitor>,
210
+ ): BulkActionOnClickProps<Monitor> => {
211
+ return {
212
+ items: items,
213
+ onProgressInfo:
214
+ onProgressInfoMock as unknown as BulkActionOnClickProps<Monitor>["onProgressInfo"],
215
+ onBulkActionStart: onBulkActionStartMock as unknown as () => void,
216
+ onBulkActionEnd: onBulkActionEndMock as unknown as () => void,
217
+ };
218
+ };
219
+
220
+ interface HarnessProps {
221
+ items: Array<Monitor>;
222
+ }
223
+
224
+ /*
225
+ * `modals` has to be mounted for any of the modal behavior to exist, which
226
+ * renderHook cannot do, so the hook is driven through a component that renders
227
+ * a plain trigger per action.
228
+ */
229
+ const Harness: FunctionComponent<HarnessProps> = (
230
+ props: HarnessProps,
231
+ ): ReactElement => {
232
+ const { bulkActions, modals }: BulkLabelActionsResult<Monitor> =
233
+ useBulkLabelActions<Monitor>({ modelType: Monitor });
234
+
235
+ capturedActions = bulkActions;
236
+ capturedModals = modals;
237
+
238
+ return (
239
+ <div>
240
+ {bulkActions.map((action: BulkActionButtonSchema<Monitor>) => {
241
+ return (
242
+ <button
243
+ key={action.title}
244
+ type="button"
245
+ onClick={() => {
246
+ void action.onClick(makeActionProps(props.items));
247
+ }}
248
+ >
249
+ {`Trigger ${action.title}`}
250
+ </button>
251
+ );
252
+ })}
253
+ {modals}
254
+ </div>
255
+ );
256
+ };
257
+
258
+ type ClickTriggerFunction = (title: string) => void;
259
+
260
+ const clickTrigger: ClickTriggerFunction = (title: string): void => {
261
+ fireEvent.click(screen.getByText(`Trigger ${title}`));
262
+ };
263
+
264
+ type WaitForModalTitleFunction = (title: string) => Promise<void>;
265
+
266
+ const waitForModalTitle: WaitForModalTitleFunction = async (
267
+ title: string,
268
+ ): Promise<void> => {
269
+ await waitFor(
270
+ () => {
271
+ expect(screen.getByTestId("modal-title")).toHaveTextContent(title);
272
+ },
273
+ { timeout: WAIT_TIMEOUT },
274
+ );
275
+ };
276
+
277
+ type WaitForMountedLabelFetchFunction = () => Promise<void>;
278
+
279
+ /*
280
+ * The mount effect writes state after its fetch settles; waiting for the call
281
+ * keeps that write inside act() instead of leaking into the next test.
282
+ */
283
+ const waitForMountedLabelFetch: WaitForMountedLabelFetchFunction =
284
+ async (): Promise<void> => {
285
+ await waitFor(() => {
286
+ expect(getListMock).toHaveBeenCalledTimes(1);
287
+ });
288
+ };
289
+
290
+ type OpenMenuFunction = () => Promise<Array<HTMLElement>>;
291
+
292
+ /*
293
+ * react-select renders its menu lazily; ArrowDown on the combobox is the only
294
+ * interaction that mounts the option list.
295
+ */
296
+ const openMenu: OpenMenuFunction = async (): Promise<Array<HTMLElement>> => {
297
+ const combobox: HTMLElement = await screen.findByRole("combobox", undefined, {
298
+ timeout: WAIT_TIMEOUT,
299
+ });
300
+ fireEvent.keyDown(combobox, { key: "ArrowDown", code: "ArrowDown" });
301
+ return screen.queryAllByRole("option");
302
+ };
303
+
304
+ type OptionTextsFunction = (options: Array<HTMLElement>) => Array<string>;
305
+
306
+ const optionTexts: OptionTextsFunction = (
307
+ options: Array<HTMLElement>,
308
+ ): Array<string> => {
309
+ return options.map((option: HTMLElement) => {
310
+ return option.textContent || "";
311
+ });
312
+ };
313
+
314
+ type SelectLabelFunction = (name: string) => Promise<void>;
315
+
316
+ const selectLabel: SelectLabelFunction = async (
317
+ name: string,
318
+ ): Promise<void> => {
319
+ const combobox: HTMLElement = await screen.findByRole("combobox", undefined, {
320
+ timeout: WAIT_TIMEOUT,
321
+ });
322
+ fireEvent.keyDown(combobox, { key: "ArrowDown", code: "ArrowDown" });
323
+ fireEvent.click(
324
+ await screen.findByText(name, undefined, { timeout: WAIT_TIMEOUT }),
325
+ );
326
+ };
327
+
328
+ type SubmitModalFunction = () => void;
329
+
330
+ const submitModal: SubmitModalFunction = (): void => {
331
+ fireEvent.click(screen.getByTestId("modal-footer-submit-button"));
332
+ };
333
+
334
+ interface LabelFormData {
335
+ labelIds: Array<string>;
336
+ }
337
+
338
+ type ModalSubmitFunction = (formData: LabelFormData) => Promise<void>;
339
+
340
+ type GetModalSubmitFunction = (title: string) => ModalSubmitFunction;
341
+
342
+ /*
343
+ * react-select cannot deliver an all-blank selection through the UI: the form's
344
+ * required check rejects a lone blank-valued option (`[""].toString()` is "")
345
+ * and hideSelectedOptions hides every other option sharing that same blank
346
+ * value the moment one is picked. The hook's contract with BasicFormModal is
347
+ * the onSubmit prop, so that boundary is driven directly instead.
348
+ */
349
+ const getModalSubmit: GetModalSubmitFunction = (
350
+ title: string,
351
+ ): ModalSubmitFunction => {
352
+ const modal: any = React.Children.toArray(
353
+ (capturedModals as any).props.children,
354
+ ).find((child: any) => {
355
+ return child?.props?.title === title;
356
+ });
357
+
358
+ if (!modal) {
359
+ throw new Error(`No "${title}" modal is mounted.`);
360
+ }
361
+
362
+ return modal.props.onSubmit as ModalSubmitFunction;
363
+ };
364
+
365
+ interface DeferredListResult {
366
+ promise: Promise<any>;
367
+ resolve: (value: any) => void;
368
+ }
369
+
370
+ type CreateDeferredListResultFunction = () => DeferredListResult;
371
+
372
+ const createDeferredListResult: CreateDeferredListResultFunction =
373
+ (): DeferredListResult => {
374
+ let settle: (value: any) => void = (): void => {};
375
+
376
+ const promise: Promise<any> = new Promise<any>(
377
+ (resolve: (value: any) => void): void => {
378
+ settle = resolve;
379
+ },
380
+ );
381
+
382
+ return {
383
+ promise: promise,
384
+ resolve: (value: any): void => {
385
+ settle(value);
386
+ },
387
+ };
388
+ };
389
+
390
+ type ActAsyncFunction = (body: () => Promise<void>) => Promise<void>;
391
+
392
+ /*
393
+ * zone.js reschedules the microtask React's act() uses to check that its
394
+ * thenable was awaited, so it runs before `await` gets round to attaching a
395
+ * handler and every plain `await act(...)` is reported as un-awaited. Hooking
396
+ * then() up synchronously answers that check in time; the scope still exits
397
+ * exactly when act says it does.
398
+ */
399
+ const actAsync: ActAsyncFunction = (
400
+ body: () => Promise<void>,
401
+ ): Promise<void> => {
402
+ const scope: PromiseLike<void> = act(body) as unknown as PromiseLike<void>;
403
+
404
+ return new Promise<void>(
405
+ (resolve: () => void, reject: (reason: unknown) => void): void => {
406
+ scope.then(resolve, reject);
407
+ },
408
+ );
409
+ };
410
+
411
+ type GetUpdatedLabelIdsFunction = (callIndex: number) => Array<string>;
412
+
413
+ const getUpdatedLabelIds: GetUpdatedLabelIdsFunction = (
414
+ callIndex: number,
415
+ ): Array<string> => {
416
+ const call: Array<any> = updateByIdMock.mock.calls[callIndex] as Array<any>;
417
+ return (call[0] as any).data.labels as Array<string>;
418
+ };
419
+
420
+ type GetListCallFunction = (callIndex: number) => any;
421
+
422
+ const getListCall: GetListCallFunction = (callIndex: number): any => {
423
+ const call: Array<any> = getListMock.mock.calls[callIndex] as Array<any>;
424
+ return call[0];
425
+ };
426
+
427
+ type WaitForBulkRunToEndFunction = () => Promise<void>;
428
+
429
+ const waitForBulkRunToEnd: WaitForBulkRunToEndFunction =
430
+ async (): Promise<void> => {
431
+ await waitFor(
432
+ () => {
433
+ expect(onBulkActionEndMock).toHaveBeenCalledTimes(1);
434
+ },
435
+ { timeout: WAIT_TIMEOUT },
436
+ );
437
+ };
438
+
439
+ type AddAlphaOverCurrentReadFunction = (
440
+ currentRead: Monitor | null,
441
+ ) => Promise<void>;
442
+
443
+ /*
444
+ * Shared driver for the two "the current read is unusable" cases: one item,
445
+ * one project label, add mode, with getItem answering whatever the caller
446
+ * wants. Assertions stay in the tests so a failure points at the real case.
447
+ */
448
+ const addAlphaOverCurrentRead: AddAlphaOverCurrentReadFunction = async (
449
+ currentRead: Monitor | null,
450
+ ): Promise<void> => {
451
+ getListMock.mockResolvedValue({
452
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
453
+ count: 1,
454
+ });
455
+ getItemMock.mockImplementation((): Promise<Monitor | null> => {
456
+ return Promise.resolve(currentRead);
457
+ });
458
+
459
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
460
+
461
+ await waitForMountedLabelFetch();
462
+
463
+ clickTrigger("Add Labels");
464
+ await waitForModalTitle("Add Labels");
465
+ await selectLabel("Alpha");
466
+ submitModal();
467
+
468
+ await waitForBulkRunToEnd();
469
+ };
470
+
471
+ /*
472
+ * These mount the real Modal, Formik and react-select trees, so a cold
473
+ * ts-jest transform of that import graph can push individual tests past
474
+ * jest's 5s default - which CI, always starting cold, would report as a
475
+ * red build with no assertion having failed. The slowest test here runs
476
+ * in ~1.3s warm; 30s is the same ceiling App/jest.config.json already
477
+ * sets for its whole suite.
478
+ */
479
+ jest.setTimeout(30000);
480
+
481
+ describe("useBulkLabelActions", () => {
482
+ beforeEach(() => {
483
+ /*
484
+ * resetAllMocks rather than clearAllMocks: implementations set by one test
485
+ * would otherwise silently answer another test's requests.
486
+ */
487
+ jest.resetAllMocks();
488
+
489
+ progressSnapshots = [];
490
+ callOrder = [];
491
+ capturedActions = [];
492
+ capturedModals = null;
493
+
494
+ onProgressInfoMock.mockImplementation((progressInfo: any) => {
495
+ callOrder.push("progress");
496
+ progressSnapshots.push(
497
+ snapshotProgress(progressInfo as ProgressInfo<Monitor>),
498
+ );
499
+ });
500
+ onBulkActionStartMock.mockImplementation(() => {
501
+ callOrder.push("start");
502
+ });
503
+ onBulkActionEndMock.mockImplementation(() => {
504
+ callOrder.push("end");
505
+ });
506
+
507
+ getListMock.mockResolvedValue({ data: [], count: 0, skip: 0, limit: 0 });
508
+ updateByIdMock.mockResolvedValue({});
509
+
510
+ window.localStorage.clear();
511
+ window.sessionStorage.clear();
512
+ // ProjectUtil.getCurrentProjectId() reads pathname.split("/")[2].
513
+ window.history.replaceState({}, "", `/dashboard/${PROJECT_ID}/monitors`);
514
+ });
515
+
516
+ afterEach(() => {
517
+ jest.restoreAllMocks();
518
+ });
519
+
520
+ test("exposes Add Labels then Remove Labels, in that order", async () => {
521
+ render(<Harness items={[]} />);
522
+
523
+ await waitForMountedLabelFetch();
524
+
525
+ /*
526
+ * Consumers spread these into ModelTable's button list and BulkUpdateForm
527
+ * renders them in array order, so the order is part of the contract.
528
+ */
529
+ expect(capturedActions).toHaveLength(2);
530
+ expect(capturedActions[0]!.title).toBe("Add Labels");
531
+ expect(capturedActions[1]!.title).toBe("Remove Labels");
532
+ });
533
+
534
+ test("keeps both actions in the non-destructive button group", async () => {
535
+ render(<Harness items={[]} />);
536
+
537
+ await waitForMountedLabelFetch();
538
+
539
+ expect(capturedActions[0]!.icon).toBe(IconProp.Label);
540
+ expect(capturedActions[0]!.buttonStyleType).toBe(ButtonStyleType.NORMAL);
541
+ expect(capturedActions[1]!.icon).toBe(IconProp.Close);
542
+
543
+ /*
544
+ * Remove Labels borrows the Close icon but must not borrow a danger style:
545
+ * BulkUpdateForm splits danger buttons into a separate red menu group
546
+ * behind a divider, which is for irreversible actions.
547
+ */
548
+ expect(capturedActions[1]!.buttonStyleType).toBe(ButtonStyleType.NORMAL);
549
+ });
550
+
551
+ test("leaves the click contract unconditional on both actions", async () => {
552
+ render(<Harness items={[]} />);
553
+
554
+ await waitForMountedLabelFetch();
555
+
556
+ expect(capturedActions).toHaveLength(2);
557
+
558
+ for (const action of capturedActions) {
559
+ /*
560
+ * A confirmMessage would make BulkUpdateForm open a ConfirmModal instead
561
+ * of calling onClick, and isVisible/disabled can drop the click entirely
562
+ * - each of those quietly changes how the modal gets opened.
563
+ */
564
+ expect(action.confirmMessage).toBeUndefined();
565
+ expect(action.confirmTitle).toBeUndefined();
566
+ expect(action.isVisible).toBeUndefined();
567
+ expect(action.disabled).toBeUndefined();
568
+ }
569
+ });
570
+
571
+ test("renders no modal until an action is clicked", async () => {
572
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
573
+
574
+ await waitForMountedLabelFetch();
575
+
576
+ expect(screen.queryByTestId("modal")).toBeNull();
577
+ });
578
+
579
+ test("fetches the project's labels once on mount with the full query", async () => {
580
+ render(<Harness items={[]} />);
581
+
582
+ await waitForMountedLabelFetch();
583
+
584
+ const call: any = getListCall(0);
585
+
586
+ expect(call.modelType).toBe(Label);
587
+ expect(call.query.projectId.toString()).toBe(PROJECT_ID);
588
+ expect(call.limit).toBe(LIMIT_PER_PROJECT);
589
+ expect(call.skip).toBe(0);
590
+ /*
591
+ * `color` has to be selected or the add dropdown loses the colour swatch;
592
+ * `labels` must not be, since Label has no labels of its own.
593
+ */
594
+ expect(call.select).toEqual({ _id: true, name: true, color: true });
595
+ expect(call.sort).toEqual({ name: SortOrder.Ascending });
596
+ });
597
+
598
+ test("does not fetch labels at all when there is no current project", async () => {
599
+ window.history.replaceState({}, "", "/dashboard");
600
+
601
+ render(<Harness items={[]} />);
602
+
603
+ await waitFor(() => {
604
+ expect(screen.getByText("Trigger Add Labels")).toBeInTheDocument();
605
+ });
606
+
607
+ /*
608
+ * The query used to go out as projectId: null, which matches nothing, so
609
+ * the user got an empty dropdown after a pointless round trip.
610
+ */
611
+ expect(getListMock).not.toHaveBeenCalled();
612
+ });
613
+
614
+ test("leaves the add dropdown empty when the label fetch fails", async () => {
615
+ /*
616
+ * Throwing from inside the call rather than handing back a promise that is
617
+ * already rejected (mockRejectedValue, or an equivalent Promise.reject):
618
+ * zone.js sees the rejection before the hook's catch is attached and
619
+ * prints the whole stack as a console error on every run.
620
+ */
621
+ getListMock.mockImplementation(async (): Promise<never> => {
622
+ throw new Error("network down");
623
+ });
624
+
625
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
626
+
627
+ await waitForMountedLabelFetch();
628
+
629
+ clickTrigger("Add Labels");
630
+ await waitForModalTitle("Add Labels");
631
+
632
+ const options: Array<HTMLElement> = await openMenu();
633
+
634
+ expect(options).toHaveLength(0);
635
+ });
636
+
637
+ test("keeps Remove Labels working when the project label fetch failed", async () => {
638
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
639
+ if (data.modelType === Label) {
640
+ throw new Error("network down");
641
+ }
642
+
643
+ return {
644
+ data: [
645
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_BETA_ID, "Beta")]),
646
+ ],
647
+ count: 1,
648
+ };
649
+ });
650
+
651
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
652
+
653
+ clickTrigger("Remove Labels");
654
+ await waitForModalTitle("Remove Labels");
655
+
656
+ /*
657
+ * Remove reads only from the per-item fetch, so the mount failure that
658
+ * empties the add dropdown must not degrade it.
659
+ */
660
+ const options: Array<HTMLElement> = await openMenu();
661
+
662
+ expect(options).toHaveLength(1);
663
+ expect(options[0]!).toHaveTextContent("Beta");
664
+ });
665
+
666
+ test("adds the picked label without clobbering labels the user did not pick", async () => {
667
+ getListMock.mockResolvedValue({
668
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
669
+ count: 1,
670
+ });
671
+ getItemMock.mockResolvedValue(
672
+ makeItemWithLabels(ITEM_ONE_ID, [
673
+ makeLabel(LABEL_BETA_ID, "Beta"),
674
+ makeLabel(LABEL_GAMMA_ID, "Gamma"),
675
+ ]),
676
+ );
677
+
678
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
679
+
680
+ await waitForMountedLabelFetch();
681
+
682
+ clickTrigger("Add Labels");
683
+ await waitForModalTitle("Add Labels");
684
+ await selectLabel("Alpha");
685
+ submitModal();
686
+
687
+ await waitForBulkRunToEnd();
688
+
689
+ expect(getUpdatedLabelIds(0)).toEqual([
690
+ LABEL_BETA_ID,
691
+ LABEL_GAMMA_ID,
692
+ LABEL_ALPHA_ID,
693
+ ]);
694
+ });
695
+
696
+ test("de-dupes a picked label the item already carries", async () => {
697
+ getListMock.mockResolvedValue({
698
+ data: [
699
+ makeLabel(LABEL_ALPHA_ID, "Alpha"),
700
+ makeLabel(LABEL_BETA_ID, "Beta"),
701
+ ],
702
+ count: 2,
703
+ });
704
+ getItemMock.mockResolvedValue(
705
+ makeItemWithLabels(ITEM_ONE_ID, [
706
+ makeLabel(LABEL_ALPHA_ID, "Alpha"),
707
+ makeLabel(LABEL_GAMMA_ID, "Gamma"),
708
+ ]),
709
+ );
710
+
711
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
712
+
713
+ await waitForMountedLabelFetch();
714
+
715
+ clickTrigger("Add Labels");
716
+ await waitForModalTitle("Add Labels");
717
+ await selectLabel("Alpha");
718
+ await selectLabel("Beta");
719
+ submitModal();
720
+
721
+ await waitForBulkRunToEnd();
722
+
723
+ // Alpha was on the item and was picked again; it must land once.
724
+ expect(getUpdatedLabelIds(0)).toEqual([
725
+ LABEL_ALPHA_ID,
726
+ LABEL_GAMMA_ID,
727
+ LABEL_BETA_ID,
728
+ ]);
729
+ });
730
+
731
+ test("reads the item's current label ids before merging", async () => {
732
+ getListMock.mockResolvedValue({
733
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
734
+ count: 1,
735
+ });
736
+ getItemMock.mockResolvedValue(makeItemWithLabels(ITEM_ONE_ID, []));
737
+
738
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
739
+
740
+ await waitForMountedLabelFetch();
741
+
742
+ clickTrigger("Add Labels");
743
+ await waitForModalTitle("Add Labels");
744
+ await selectLabel("Alpha");
745
+ submitModal();
746
+
747
+ await waitForBulkRunToEnd();
748
+
749
+ const call: any = (getItemMock.mock.calls[0] as Array<any>)[0];
750
+
751
+ expect(call.modelType).toBe(Monitor);
752
+ expect(call.id.toString()).toBe(ITEM_ONE_ID);
753
+ /*
754
+ * `_id` is not cosmetic here: it is the only field that separates a real
755
+ * read of an unlabelled item from the empty body a deleted or filtered row
756
+ * answers with, and the guard below leans on it entirely.
757
+ */
758
+ expect(call.select).toEqual({ _id: true, labels: { _id: true } });
759
+ expect(getUpdatedLabelIds(0)).toEqual([LABEL_ALPHA_ID]);
760
+ });
761
+
762
+ test("drops a dropdown option that came back without an id before merging", async () => {
763
+ const orphan: Label = new Label();
764
+ orphan.name = "Orphan";
765
+
766
+ getListMock.mockResolvedValue({
767
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha"), orphan],
768
+ count: 2,
769
+ });
770
+ getItemMock.mockResolvedValue(
771
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_GAMMA_ID, "Gamma")]),
772
+ );
773
+
774
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
775
+
776
+ await waitForMountedLabelFetch();
777
+
778
+ clickTrigger("Add Labels");
779
+ await waitForModalTitle("Add Labels");
780
+ await selectLabel("Orphan");
781
+ await selectLabel("Alpha");
782
+ submitModal();
783
+
784
+ await waitForBulkRunToEnd();
785
+
786
+ /*
787
+ * An id-less label renders as an option with an empty value; sending ""
788
+ * inside the labels array makes the API reject the whole item, so one
789
+ * unusable option would fail the entire bulk run.
790
+ */
791
+ expect(getUpdatedLabelIds(0)).toEqual([LABEL_GAMMA_ID, LABEL_ALPHA_ID]);
792
+ });
793
+
794
+ test("starts nothing and reports nothing when every submitted label id is blank", async () => {
795
+ getListMock.mockResolvedValue({
796
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
797
+ count: 1,
798
+ });
799
+
800
+ render(<Harness items={[makeItem(ITEM_ONE_ID), makeItem(ITEM_TWO_ID)]} />);
801
+
802
+ await waitForMountedLabelFetch();
803
+
804
+ clickTrigger("Add Labels");
805
+ await waitForModalTitle("Add Labels");
806
+
807
+ await actAsync(async (): Promise<void> => {
808
+ await getModalSubmit("Add Labels")({ labelIds: ["", ""] });
809
+ });
810
+
811
+ /*
812
+ * Nothing survives the blank-id filter, so there is nothing to apply. The
813
+ * loop used to run anyway: every item matched the no-op short-circuit and
814
+ * was pushed onto successItems, so the user was told a bulk edit landed on
815
+ * their whole selection when not one label had been asked for.
816
+ */
817
+ expect(onBulkActionStartMock).not.toHaveBeenCalled();
818
+ expect(onProgressInfoMock).not.toHaveBeenCalled();
819
+ expect(onBulkActionEndMock).not.toHaveBeenCalled();
820
+ expect(getItemMock).not.toHaveBeenCalled();
821
+ expect(updateByIdMock).not.toHaveBeenCalled();
822
+ // The submit still dismisses the form rather than leaving it hanging open.
823
+ expect(screen.queryByTestId("modal")).toBeNull();
824
+ });
825
+
826
+ test("fails an item whose current read carries no id instead of overwriting its labels", async () => {
827
+ /*
828
+ * This is the reachable case. A row deleted or filtered out between the
829
+ * table load and the action still answers HTTP 200, with an empty body
830
+ * that BaseModel.fromJSONObject turns into a truthy model carrying neither
831
+ * _id nor labels - so a plain null check never fires and add mode reads it
832
+ * as "this item has no labels".
833
+ */
834
+ await addAlphaOverCurrentRead(BaseModel.fromJSONObject({}, Monitor));
835
+
836
+ /*
837
+ * Any update at all is the bug: the only thing the hook could have sent is
838
+ * `labels: [LABEL_ALPHA_ID]`, which is the item's entire label set replaced
839
+ * by the one label just picked.
840
+ */
841
+ expect(updateByIdMock).not.toHaveBeenCalled();
842
+ expect(progressSnapshots[0]!.failedIds).toEqual([ITEM_ONE_ID]);
843
+ expect(progressSnapshots[0]!.failedMessages[0]).toBe(
844
+ "Could not read the current labels for this item.",
845
+ );
846
+ expect(progressSnapshots[0]!.successIds).toEqual([]);
847
+ });
848
+
849
+ test("fails an item whose current read comes back as null instead of overwriting its labels", async () => {
850
+ await addAlphaOverCurrentRead(null);
851
+
852
+ // ModelAPI.getItem is typed to allow null, so the guard has to cover it too.
853
+ expect(updateByIdMock).not.toHaveBeenCalled();
854
+ expect(progressSnapshots[0]!.failedIds).toEqual([ITEM_ONE_ID]);
855
+ expect(progressSnapshots[0]!.failedMessages[0]).toBe(
856
+ "Could not read the current labels for this item.",
857
+ );
858
+ expect(progressSnapshots[0]!.successIds).toEqual([]);
859
+ });
860
+
861
+ test("removes only the picked labels and keeps the rest", async () => {
862
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
863
+ if (data.modelType === Label) {
864
+ return { data: [], count: 0 };
865
+ }
866
+
867
+ return {
868
+ data: [
869
+ makeItemWithLabels(ITEM_ONE_ID, [
870
+ makeLabel(LABEL_ALPHA_ID, "Alpha"),
871
+ makeLabel(LABEL_BETA_ID, "Beta"),
872
+ makeLabel(LABEL_GAMMA_ID, "Gamma"),
873
+ ]),
874
+ ],
875
+ count: 1,
876
+ };
877
+ });
878
+ getItemMock.mockResolvedValue(
879
+ makeItemWithLabels(ITEM_ONE_ID, [
880
+ makeLabel(LABEL_ALPHA_ID, "Alpha"),
881
+ makeLabel(LABEL_BETA_ID, "Beta"),
882
+ makeLabel(LABEL_GAMMA_ID, "Gamma"),
883
+ ]),
884
+ );
885
+
886
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
887
+
888
+ clickTrigger("Remove Labels");
889
+ await waitForModalTitle("Remove Labels");
890
+ await selectLabel("Beta");
891
+ submitModal();
892
+
893
+ await waitForBulkRunToEnd();
894
+
895
+ expect(getUpdatedLabelIds(0)).toEqual([LABEL_ALPHA_ID, LABEL_GAMMA_ID]);
896
+ });
897
+
898
+ test("skips the update but still counts the item as processed when nothing changes", async () => {
899
+ getListMock.mockResolvedValue({
900
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
901
+ count: 1,
902
+ });
903
+ getItemMock.mockResolvedValue(
904
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_ALPHA_ID, "Alpha")]),
905
+ );
906
+
907
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
908
+
909
+ await waitForMountedLabelFetch();
910
+
911
+ clickTrigger("Add Labels");
912
+ await waitForModalTitle("Add Labels");
913
+ await selectLabel("Alpha");
914
+ submitModal();
915
+
916
+ await waitForBulkRunToEnd();
917
+
918
+ /*
919
+ * A skipped write still has to advance the progress bar, otherwise a
920
+ * selection of already-labelled items looks like a stalled run.
921
+ */
922
+ expect(updateByIdMock).not.toHaveBeenCalled();
923
+ expect(progressSnapshots[0]!.successIds).toEqual([ITEM_ONE_ID]);
924
+ expect(progressSnapshots[0]!.failedIds).toEqual([]);
925
+ });
926
+
927
+ test("keeps going after one item fails and reports only that item as failed", async () => {
928
+ getListMock.mockResolvedValue({
929
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
930
+ count: 1,
931
+ });
932
+ getItemMock.mockImplementation(async (data: any): Promise<any> => {
933
+ if (data.id.toString() === ITEM_TWO_ID) {
934
+ throw new Error("item two is gone");
935
+ }
936
+
937
+ return makeItemWithLabels(data.id.toString(), []);
938
+ });
939
+
940
+ render(
941
+ <Harness
942
+ items={[
943
+ makeItem(ITEM_ONE_ID),
944
+ makeItem(ITEM_TWO_ID),
945
+ makeItem(ITEM_THREE_ID),
946
+ ]}
947
+ />,
948
+ );
949
+
950
+ await waitForMountedLabelFetch();
951
+
952
+ clickTrigger("Add Labels");
953
+ await waitForModalTitle("Add Labels");
954
+ await selectLabel("Alpha");
955
+ submitModal();
956
+
957
+ await waitForBulkRunToEnd();
958
+
959
+ expect(getItemMock).toHaveBeenCalledTimes(3);
960
+ expect(updateByIdMock).toHaveBeenCalledTimes(2);
961
+
962
+ const finalSnapshot: ProgressSnapshot = progressSnapshots[2]!;
963
+
964
+ expect(finalSnapshot.successIds).toEqual([ITEM_ONE_ID, ITEM_THREE_ID]);
965
+ expect(finalSnapshot.failedIds).toEqual([ITEM_TWO_ID]);
966
+ expect(finalSnapshot.failedMessages).toEqual(["item two is gone"]);
967
+ });
968
+
969
+ test("takes the failure message from API.getFriendlyMessage", async () => {
970
+ const friendlyMessage: MockFunction = jest.spyOn(
971
+ API,
972
+ "getFriendlyMessage",
973
+ ) as unknown as MockFunction;
974
+ friendlyMessage.mockReturnValue("Something went wrong. Try again.");
975
+
976
+ const sentinel: Error = new Error("raw upstream text");
977
+
978
+ getListMock.mockResolvedValue({
979
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
980
+ count: 1,
981
+ });
982
+ /*
983
+ * mockRejectedValue hands zone.js a rejected promise it sees before the
984
+ * hook attaches its catch, and it prints the whole stack as a console
985
+ * error on every run. Throwing from inside the call keeps CI readable.
986
+ */
987
+ getItemMock.mockImplementation(async (): Promise<never> => {
988
+ throw sentinel;
989
+ });
990
+
991
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
992
+
993
+ await waitForMountedLabelFetch();
994
+
995
+ clickTrigger("Add Labels");
996
+ await waitForModalTitle("Add Labels");
997
+ await selectLabel("Alpha");
998
+ submitModal();
999
+
1000
+ await waitForBulkRunToEnd();
1001
+
1002
+ // The raw error must go through the formatter, not into the UI verbatim.
1003
+ expect(friendlyMessage).toHaveBeenCalledWith(sentinel);
1004
+ expect(progressSnapshots[0]!.failedMessages).toEqual([
1005
+ "Something went wrong. Try again.",
1006
+ ]);
1007
+ });
1008
+
1009
+ test("fails an item that has no id without calling the API for it", async () => {
1010
+ getListMock.mockResolvedValue({
1011
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
1012
+ count: 1,
1013
+ });
1014
+ getItemMock.mockResolvedValue(makeItemWithLabels(ITEM_ONE_ID, []));
1015
+
1016
+ render(<Harness items={[new Monitor(), makeItem(ITEM_ONE_ID)]} />);
1017
+
1018
+ await waitForMountedLabelFetch();
1019
+
1020
+ clickTrigger("Add Labels");
1021
+ await waitForModalTitle("Add Labels");
1022
+ await selectLabel("Alpha");
1023
+ submitModal();
1024
+
1025
+ await waitForBulkRunToEnd();
1026
+
1027
+ expect(progressSnapshots[0]!.failedMessages).toEqual(["Item ID not found"]);
1028
+ // Only the second item is addressable, so only it may reach the API.
1029
+ expect(getItemMock).toHaveBeenCalledTimes(1);
1030
+ expect(updateByIdMock).toHaveBeenCalledTimes(1);
1031
+ expect(progressSnapshots[1]!.successIds).toEqual([ITEM_ONE_ID]);
1032
+ });
1033
+
1034
+ test("brackets the run with one start and one end around a progress call per item", async () => {
1035
+ getListMock.mockResolvedValue({
1036
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
1037
+ count: 1,
1038
+ });
1039
+ getItemMock.mockImplementation(async (data: any): Promise<any> => {
1040
+ return makeItemWithLabels(data.id.toString(), []);
1041
+ });
1042
+
1043
+ render(<Harness items={[makeItem(ITEM_ONE_ID), makeItem(ITEM_TWO_ID)]} />);
1044
+
1045
+ await waitForMountedLabelFetch();
1046
+
1047
+ clickTrigger("Add Labels");
1048
+ await waitForModalTitle("Add Labels");
1049
+ await selectLabel("Alpha");
1050
+ submitModal();
1051
+
1052
+ await waitForBulkRunToEnd();
1053
+
1054
+ expect(callOrder).toEqual(["start", "progress", "progress", "end"]);
1055
+ expect(onBulkActionStartMock).toHaveBeenCalledTimes(1);
1056
+ expect(onProgressInfoMock).toHaveBeenCalledTimes(2);
1057
+ });
1058
+
1059
+ test("reports the running success and failure split after each item", async () => {
1060
+ getListMock.mockResolvedValue({
1061
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
1062
+ count: 1,
1063
+ });
1064
+ getItemMock.mockImplementation(async (data: any): Promise<any> => {
1065
+ if (data.id.toString() === ITEM_TWO_ID) {
1066
+ throw new Error("boom");
1067
+ }
1068
+
1069
+ return makeItemWithLabels(data.id.toString(), []);
1070
+ });
1071
+
1072
+ render(
1073
+ <Harness
1074
+ items={[
1075
+ makeItem(ITEM_ONE_ID),
1076
+ makeItem(ITEM_TWO_ID),
1077
+ makeItem(ITEM_THREE_ID),
1078
+ ]}
1079
+ />,
1080
+ );
1081
+
1082
+ await waitForMountedLabelFetch();
1083
+
1084
+ clickTrigger("Add Labels");
1085
+ await waitForModalTitle("Add Labels");
1086
+ await selectLabel("Alpha");
1087
+ submitModal();
1088
+
1089
+ await waitForBulkRunToEnd();
1090
+
1091
+ expect(progressSnapshots).toHaveLength(3);
1092
+
1093
+ /*
1094
+ * The item being worked on is spliced out of inProgressItems before its own
1095
+ * callback fires, so the remaining count is the work still queued.
1096
+ */
1097
+ expect(progressSnapshots[0]).toEqual({
1098
+ totalIds: [ITEM_ONE_ID, ITEM_TWO_ID, ITEM_THREE_ID],
1099
+ inProgressIds: [ITEM_TWO_ID, ITEM_THREE_ID],
1100
+ successIds: [ITEM_ONE_ID],
1101
+ failedIds: [],
1102
+ failedMessages: [],
1103
+ });
1104
+ expect(progressSnapshots[1]).toEqual({
1105
+ totalIds: [ITEM_ONE_ID, ITEM_TWO_ID, ITEM_THREE_ID],
1106
+ inProgressIds: [ITEM_THREE_ID],
1107
+ successIds: [ITEM_ONE_ID],
1108
+ failedIds: [ITEM_TWO_ID],
1109
+ failedMessages: ["boom"],
1110
+ });
1111
+ expect(progressSnapshots[2]).toEqual({
1112
+ totalIds: [ITEM_ONE_ID, ITEM_TWO_ID, ITEM_THREE_ID],
1113
+ inProgressIds: [],
1114
+ successIds: [ITEM_ONE_ID, ITEM_THREE_ID],
1115
+ failedIds: [ITEM_TWO_ID],
1116
+ failedMessages: ["boom"],
1117
+ });
1118
+ });
1119
+
1120
+ test("offers the de-duplicated union of the selection's labels, sorted by name", async () => {
1121
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1122
+ if (data.modelType === Label) {
1123
+ return { data: [], count: 0 };
1124
+ }
1125
+
1126
+ return {
1127
+ data: [
1128
+ makeItemWithLabels(ITEM_ONE_ID, [
1129
+ makeLabel(LABEL_ALPHA_ID, "Zulu"),
1130
+ makeLabel(LABEL_BETA_ID, "apple"),
1131
+ ]),
1132
+ makeItemWithLabels(ITEM_TWO_ID, [
1133
+ makeLabel(LABEL_BETA_ID, "apple"),
1134
+ makeLabel(LABEL_GAMMA_ID, "Mike"),
1135
+ ]),
1136
+ ],
1137
+ count: 2,
1138
+ };
1139
+ });
1140
+
1141
+ render(<Harness items={[makeItem(ITEM_ONE_ID), makeItem(ITEM_TWO_ID)]} />);
1142
+
1143
+ clickTrigger("Remove Labels");
1144
+ await waitForModalTitle("Remove Labels");
1145
+
1146
+ const options: Array<HTMLElement> = await openMenu();
1147
+
1148
+ expect(options).toHaveLength(3);
1149
+
1150
+ /*
1151
+ * localeCompare, not codepoint order - a lowercase name would otherwise
1152
+ * sort below every capitalised one and look like a broken list.
1153
+ */
1154
+ expect(optionTexts(options)).toEqual(["apple", "Mike", "Zulu"]);
1155
+ });
1156
+
1157
+ test("scopes the option fetch to the selected ids", async () => {
1158
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1159
+ if (data.modelType === Label) {
1160
+ return { data: [], count: 0 };
1161
+ }
1162
+
1163
+ return {
1164
+ data: [
1165
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_ALPHA_ID, "Alpha")]),
1166
+ ],
1167
+ count: 1,
1168
+ };
1169
+ });
1170
+
1171
+ render(<Harness items={[makeItem(ITEM_ONE_ID), makeItem(ITEM_TWO_ID)]} />);
1172
+
1173
+ clickTrigger("Remove Labels");
1174
+
1175
+ await waitFor(() => {
1176
+ expect(getListMock).toHaveBeenCalledTimes(2);
1177
+ });
1178
+
1179
+ const call: any = getListCall(1);
1180
+
1181
+ expect(call.modelType).toBe(Monitor);
1182
+ expect(call.query._id).toBeInstanceOf(Includes);
1183
+ expect(call.query._id.values).toEqual([ITEM_ONE_ID, ITEM_TWO_ID]);
1184
+ expect(call.select).toEqual({
1185
+ labels: { _id: true, name: true, color: true },
1186
+ });
1187
+ });
1188
+
1189
+ test("carries a label's colour into its remove option", async () => {
1190
+ const coloured: Label = makeLabel(LABEL_ALPHA_ID, "Alpha");
1191
+ coloured.color = Red;
1192
+
1193
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1194
+ if (data.modelType === Label) {
1195
+ return { data: [], count: 0 };
1196
+ }
1197
+
1198
+ return {
1199
+ data: [
1200
+ makeItemWithLabels(ITEM_ONE_ID, [
1201
+ coloured,
1202
+ makeLabel(LABEL_BETA_ID, "Beta"),
1203
+ ]),
1204
+ ],
1205
+ count: 1,
1206
+ };
1207
+ });
1208
+
1209
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1210
+
1211
+ clickTrigger("Remove Labels");
1212
+ await waitForModalTitle("Remove Labels");
1213
+
1214
+ const options: Array<HTMLElement> = await openMenu();
1215
+ const alphaSwatch: Element | null =
1216
+ options[0]!.querySelector("span[title]");
1217
+
1218
+ expect(alphaSwatch).not.toBeNull();
1219
+ expect(alphaSwatch!.getAttribute("title")).toBe(Red.toString());
1220
+ // A colourless label must not inherit a swatch from anywhere.
1221
+ expect(options[1]!.querySelector("span[title]")).toBeNull();
1222
+ });
1223
+
1224
+ test("offers nothing to remove for an empty selection", async () => {
1225
+ render(<Harness items={[]} />);
1226
+
1227
+ await waitForMountedLabelFetch();
1228
+
1229
+ clickTrigger("Remove Labels");
1230
+ await waitForModalTitle("No Labels to Remove");
1231
+
1232
+ // No addressable ids means there is nothing to ask the server about.
1233
+ expect(getListMock).toHaveBeenCalledTimes(1);
1234
+ });
1235
+
1236
+ test("offers nothing to remove when the option fetch fails, rather than the whole project's labels", async () => {
1237
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1238
+ if (data.modelType === Label) {
1239
+ return {
1240
+ data: [
1241
+ makeLabel(LABEL_ALPHA_ID, "Alpha"),
1242
+ makeLabel(LABEL_BETA_ID, "Beta"),
1243
+ ],
1244
+ count: 2,
1245
+ };
1246
+ }
1247
+
1248
+ throw new Error("selection fetch failed");
1249
+ });
1250
+
1251
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1252
+
1253
+ clickTrigger("Remove Labels");
1254
+ await waitForModalTitle("No Labels to Remove");
1255
+
1256
+ /*
1257
+ * Falling back to the project label list here would offer labels the items
1258
+ * do not have, and removing one would look like it silently did nothing.
1259
+ */
1260
+ expect(screen.queryByRole("combobox")).toBeNull();
1261
+ });
1262
+
1263
+ test("the No Labels to Remove modal is a dead end with only a way out", async () => {
1264
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1265
+ if (data.modelType === Label) {
1266
+ return { data: [], count: 0 };
1267
+ }
1268
+
1269
+ return { data: [makeItemWithLabels(ITEM_ONE_ID, [])], count: 1 };
1270
+ });
1271
+
1272
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1273
+
1274
+ clickTrigger("Remove Labels");
1275
+ await waitForModalTitle("No Labels to Remove");
1276
+
1277
+ expect(screen.getByTestId("modal-footer-close-button")).toHaveTextContent(
1278
+ "Close",
1279
+ );
1280
+ // There is nothing to submit, so offering a submit button would be a trap.
1281
+ expect(screen.queryByTestId("modal-footer-submit-button")).toBeNull();
1282
+ });
1283
+
1284
+ test("opening Remove Labels closes an open Add Labels modal", async () => {
1285
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1286
+ if (data.modelType === Label) {
1287
+ return { data: [makeLabel(LABEL_ALPHA_ID, "Alpha")], count: 1 };
1288
+ }
1289
+
1290
+ return {
1291
+ data: [
1292
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_BETA_ID, "Beta")]),
1293
+ ],
1294
+ count: 1,
1295
+ };
1296
+ });
1297
+
1298
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1299
+
1300
+ clickTrigger("Add Labels");
1301
+ await waitForModalTitle("Add Labels");
1302
+
1303
+ clickTrigger("Remove Labels");
1304
+ await waitForModalTitle("Remove Labels");
1305
+
1306
+ /*
1307
+ * The two modals are independent siblings sharing one set of action props;
1308
+ * stacked, whichever submits first leaves the other silently inert.
1309
+ */
1310
+ expect(screen.getAllByTestId("modal-title")).toHaveLength(1);
1311
+ });
1312
+
1313
+ test("opening Add Labels closes an open Remove Labels modal", async () => {
1314
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1315
+ if (data.modelType === Label) {
1316
+ return { data: [makeLabel(LABEL_ALPHA_ID, "Alpha")], count: 1 };
1317
+ }
1318
+
1319
+ return {
1320
+ data: [
1321
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_BETA_ID, "Beta")]),
1322
+ ],
1323
+ count: 1,
1324
+ };
1325
+ });
1326
+
1327
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1328
+
1329
+ clickTrigger("Remove Labels");
1330
+ await waitForModalTitle("Remove Labels");
1331
+
1332
+ clickTrigger("Add Labels");
1333
+ await waitForModalTitle("Add Labels");
1334
+
1335
+ expect(screen.getAllByTestId("modal-title")).toHaveLength(1);
1336
+ });
1337
+
1338
+ test("re-opening the remove modal re-reads the new selection instead of reusing the old options", async () => {
1339
+ getListMock.mockImplementation(async (data: any): Promise<any> => {
1340
+ if (data.modelType === Label) {
1341
+ return { data: [], count: 0 };
1342
+ }
1343
+
1344
+ const ids: Array<string> = data.query._id.values as Array<string>;
1345
+
1346
+ if (ids.includes(ITEM_ONE_ID)) {
1347
+ return {
1348
+ data: [
1349
+ makeItemWithLabels(ITEM_ONE_ID, [
1350
+ makeLabel(LABEL_ALPHA_ID, "Alpha"),
1351
+ ]),
1352
+ ],
1353
+ count: 1,
1354
+ };
1355
+ }
1356
+
1357
+ return {
1358
+ data: [
1359
+ makeItemWithLabels(ITEM_TWO_ID, [makeLabel(LABEL_BETA_ID, "Beta")]),
1360
+ ],
1361
+ count: 1,
1362
+ };
1363
+ });
1364
+
1365
+ const { rerender } = render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1366
+
1367
+ clickTrigger("Remove Labels");
1368
+ await waitForModalTitle("Remove Labels");
1369
+
1370
+ const firstOptions: Array<HTMLElement> = await openMenu();
1371
+
1372
+ expect(firstOptions[0]!).toHaveTextContent("Alpha");
1373
+
1374
+ fireEvent.click(screen.getByTestId("modal-footer-close-button"));
1375
+
1376
+ rerender(<Harness items={[makeItem(ITEM_TWO_ID)]} />);
1377
+
1378
+ clickTrigger("Remove Labels");
1379
+ await waitForModalTitle("Remove Labels");
1380
+
1381
+ await waitFor(() => {
1382
+ expect(getListMock).toHaveBeenCalledTimes(3);
1383
+ });
1384
+
1385
+ const secondOptions: Array<HTMLElement> = await openMenu();
1386
+
1387
+ /*
1388
+ * The options are keyed to the selection, so a cached list from the last
1389
+ * open would offer labels the newly selected item does not have.
1390
+ */
1391
+ expect(secondOptions).toHaveLength(1);
1392
+ expect(secondOptions[0]!).toHaveTextContent("Beta");
1393
+ });
1394
+
1395
+ test("keeps the newest selection's remove options when an older fetch settles last", async () => {
1396
+ const firstSelectionFetch: DeferredListResult = createDeferredListResult();
1397
+ const secondSelectionFetch: DeferredListResult = createDeferredListResult();
1398
+
1399
+ getListMock.mockImplementation((data: any): Promise<any> => {
1400
+ if (data.modelType === Label) {
1401
+ return Promise.resolve({ data: [], count: 0 });
1402
+ }
1403
+
1404
+ const ids: Array<string> = data.query._id.values as Array<string>;
1405
+
1406
+ return ids.includes(ITEM_ONE_ID)
1407
+ ? firstSelectionFetch.promise
1408
+ : secondSelectionFetch.promise;
1409
+ });
1410
+
1411
+ render(<Harness items={[]} />);
1412
+
1413
+ await waitForMountedLabelFetch();
1414
+
1415
+ /*
1416
+ * Driven through onClick rather than the trigger button because the two
1417
+ * opens have to carry different selections while both fetches are still
1418
+ * in flight, which one mounted Harness cannot express.
1419
+ */
1420
+ const runs: Array<Promise<void>> = [];
1421
+
1422
+ await actAsync(async (): Promise<void> => {
1423
+ runs.push(
1424
+ capturedActions[1]!.onClick(makeActionProps([makeItem(ITEM_ONE_ID)])),
1425
+ );
1426
+ });
1427
+
1428
+ await actAsync(async (): Promise<void> => {
1429
+ runs.push(
1430
+ capturedActions[1]!.onClick(makeActionProps([makeItem(ITEM_TWO_ID)])),
1431
+ );
1432
+ });
1433
+
1434
+ await waitFor(() => {
1435
+ expect(getListMock).toHaveBeenCalledTimes(3);
1436
+ });
1437
+
1438
+ // The newest selection answers first...
1439
+ await actAsync(async (): Promise<void> => {
1440
+ secondSelectionFetch.resolve({
1441
+ data: [
1442
+ makeItemWithLabels(ITEM_TWO_ID, [makeLabel(LABEL_BETA_ID, "Beta")]),
1443
+ ],
1444
+ count: 1,
1445
+ });
1446
+ await runs[1]!;
1447
+ });
1448
+
1449
+ // ...and the abandoned first selection lands afterwards.
1450
+ await actAsync(async (): Promise<void> => {
1451
+ firstSelectionFetch.resolve({
1452
+ data: [
1453
+ makeItemWithLabels(ITEM_ONE_ID, [makeLabel(LABEL_ALPHA_ID, "Alpha")]),
1454
+ ],
1455
+ count: 1,
1456
+ });
1457
+ await runs[0]!;
1458
+ });
1459
+
1460
+ await waitForModalTitle("Remove Labels");
1461
+
1462
+ const options: Array<HTMLElement> = await openMenu();
1463
+
1464
+ /*
1465
+ * Last writer wins without the request stamp, and the slow first fetch is
1466
+ * the last writer here: the user would be offered Alpha, a label the item
1467
+ * they actually selected does not carry, and removing it would appear to
1468
+ * do nothing.
1469
+ */
1470
+ expect(optionTexts(options)).toEqual(["Beta"]);
1471
+ });
1472
+
1473
+ test("blocks a submit with nothing picked and touches no API", async () => {
1474
+ getListMock.mockResolvedValue({
1475
+ data: [makeLabel(LABEL_ALPHA_ID, "Alpha")],
1476
+ count: 1,
1477
+ });
1478
+
1479
+ render(<Harness items={[makeItem(ITEM_ONE_ID)]} />);
1480
+
1481
+ await waitForMountedLabelFetch();
1482
+
1483
+ clickTrigger("Add Labels");
1484
+ await waitForModalTitle("Add Labels");
1485
+
1486
+ submitModal();
1487
+
1488
+ expect(
1489
+ await screen.findByText("Select Labels is required.", undefined, {
1490
+ timeout: WAIT_TIMEOUT,
1491
+ }),
1492
+ ).toBeInTheDocument();
1493
+
1494
+ /*
1495
+ * An empty submit would otherwise start a bulk run that reads and rewrites
1496
+ * every selected item to no effect.
1497
+ */
1498
+ expect(onBulkActionStartMock).not.toHaveBeenCalled();
1499
+ expect(getItemMock).not.toHaveBeenCalled();
1500
+ expect(updateByIdMock).not.toHaveBeenCalled();
1501
+ });
1502
+ });