@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,1595 @@
1
+ import BooleanFilter from "../../../UI/Components/Filters/BooleanFilter";
2
+ import DateFilter from "../../../UI/Components/Filters/DateFilter";
3
+ import DropdownFilter from "../../../UI/Components/Filters/DropdownFilter";
4
+ import EntityFilter from "../../../UI/Components/Filters/EntityFilter";
5
+ import JSONFilter from "../../../UI/Components/Filters/JSONFilter";
6
+ import NumberFilter from "../../../UI/Components/Filters/NumberFilter";
7
+ import OperatorSelector from "../../../UI/Components/Filters/OperatorSelector";
8
+ import TextFilter from "../../../UI/Components/Filters/TextFilter";
9
+ import FilterOperator from "../../../UI/Components/Filters/Types/FilterOperator";
10
+ import Filter from "../../../UI/Components/Filters/Types/Filter";
11
+ import FilterData from "../../../UI/Components/Filters/Types/FilterData";
12
+ import FieldType from "../../../UI/Components/Types/FieldType";
13
+ import EqualTo from "../../../Types/BaseDatabase/EqualTo";
14
+ import GreaterThan from "../../../Types/BaseDatabase/GreaterThan";
15
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
16
+ import Includes from "../../../Types/BaseDatabase/Includes";
17
+ import IncludesAll from "../../../Types/BaseDatabase/IncludesAll";
18
+ import IncludesNone from "../../../Types/BaseDatabase/IncludesNone";
19
+ import IsNull from "../../../Types/BaseDatabase/IsNull";
20
+ import LessThan from "../../../Types/BaseDatabase/LessThan";
21
+ import NotContains from "../../../Types/BaseDatabase/NotContains";
22
+ import NotNull from "../../../Types/BaseDatabase/NotNull";
23
+ import Search from "../../../Types/BaseDatabase/Search";
24
+ import StartsWith from "../../../Types/BaseDatabase/StartsWith";
25
+ import OneUptimeDate from "../../../Types/Date";
26
+ import GenericObject from "../../../Types/GenericObject";
27
+ import {
28
+ RenderResult,
29
+ cleanup,
30
+ fireEvent,
31
+ render,
32
+ screen,
33
+ within,
34
+ } from "@testing-library/react";
35
+ import React, { ReactElement } from "react";
36
+ import {
37
+ afterEach,
38
+ beforeEach,
39
+ describe,
40
+ expect,
41
+ jest,
42
+ test,
43
+ } from "@jest/globals";
44
+
45
+ /*
46
+ * The reported bug was "I don't see the dropdown contents": the operator menu
47
+ * was absolutely positioned inside the modal body, which is a scroll container
48
+ * (`overflow-y-auto`), so it was clipped down to a sliver. These tests render
49
+ * every filter component that owns an OperatorSelector inside a clipping
50
+ * scroll container and assert that the menu (a) only exists once opened,
51
+ * (b) escapes the container by being portalled to document.body, (c) lists
52
+ * exactly that filter's operator set in order, and (d) writes back the value
53
+ * the picked operator is supposed to build — including the round trip that
54
+ * reads that value back as the same operator.
55
+ */
56
+
57
+ type Resource = GenericObject & {
58
+ name?: string;
59
+ count?: number;
60
+ createdAt?: Date;
61
+ lastSeenAt?: Date;
62
+ ownerId?: string;
63
+ labelIds?: Array<string>;
64
+ isActive?: boolean;
65
+ status?: string;
66
+ attributes?: Record<string, string>;
67
+ };
68
+
69
+ const MENU_TEST_ID: string = "operator-selector-menu";
70
+
71
+ /*
72
+ * Modal surfaces render at z-50 (Modal.tsx). The portalled menu has to sit
73
+ * above that stacking context or it is hidden behind the dialog even though it
74
+ * is no longer clipped.
75
+ */
76
+ const MODAL_SURFACE_Z_INDEX: number = 50;
77
+
78
+ const TEXT_FILTER: Filter<Resource> = {
79
+ key: "name",
80
+ title: "Name",
81
+ type: FieldType.Text,
82
+ } as unknown as Filter<Resource>;
83
+
84
+ const NUMBER_FILTER: Filter<Resource> = {
85
+ key: "count",
86
+ title: "Count",
87
+ type: FieldType.Number,
88
+ } as unknown as Filter<Resource>;
89
+
90
+ const DATE_FILTER: Filter<Resource> = {
91
+ key: "createdAt",
92
+ title: "Created At",
93
+ type: FieldType.Date,
94
+ } as unknown as Filter<Resource>;
95
+
96
+ const DATE_TIME_FILTER: Filter<Resource> = {
97
+ key: "lastSeenAt",
98
+ title: "Last Seen At",
99
+ type: FieldType.DateTime,
100
+ } as unknown as Filter<Resource>;
101
+
102
+ const ENTITY_FILTER: Filter<Resource> = {
103
+ key: "ownerId",
104
+ title: "Owner",
105
+ type: FieldType.Entity,
106
+ filterDropdownOptions: [
107
+ { label: "Alex", value: "user-1" },
108
+ { label: "Sam", value: "user-2" },
109
+ ],
110
+ } as unknown as Filter<Resource>;
111
+
112
+ const ENTITY_ARRAY_FILTER: Filter<Resource> = {
113
+ key: "labelIds",
114
+ title: "Labels",
115
+ type: FieldType.EntityArray,
116
+ filterDropdownOptions: [
117
+ { label: "Critical", value: "label-1" },
118
+ { label: "Backend", value: "label-2" },
119
+ ],
120
+ } as unknown as Filter<Resource>;
121
+
122
+ const BOOLEAN_FILTER: Filter<Resource> = {
123
+ key: "isActive",
124
+ title: "Is Active",
125
+ type: FieldType.Boolean,
126
+ } as unknown as Filter<Resource>;
127
+
128
+ const DROPDOWN_FILTER: Filter<Resource> = {
129
+ key: "status",
130
+ title: "Status",
131
+ type: FieldType.Text,
132
+ filterDropdownOptions: [
133
+ { label: "Open", value: "open" },
134
+ { label: "Closed", value: "closed" },
135
+ ],
136
+ } as unknown as Filter<Resource>;
137
+
138
+ const JSON_FILTER: Filter<Resource> = {
139
+ key: "attributes",
140
+ title: "Attributes",
141
+ type: FieldType.JSON,
142
+ } as unknown as Filter<Resource>;
143
+
144
+ const EMPTY_FILTER_DATA: FilterData<Resource> =
145
+ {} as unknown as FilterData<Resource>;
146
+
147
+ const TEXT_OPERATOR_LABELS: Array<string> = [
148
+ "contains",
149
+ "does not contain",
150
+ "equals",
151
+ "does not equal",
152
+ "starts with",
153
+ "ends with",
154
+ "is empty",
155
+ "is not empty",
156
+ ];
157
+
158
+ interface ClippedRender {
159
+ container: HTMLDivElement;
160
+ rerender: (element: ReactElement) => void;
161
+ unmount: () => void;
162
+ }
163
+
164
+ /*
165
+ * Mirrors the modal body: a scroll container that clips anything absolutely
166
+ * positioned inside it.
167
+ */
168
+ type RenderInClippingContainerFunction = (
169
+ element: ReactElement,
170
+ ) => ClippedRender;
171
+
172
+ const renderInClippingContainer: RenderInClippingContainerFunction = (
173
+ element: ReactElement,
174
+ ): ClippedRender => {
175
+ const scrollContainer: HTMLDivElement = document.createElement("div");
176
+ scrollContainer.style.overflowY = "auto";
177
+ scrollContainer.style.maxHeight = "120px";
178
+ document.body.appendChild(scrollContainer);
179
+
180
+ const result: RenderResult = render(element, { container: scrollContainer });
181
+
182
+ return {
183
+ container: scrollContainer,
184
+ rerender: (next: ReactElement): void => {
185
+ result.rerender(next);
186
+ },
187
+ unmount: (): void => {
188
+ result.unmount();
189
+ },
190
+ };
191
+ };
192
+
193
+ type GetTriggersFunction = (container: HTMLElement) => Array<HTMLButtonElement>;
194
+
195
+ const getTriggers: GetTriggersFunction = (
196
+ container: HTMLElement,
197
+ ): Array<HTMLButtonElement> => {
198
+ return Array.from(
199
+ container.querySelectorAll<HTMLButtonElement>(
200
+ '[id^="operator-selector-trigger"]',
201
+ ),
202
+ );
203
+ };
204
+
205
+ type GetTriggerFunction = (container: HTMLElement) => HTMLButtonElement | null;
206
+
207
+ const getTrigger: GetTriggerFunction = (
208
+ container: HTMLElement,
209
+ ): HTMLButtonElement | null => {
210
+ return getTriggers(container)[0] || null;
211
+ };
212
+
213
+ type RequireTriggerFunction = (container: HTMLElement) => HTMLButtonElement;
214
+
215
+ const requireTrigger: RequireTriggerFunction = (
216
+ container: HTMLElement,
217
+ ): HTMLButtonElement => {
218
+ const trigger: HTMLButtonElement | null = getTrigger(container);
219
+
220
+ if (!trigger) {
221
+ throw new Error("No OperatorSelector trigger was rendered.");
222
+ }
223
+
224
+ return trigger;
225
+ };
226
+
227
+ type GetTriggerLabelFunction = (container: HTMLElement) => string;
228
+
229
+ const getTriggerLabel: GetTriggerLabelFunction = (
230
+ container: HTMLElement,
231
+ ): string => {
232
+ return (requireTrigger(container).textContent || "").trim();
233
+ };
234
+
235
+ type OpenMenuForTriggerFunction = (trigger: HTMLButtonElement) => HTMLElement;
236
+
237
+ /*
238
+ * Opening is a state transition, so pin the closed half of it too: the menu
239
+ * must not exist and the trigger must not advertise one before the click.
240
+ * Without that, everything below would still pass against a menu that rendered
241
+ * unconditionally.
242
+ */
243
+ const openMenuForTrigger: OpenMenuForTriggerFunction = (
244
+ trigger: HTMLButtonElement,
245
+ ): HTMLElement => {
246
+ expect(trigger.getAttribute("aria-expanded")).toBe("false");
247
+ expect(trigger.getAttribute("aria-controls")).toBeNull();
248
+
249
+ fireEvent.mouseDown(trigger);
250
+ fireEvent.click(trigger);
251
+
252
+ const menuId: string | null = trigger.getAttribute("aria-controls");
253
+
254
+ expect(menuId).toBeTruthy();
255
+
256
+ const menu: HTMLElement | null = document.getElementById(menuId as string);
257
+
258
+ if (!menu) {
259
+ throw new Error("The OperatorSelector menu did not open.");
260
+ }
261
+
262
+ return menu;
263
+ };
264
+
265
+ type OpenMenuFunction = (container: HTMLElement) => HTMLElement;
266
+
267
+ const openMenu: OpenMenuFunction = (container: HTMLElement): HTMLElement => {
268
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
269
+
270
+ return openMenuForTrigger(requireTrigger(container));
271
+ };
272
+
273
+ type GetOptionLabelsFunction = (menu: HTMLElement) => Array<string>;
274
+
275
+ const getOptionLabels: GetOptionLabelsFunction = (
276
+ menu: HTMLElement,
277
+ ): Array<string> => {
278
+ return within(menu)
279
+ .getAllByRole("option")
280
+ .map((option: HTMLElement): string => {
281
+ return (option.textContent || "").trim();
282
+ });
283
+ };
284
+
285
+ type GetSelectedLabelsFunction = (menu: HTMLElement) => Array<string>;
286
+
287
+ const getSelectedLabels: GetSelectedLabelsFunction = (
288
+ menu: HTMLElement,
289
+ ): Array<string> => {
290
+ return within(menu)
291
+ .getAllByRole("option")
292
+ .filter((option: HTMLElement): boolean => {
293
+ return option.getAttribute("aria-selected") === "true";
294
+ })
295
+ .map((option: HTMLElement): string => {
296
+ return (option.textContent || "").trim();
297
+ });
298
+ };
299
+
300
+ type PickOperatorFunction = (menu: HTMLElement, label: string) => void;
301
+
302
+ const pickOperator: PickOperatorFunction = (
303
+ menu: HTMLElement,
304
+ label: string,
305
+ ): void => {
306
+ fireEvent.click(within(menu).getByRole("option", { name: label }));
307
+
308
+ // Picking always closes the menu, whatever branch the new operator renders.
309
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
310
+ };
311
+
312
+ type WrittenAtFunction = (mock: unknown, index: number) => FilterData<Resource>;
313
+
314
+ const writtenAt: WrittenAtFunction = (
315
+ mock: unknown,
316
+ index: number,
317
+ ): FilterData<Resource> => {
318
+ const calls: Array<Array<unknown>> = (
319
+ mock as { mock: { calls: Array<Array<unknown>> } }
320
+ ).mock.calls;
321
+
322
+ return calls[index]![0] as FilterData<Resource>;
323
+ };
324
+
325
+ type WrittenFunction = (mock: unknown) => FilterData<Resource>;
326
+
327
+ const written: WrittenFunction = (mock: unknown): FilterData<Resource> => {
328
+ return writtenAt(mock, 0);
329
+ };
330
+
331
+ /*
332
+ * Walks up from the menu looking for a scroll container. A `position: fixed`
333
+ * element is positioned against the viewport, but it is still clipped by an
334
+ * ancestor with `overflow` — which is exactly how the original bug worked.
335
+ */
336
+ type ClippingAncestorOfFunction = (element: HTMLElement) => HTMLElement | null;
337
+
338
+ const clippingAncestorOf: ClippingAncestorOfFunction = (
339
+ element: HTMLElement,
340
+ ): HTMLElement | null => {
341
+ let current: HTMLElement | null = element.parentElement;
342
+
343
+ while (current) {
344
+ const overflow: string = [
345
+ current.style.overflow,
346
+ current.style.overflowX,
347
+ current.style.overflowY,
348
+ ].join(" ");
349
+
350
+ if (overflow.includes("auto") || overflow.includes("hidden")) {
351
+ return current;
352
+ }
353
+
354
+ current = current.parentElement;
355
+ }
356
+
357
+ return null;
358
+ };
359
+
360
+ interface FilterCase {
361
+ name: string;
362
+ labels: Array<string>;
363
+ element: () => ReactElement;
364
+ }
365
+
366
+ type BuildCasesFunction = () => Array<FilterCase>;
367
+
368
+ const buildCases: BuildCasesFunction = (): Array<FilterCase> => {
369
+ return [
370
+ {
371
+ name: "TextFilter",
372
+ labels: TEXT_OPERATOR_LABELS,
373
+ element: (): ReactElement => {
374
+ return (
375
+ <TextFilter<Resource>
376
+ filter={TEXT_FILTER}
377
+ filterData={EMPTY_FILTER_DATA}
378
+ onFilterChanged={jest.fn()}
379
+ />
380
+ );
381
+ },
382
+ },
383
+ {
384
+ name: "NumberFilter",
385
+ labels: [
386
+ "equals",
387
+ "does not equal",
388
+ "is greater than",
389
+ "is less than",
390
+ "is greater than or equal to",
391
+ "is less than or equal to",
392
+ "is between",
393
+ "is empty",
394
+ "is not empty",
395
+ ],
396
+ element: (): ReactElement => {
397
+ return (
398
+ <NumberFilter<Resource>
399
+ filter={NUMBER_FILTER}
400
+ filterData={EMPTY_FILTER_DATA}
401
+ onFilterChanged={jest.fn()}
402
+ />
403
+ );
404
+ },
405
+ },
406
+ {
407
+ name: "DateFilter (Date)",
408
+ labels: [
409
+ "is",
410
+ "is before",
411
+ "is after",
412
+ "is between",
413
+ "is empty",
414
+ "is not empty",
415
+ ],
416
+ element: (): ReactElement => {
417
+ return (
418
+ <DateFilter<Resource>
419
+ filter={DATE_FILTER}
420
+ filterData={EMPTY_FILTER_DATA}
421
+ onFilterChanged={jest.fn()}
422
+ />
423
+ );
424
+ },
425
+ },
426
+ {
427
+ name: "DateFilter (DateTime)",
428
+ labels: [
429
+ "is",
430
+ "is before",
431
+ "is after",
432
+ "is between",
433
+ "is empty",
434
+ "is not empty",
435
+ ],
436
+ element: (): ReactElement => {
437
+ return (
438
+ <DateFilter<Resource>
439
+ filter={DATE_TIME_FILTER}
440
+ filterData={EMPTY_FILTER_DATA}
441
+ onFilterChanged={jest.fn()}
442
+ />
443
+ );
444
+ },
445
+ },
446
+ {
447
+ name: "EntityFilter (Entity)",
448
+ labels: ["is", "is not", "is empty", "is not empty"],
449
+ element: (): ReactElement => {
450
+ return (
451
+ <EntityFilter<Resource>
452
+ filter={ENTITY_FILTER}
453
+ filterData={EMPTY_FILTER_DATA}
454
+ onFilterChanged={jest.fn()}
455
+ />
456
+ );
457
+ },
458
+ },
459
+ {
460
+ name: "EntityFilter (EntityArray)",
461
+ labels: [
462
+ "has any of",
463
+ "has all of",
464
+ "has none of",
465
+ "is empty",
466
+ "is not empty",
467
+ ],
468
+ element: (): ReactElement => {
469
+ return (
470
+ <EntityFilter<Resource>
471
+ filter={ENTITY_ARRAY_FILTER}
472
+ filterData={EMPTY_FILTER_DATA}
473
+ onFilterChanged={jest.fn()}
474
+ />
475
+ );
476
+ },
477
+ },
478
+ ];
479
+ };
480
+
481
+ describe("OperatorSelector as each filter type uses it", () => {
482
+ beforeEach(() => {
483
+ jest.clearAllMocks();
484
+ });
485
+
486
+ afterEach(() => {
487
+ cleanup();
488
+ document.body.innerHTML = "";
489
+ });
490
+
491
+ describe("the menu lists the complete operator set for the filter", () => {
492
+ for (const filterCase of buildCases()) {
493
+ test(`${filterCase.name} lists exactly its operators, in order`, () => {
494
+ const rendered: ClippedRender = renderInClippingContainer(
495
+ filterCase.element(),
496
+ );
497
+
498
+ const menu: HTMLElement = openMenu(rendered.container);
499
+
500
+ expect(getOptionLabels(menu)).toEqual(filterCase.labels);
501
+ });
502
+ }
503
+
504
+ /*
505
+ * TextFilter is the dispatch target for nine field types. The operator list
506
+ * is the same for all of them, so a regression that narrows
507
+ * TEXT_FIELD_TYPES makes those filters render nothing at all — silently.
508
+ */
509
+ const OTHER_TEXT_FIELD_TYPES: Array<FieldType> = [
510
+ FieldType.LongText,
511
+ FieldType.Email,
512
+ FieldType.Phone,
513
+ FieldType.Name,
514
+ FieldType.Port,
515
+ FieldType.URL,
516
+ FieldType.Hostname,
517
+ FieldType.ObjectID,
518
+ ];
519
+
520
+ for (const fieldType of OTHER_TEXT_FIELD_TYPES) {
521
+ test(`a ${fieldType} field routes to TextFilter and offers the text operators`, () => {
522
+ const rendered: ClippedRender = renderInClippingContainer(
523
+ <TextFilter<Resource>
524
+ filter={
525
+ {
526
+ key: "name",
527
+ title: "Name",
528
+ type: fieldType,
529
+ } as unknown as Filter<Resource>
530
+ }
531
+ filterData={EMPTY_FILTER_DATA}
532
+ onFilterChanged={jest.fn()}
533
+ />,
534
+ );
535
+
536
+ const menu: HTMLElement = openMenu(rendered.container);
537
+
538
+ expect(getOptionLabels(menu)).toEqual(TEXT_OPERATOR_LABELS);
539
+ });
540
+ }
541
+ });
542
+
543
+ describe("the menu escapes the modal's clipping scroll container", () => {
544
+ test("TextFilter's menu is portalled out of the scroll container", () => {
545
+ const rendered: ClippedRender = renderInClippingContainer(
546
+ <TextFilter<Resource>
547
+ filter={TEXT_FILTER}
548
+ filterData={EMPTY_FILTER_DATA}
549
+ onFilterChanged={jest.fn()}
550
+ />,
551
+ );
552
+
553
+ const menu: HTMLElement = openMenu(rendered.container);
554
+
555
+ expect(rendered.container.contains(menu)).toBe(false);
556
+ expect(menu.parentElement).toBe(document.body);
557
+ // Nothing between the menu and the document root can clip it any more.
558
+ expect(clippingAncestorOf(menu)).toBeNull();
559
+ });
560
+
561
+ test("EntityFilter's menu is portalled out of the scroll container", () => {
562
+ const rendered: ClippedRender = renderInClippingContainer(
563
+ <EntityFilter<Resource>
564
+ filter={ENTITY_ARRAY_FILTER}
565
+ filterData={EMPTY_FILTER_DATA}
566
+ onFilterChanged={jest.fn()}
567
+ />,
568
+ );
569
+
570
+ const menu: HTMLElement = openMenu(rendered.container);
571
+
572
+ expect(rendered.container.contains(menu)).toBe(false);
573
+ expect(menu.parentElement).toBe(document.body);
574
+ expect(clippingAncestorOf(menu)).toBeNull();
575
+ });
576
+
577
+ test("the menu is viewport-positioned and stacks above the modal surface", () => {
578
+ const rendered: ClippedRender = renderInClippingContainer(
579
+ <TextFilter<Resource>
580
+ filter={TEXT_FILTER}
581
+ filterData={EMPTY_FILTER_DATA}
582
+ onFilterChanged={jest.fn()}
583
+ />,
584
+ );
585
+
586
+ const menu: HTMLElement = openMenu(rendered.container);
587
+
588
+ /*
589
+ * Fixed positioning is what makes portalling useful: the menu is placed
590
+ * against the viewport from the trigger's rect rather than flowing inside
591
+ * whatever ancestor it happens to sit in.
592
+ */
593
+ expect(menu.className.split(" ")).toContain("fixed");
594
+
595
+ // Modal surfaces are z-50; the menu must win against them.
596
+ expect(Number(menu.style.zIndex)).toBeGreaterThan(MODAL_SURFACE_Z_INDEX);
597
+ });
598
+
599
+ test("unmounting a filter while its menu is open tears the portal down", () => {
600
+ const rendered: ClippedRender = renderInClippingContainer(
601
+ <TextFilter<Resource>
602
+ filter={TEXT_FILTER}
603
+ filterData={EMPTY_FILTER_DATA}
604
+ onFilterChanged={jest.fn()}
605
+ />,
606
+ );
607
+
608
+ openMenu(rendered.container);
609
+
610
+ rendered.unmount();
611
+
612
+ /*
613
+ * Checked here, before afterEach wipes document.body — a leaked fixed
614
+ * z-60 node floating over the app is the failure mode portalling invites.
615
+ */
616
+ expect(
617
+ document.querySelector(`[data-testid="${MENU_TEST_ID}"]`),
618
+ ).toBeNull();
619
+ });
620
+ });
621
+
622
+ describe("the trigger describes the menu it owns", () => {
623
+ test("aria-expanded and aria-controls track the open state", () => {
624
+ const rendered: ClippedRender = renderInClippingContainer(
625
+ <TextFilter<Resource>
626
+ filter={TEXT_FILTER}
627
+ filterData={EMPTY_FILTER_DATA}
628
+ onFilterChanged={jest.fn()}
629
+ />,
630
+ );
631
+
632
+ const trigger: HTMLButtonElement = requireTrigger(rendered.container);
633
+
634
+ expect(trigger.getAttribute("aria-haspopup")).toBe("listbox");
635
+ expect(trigger.getAttribute("aria-expanded")).toBe("false");
636
+ expect(trigger.getAttribute("aria-controls")).toBeNull();
637
+
638
+ const menu: HTMLElement = openMenuForTrigger(trigger);
639
+
640
+ expect(trigger.getAttribute("aria-expanded")).toBe("true");
641
+ expect(trigger.getAttribute("aria-controls")).toBe(menu.id);
642
+ expect(menu.getAttribute("aria-labelledby")).toBe(trigger.id);
643
+ expect(menu.getAttribute("role")).toBe("listbox");
644
+
645
+ fireEvent.click(trigger);
646
+
647
+ expect(trigger.getAttribute("aria-expanded")).toBe("false");
648
+ expect(trigger.getAttribute("aria-controls")).toBeNull();
649
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
650
+ });
651
+
652
+ test("the collapsed trigger shows the operator encoded in filterData", () => {
653
+ const rendered: ClippedRender = renderInClippingContainer(
654
+ <TextFilter<Resource>
655
+ filter={TEXT_FILTER}
656
+ filterData={
657
+ { name: new NotContains("db") } as unknown as FilterData<Resource>
658
+ }
659
+ onFilterChanged={jest.fn()}
660
+ />,
661
+ );
662
+
663
+ // Read before anything is clicked: this is the collapsed state.
664
+ expect(getTriggerLabel(rendered.container)).toBe("does not contain");
665
+ expect(
666
+ rendered.container.querySelector<HTMLInputElement>('input[type="text"]')
667
+ ?.value,
668
+ ).toBe("db");
669
+ });
670
+
671
+ test("the collapsed trigger of an EntityArray filter shows its array operator", () => {
672
+ const rendered: ClippedRender = renderInClippingContainer(
673
+ <EntityFilter<Resource>
674
+ filter={ENTITY_ARRAY_FILTER}
675
+ filterData={
676
+ {
677
+ labelIds: new IncludesNone(["label-1"]),
678
+ } as unknown as FilterData<Resource>
679
+ }
680
+ onFilterChanged={jest.fn()}
681
+ />,
682
+ );
683
+
684
+ expect(getTriggerLabel(rendered.container)).toBe("has none of");
685
+ });
686
+
687
+ test("two filters side by side own separate menus, and only one is open at a time", () => {
688
+ const rendered: ClippedRender = renderInClippingContainer(
689
+ <>
690
+ <TextFilter<Resource>
691
+ filter={TEXT_FILTER}
692
+ filterData={EMPTY_FILTER_DATA}
693
+ onFilterChanged={jest.fn()}
694
+ />
695
+ <NumberFilter<Resource>
696
+ filter={NUMBER_FILTER}
697
+ filterData={EMPTY_FILTER_DATA}
698
+ onFilterChanged={jest.fn()}
699
+ />
700
+ </>,
701
+ );
702
+
703
+ const triggers: Array<HTMLButtonElement> = getTriggers(
704
+ rendered.container,
705
+ );
706
+
707
+ expect(triggers).toHaveLength(2);
708
+ expect(triggers[0]!.id).not.toBe(triggers[1]!.id);
709
+
710
+ const firstMenu: HTMLElement = openMenuForTrigger(triggers[0]!);
711
+
712
+ expect(getOptionLabels(firstMenu)).toEqual(TEXT_OPERATOR_LABELS);
713
+
714
+ const secondMenu: HTMLElement = openMenuForTrigger(triggers[1]!);
715
+
716
+ expect(secondMenu.id).not.toBe(firstMenu.id);
717
+ expect(triggers[0]!.getAttribute("aria-expanded")).toBe("false");
718
+ expect(triggers[1]!.getAttribute("aria-controls")).toBe(secondMenu.id);
719
+ expect(screen.queryAllByTestId(MENU_TEST_ID)).toHaveLength(1);
720
+ expect(getOptionLabels(secondMenu)).toContain("is greater than");
721
+ });
722
+ });
723
+
724
+ describe("an EntityArray filter offers array operators, not single-entity ones", () => {
725
+ test("array operators are offered and single-entity ones are not", () => {
726
+ const rendered: ClippedRender = renderInClippingContainer(
727
+ <EntityFilter<Resource>
728
+ filter={ENTITY_ARRAY_FILTER}
729
+ filterData={EMPTY_FILTER_DATA}
730
+ onFilterChanged={jest.fn()}
731
+ />,
732
+ );
733
+
734
+ const labels: Array<string> = getOptionLabels(
735
+ openMenu(rendered.container),
736
+ );
737
+
738
+ expect(labels).toContain("has any of");
739
+ expect(labels).toContain("has all of");
740
+ expect(labels).toContain("has none of");
741
+ expect(labels).not.toContain("is");
742
+ expect(labels).not.toContain("is not");
743
+ });
744
+
745
+ test("a single Entity filter offers 'is' / 'is not' and no array operators", () => {
746
+ const rendered: ClippedRender = renderInClippingContainer(
747
+ <EntityFilter<Resource>
748
+ filter={ENTITY_FILTER}
749
+ filterData={EMPTY_FILTER_DATA}
750
+ onFilterChanged={jest.fn()}
751
+ />,
752
+ );
753
+
754
+ const labels: Array<string> = getOptionLabels(
755
+ openMenu(rendered.container),
756
+ );
757
+
758
+ expect(labels).toContain("is");
759
+ expect(labels).toContain("is not");
760
+ expect(labels).not.toContain("has any of");
761
+ expect(labels).not.toContain("has all of");
762
+ expect(labels).not.toContain("has none of");
763
+ });
764
+ });
765
+
766
+ describe("the operator encoded in filterData is the selected one when the menu opens", () => {
767
+ test("TextFilter: NotContains opens with 'does not contain' selected", () => {
768
+ const rendered: ClippedRender = renderInClippingContainer(
769
+ <TextFilter<Resource>
770
+ filter={TEXT_FILTER}
771
+ filterData={
772
+ { name: new NotContains("db") } as unknown as FilterData<Resource>
773
+ }
774
+ onFilterChanged={jest.fn()}
775
+ />,
776
+ );
777
+
778
+ const menu: HTMLElement = openMenu(rendered.container);
779
+
780
+ expect(getSelectedLabels(menu)).toEqual(["does not contain"]);
781
+ });
782
+
783
+ test("TextFilter: IsNull opens with 'is empty' selected", () => {
784
+ const rendered: ClippedRender = renderInClippingContainer(
785
+ <TextFilter<Resource>
786
+ filter={TEXT_FILTER}
787
+ filterData={{ name: new IsNull() } as unknown as FilterData<Resource>}
788
+ onFilterChanged={jest.fn()}
789
+ />,
790
+ );
791
+
792
+ const menu: HTMLElement = openMenu(rendered.container);
793
+
794
+ expect(getSelectedLabels(menu)).toEqual(["is empty"]);
795
+ });
796
+
797
+ test("NumberFilter: NotNull opens with 'is not empty' selected", () => {
798
+ const rendered: ClippedRender = renderInClippingContainer(
799
+ <NumberFilter<Resource>
800
+ filter={NUMBER_FILTER}
801
+ filterData={
802
+ { count: new NotNull() } as unknown as FilterData<Resource>
803
+ }
804
+ onFilterChanged={jest.fn()}
805
+ />,
806
+ );
807
+
808
+ const menu: HTMLElement = openMenu(rendered.container);
809
+
810
+ expect(getSelectedLabels(menu)).toEqual(["is not empty"]);
811
+ });
812
+
813
+ test("DateFilter: GreaterThan opens with 'is after' selected", () => {
814
+ const rendered: ClippedRender = renderInClippingContainer(
815
+ <DateFilter<Resource>
816
+ filter={DATE_FILTER}
817
+ filterData={
818
+ {
819
+ createdAt: new GreaterThan(new Date("2024-03-04T05:06:07.000Z")),
820
+ } as unknown as FilterData<Resource>
821
+ }
822
+ onFilterChanged={jest.fn()}
823
+ />,
824
+ );
825
+
826
+ const menu: HTMLElement = openMenu(rendered.container);
827
+
828
+ expect(getSelectedLabels(menu)).toEqual(["is after"]);
829
+ });
830
+
831
+ test("EntityFilter (EntityArray): IncludesNone opens with 'has none of' selected", () => {
832
+ const rendered: ClippedRender = renderInClippingContainer(
833
+ <EntityFilter<Resource>
834
+ filter={ENTITY_ARRAY_FILTER}
835
+ filterData={
836
+ {
837
+ labelIds: new IncludesNone(["label-1"]),
838
+ } as unknown as FilterData<Resource>
839
+ }
840
+ onFilterChanged={jest.fn()}
841
+ />,
842
+ );
843
+
844
+ const menu: HTMLElement = openMenu(rendered.container);
845
+
846
+ expect(getSelectedLabels(menu)).toEqual(["has none of"]);
847
+ });
848
+
849
+ /*
850
+ * "has any of" is the odd one out on the write side: it encodes as a bare
851
+ * array rather than an Includes instance, so the read side has to decode a
852
+ * raw array too.
853
+ */
854
+ test("EntityFilter (EntityArray): a bare array opens with 'has any of' selected", () => {
855
+ const rendered: ClippedRender = renderInClippingContainer(
856
+ <EntityFilter<Resource>
857
+ filter={ENTITY_ARRAY_FILTER}
858
+ filterData={
859
+ {
860
+ labelIds: ["label-1"],
861
+ } as unknown as FilterData<Resource>
862
+ }
863
+ onFilterChanged={jest.fn()}
864
+ />,
865
+ );
866
+
867
+ const menu: HTMLElement = openMenu(rendered.container);
868
+
869
+ expect(getSelectedLabels(menu)).toEqual(["has any of"]);
870
+ });
871
+
872
+ test("EntityFilter (Entity): a one-item IncludesNone opens with 'is not' selected", () => {
873
+ const rendered: ClippedRender = renderInClippingContainer(
874
+ <EntityFilter<Resource>
875
+ filter={ENTITY_FILTER}
876
+ filterData={
877
+ {
878
+ ownerId: new IncludesNone(["user-1"]),
879
+ } as unknown as FilterData<Resource>
880
+ }
881
+ onFilterChanged={jest.fn()}
882
+ />,
883
+ );
884
+
885
+ const menu: HTMLElement = openMenu(rendered.container);
886
+
887
+ expect(getSelectedLabels(menu)).toEqual(["is not"]);
888
+ });
889
+ });
890
+
891
+ describe("picking an operator writes the value that operator builds", () => {
892
+ test("TextFilter: picking 'starts with' keeps the typed text", () => {
893
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
894
+ jest.fn();
895
+
896
+ const rendered: ClippedRender = renderInClippingContainer(
897
+ <TextFilter<Resource>
898
+ filter={TEXT_FILTER}
899
+ filterData={
900
+ { name: new Search("prod") } as unknown as FilterData<Resource>
901
+ }
902
+ onFilterChanged={onFilterChanged}
903
+ />,
904
+ );
905
+
906
+ pickOperator(openMenu(rendered.container), "starts with");
907
+
908
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
909
+
910
+ const value: unknown = written(onFilterChanged)["name"];
911
+
912
+ expect(value).toBeInstanceOf(StartsWith);
913
+ expect((value as StartsWith<string>).value).toBe("prod");
914
+ });
915
+
916
+ test("TextFilter: picking 'is empty' writes IsNull", () => {
917
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
918
+ jest.fn();
919
+
920
+ const rendered: ClippedRender = renderInClippingContainer(
921
+ <TextFilter<Resource>
922
+ filter={TEXT_FILTER}
923
+ filterData={
924
+ { name: new Search("prod") } as unknown as FilterData<Resource>
925
+ }
926
+ onFilterChanged={onFilterChanged}
927
+ />,
928
+ );
929
+
930
+ pickOperator(openMenu(rendered.container), "is empty");
931
+
932
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
933
+ expect(written(onFilterChanged)["name"]).toBeInstanceOf(IsNull);
934
+ });
935
+
936
+ test("TextFilter: picking 'is not empty' writes NotNull", () => {
937
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
938
+ jest.fn();
939
+
940
+ const rendered: ClippedRender = renderInClippingContainer(
941
+ <TextFilter<Resource>
942
+ filter={TEXT_FILTER}
943
+ filterData={EMPTY_FILTER_DATA}
944
+ onFilterChanged={onFilterChanged}
945
+ />,
946
+ );
947
+
948
+ pickOperator(openMenu(rendered.container), "is not empty");
949
+
950
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
951
+ expect(written(onFilterChanged)["name"]).toBeInstanceOf(NotNull);
952
+ });
953
+
954
+ test("NumberFilter: picking 'is greater than' keeps the number", () => {
955
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
956
+ jest.fn();
957
+
958
+ const rendered: ClippedRender = renderInClippingContainer(
959
+ <NumberFilter<Resource>
960
+ filter={NUMBER_FILTER}
961
+ filterData={
962
+ { count: new EqualTo(5) } as unknown as FilterData<Resource>
963
+ }
964
+ onFilterChanged={onFilterChanged}
965
+ />,
966
+ );
967
+
968
+ pickOperator(openMenu(rendered.container), "is greater than");
969
+
970
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
971
+
972
+ const value: unknown = written(onFilterChanged)["count"];
973
+
974
+ expect(value).toBeInstanceOf(GreaterThan);
975
+ expect((value as GreaterThan<number>).value).toBe(5);
976
+ });
977
+
978
+ test("DateFilter: picking 'is before' keeps the date and writes LessThan", () => {
979
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
980
+ jest.fn();
981
+
982
+ const date: Date = new Date("2024-03-04T05:06:07.000Z");
983
+
984
+ const rendered: ClippedRender = renderInClippingContainer(
985
+ <DateFilter<Resource>
986
+ filter={DATE_FILTER}
987
+ filterData={
988
+ {
989
+ createdAt: new GreaterThan(date),
990
+ } as unknown as FilterData<Resource>
991
+ }
992
+ onFilterChanged={onFilterChanged}
993
+ />,
994
+ );
995
+
996
+ pickOperator(openMenu(rendered.container), "is before");
997
+
998
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
999
+
1000
+ const value: unknown = written(onFilterChanged)["createdAt"];
1001
+
1002
+ expect(value).toBeInstanceOf(LessThan);
1003
+ expect((value as LessThan<Date>).value.getTime()).toBe(date.getTime());
1004
+ });
1005
+
1006
+ /*
1007
+ * A Date field has no time component, so "is" has to widen to the whole
1008
+ * day; a DateTime field keeps the exact instant. Same operator, two
1009
+ * encodings — the branch DateFilter.buildValue takes on `isDateTime`.
1010
+ */
1011
+ test("DateFilter (Date): picking 'is' widens to the bounds of that day", () => {
1012
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1013
+ jest.fn();
1014
+
1015
+ const date: Date = new Date("2024-03-04T05:06:07.000Z");
1016
+
1017
+ const rendered: ClippedRender = renderInClippingContainer(
1018
+ <DateFilter<Resource>
1019
+ filter={DATE_FILTER}
1020
+ filterData={
1021
+ {
1022
+ createdAt: new LessThan(date),
1023
+ } as unknown as FilterData<Resource>
1024
+ }
1025
+ onFilterChanged={onFilterChanged}
1026
+ />,
1027
+ );
1028
+
1029
+ pickOperator(openMenu(rendered.container), "is");
1030
+
1031
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1032
+
1033
+ const value: unknown = written(onFilterChanged)["createdAt"];
1034
+
1035
+ expect(value).toBeInstanceOf(InBetween);
1036
+ expect((value as InBetween<Date>).startValue.getTime()).toBe(
1037
+ OneUptimeDate.getStartOfDay(date).getTime(),
1038
+ );
1039
+ expect((value as InBetween<Date>).endValue.getTime()).toBe(
1040
+ OneUptimeDate.getEndOfDay(date).getTime(),
1041
+ );
1042
+ });
1043
+
1044
+ test("DateFilter (DateTime): picking 'is' keeps the exact instant", () => {
1045
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1046
+ jest.fn();
1047
+
1048
+ const date: Date = new Date("2024-03-04T05:06:07.000Z");
1049
+
1050
+ const rendered: ClippedRender = renderInClippingContainer(
1051
+ <DateFilter<Resource>
1052
+ filter={DATE_TIME_FILTER}
1053
+ filterData={
1054
+ {
1055
+ lastSeenAt: new LessThan(date),
1056
+ } as unknown as FilterData<Resource>
1057
+ }
1058
+ onFilterChanged={onFilterChanged}
1059
+ />,
1060
+ );
1061
+
1062
+ pickOperator(openMenu(rendered.container), "is");
1063
+
1064
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1065
+
1066
+ const value: unknown = written(onFilterChanged)["lastSeenAt"];
1067
+
1068
+ expect(value).toBeInstanceOf(EqualTo);
1069
+ expect(value).not.toBeInstanceOf(InBetween);
1070
+ expect((value as EqualTo<Date>).value.getTime()).toBe(date.getTime());
1071
+ });
1072
+
1073
+ test("EntityFilter (EntityArray): picking 'has all of' keeps the selected ids", () => {
1074
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1075
+ jest.fn();
1076
+
1077
+ const rendered: ClippedRender = renderInClippingContainer(
1078
+ <EntityFilter<Resource>
1079
+ filter={ENTITY_ARRAY_FILTER}
1080
+ filterData={
1081
+ {
1082
+ labelIds: new Includes(["label-1", "label-2"]),
1083
+ } as unknown as FilterData<Resource>
1084
+ }
1085
+ onFilterChanged={onFilterChanged}
1086
+ />,
1087
+ );
1088
+
1089
+ pickOperator(openMenu(rendered.container), "has all of");
1090
+
1091
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1092
+
1093
+ const value: unknown = written(onFilterChanged)["labelIds"];
1094
+
1095
+ expect(value).toBeInstanceOf(IncludesAll);
1096
+ expect((value as IncludesAll).values).toEqual(["label-1", "label-2"]);
1097
+ });
1098
+
1099
+ /*
1100
+ * Unlike every other array operator, "has any of" writes a bare string[] —
1101
+ * not an Includes instance. Wrapping it would change the query the server
1102
+ * sees, so pin the raw shape.
1103
+ */
1104
+ test("EntityFilter (EntityArray): picking 'has any of' writes a bare array", () => {
1105
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1106
+ jest.fn();
1107
+
1108
+ const rendered: ClippedRender = renderInClippingContainer(
1109
+ <EntityFilter<Resource>
1110
+ filter={ENTITY_ARRAY_FILTER}
1111
+ filterData={
1112
+ {
1113
+ labelIds: new IncludesAll(["label-1", "label-2"]),
1114
+ } as unknown as FilterData<Resource>
1115
+ }
1116
+ onFilterChanged={onFilterChanged}
1117
+ />,
1118
+ );
1119
+
1120
+ pickOperator(openMenu(rendered.container), "has any of");
1121
+
1122
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1123
+
1124
+ const value: unknown = written(onFilterChanged)["labelIds"];
1125
+
1126
+ expect(Array.isArray(value)).toBe(true);
1127
+ expect(value).toEqual(["label-1", "label-2"]);
1128
+ expect(value).not.toBeInstanceOf(Includes);
1129
+ });
1130
+
1131
+ test("EntityFilter (EntityArray): picking 'is empty' writes IsNull", () => {
1132
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1133
+ jest.fn();
1134
+
1135
+ const rendered: ClippedRender = renderInClippingContainer(
1136
+ <EntityFilter<Resource>
1137
+ filter={ENTITY_ARRAY_FILTER}
1138
+ filterData={
1139
+ {
1140
+ labelIds: new Includes(["label-1"]),
1141
+ } as unknown as FilterData<Resource>
1142
+ }
1143
+ onFilterChanged={onFilterChanged}
1144
+ />,
1145
+ );
1146
+
1147
+ pickOperator(openMenu(rendered.container), "is empty");
1148
+
1149
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1150
+ expect(written(onFilterChanged)["labelIds"]).toBeInstanceOf(IsNull);
1151
+ });
1152
+
1153
+ test("EntityFilter (Entity): picking 'is not' encodes a one-item IncludesNone", () => {
1154
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1155
+ jest.fn();
1156
+
1157
+ const rendered: ClippedRender = renderInClippingContainer(
1158
+ <EntityFilter<Resource>
1159
+ filter={ENTITY_FILTER}
1160
+ filterData={{ ownerId: "user-1" } as unknown as FilterData<Resource>}
1161
+ onFilterChanged={onFilterChanged}
1162
+ />,
1163
+ );
1164
+
1165
+ pickOperator(openMenu(rendered.container), "is not");
1166
+
1167
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1168
+
1169
+ const value: unknown = written(onFilterChanged)["ownerId"];
1170
+
1171
+ expect(value).toBeInstanceOf(IncludesNone);
1172
+ expect((value as IncludesNone).values).toEqual(["user-1"]);
1173
+ });
1174
+ });
1175
+
1176
+ /*
1177
+ * Closing the loop: feed the object the filter wrote straight back in as its
1178
+ * own props. Hand-authored fixtures on both sides can agree with each other
1179
+ * while disagreeing with what the component actually emits.
1180
+ */
1181
+ describe("what a filter writes reads back as the operator that wrote it", () => {
1182
+ test("EntityFilter (Entity): 'is not' survives a round trip through filterData", () => {
1183
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1184
+ jest.fn();
1185
+
1186
+ const rendered: ClippedRender = renderInClippingContainer(
1187
+ <EntityFilter<Resource>
1188
+ filter={ENTITY_FILTER}
1189
+ filterData={{ ownerId: "user-1" } as unknown as FilterData<Resource>}
1190
+ onFilterChanged={onFilterChanged}
1191
+ />,
1192
+ );
1193
+
1194
+ pickOperator(openMenu(rendered.container), "is not");
1195
+
1196
+ const emitted: FilterData<Resource> = written(onFilterChanged);
1197
+
1198
+ rendered.rerender(
1199
+ <EntityFilter<Resource>
1200
+ filter={ENTITY_FILTER}
1201
+ filterData={emitted}
1202
+ onFilterChanged={jest.fn()}
1203
+ />,
1204
+ );
1205
+
1206
+ expect(getTriggerLabel(rendered.container)).toBe("is not");
1207
+ expect(getSelectedLabels(openMenu(rendered.container))).toEqual([
1208
+ "is not",
1209
+ ]);
1210
+ });
1211
+
1212
+ test("TextFilter: 'does not contain' survives a round trip through filterData", () => {
1213
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1214
+ jest.fn();
1215
+
1216
+ const rendered: ClippedRender = renderInClippingContainer(
1217
+ <TextFilter<Resource>
1218
+ filter={TEXT_FILTER}
1219
+ filterData={
1220
+ { name: new Search("prod") } as unknown as FilterData<Resource>
1221
+ }
1222
+ onFilterChanged={onFilterChanged}
1223
+ />,
1224
+ );
1225
+
1226
+ pickOperator(openMenu(rendered.container), "does not contain");
1227
+
1228
+ const emitted: FilterData<Resource> = written(onFilterChanged);
1229
+
1230
+ rendered.rerender(
1231
+ <TextFilter<Resource>
1232
+ filter={TEXT_FILTER}
1233
+ filterData={emitted}
1234
+ onFilterChanged={jest.fn()}
1235
+ />,
1236
+ );
1237
+
1238
+ expect(getTriggerLabel(rendered.container)).toBe("does not contain");
1239
+ expect(getSelectedLabels(openMenu(rendered.container))).toEqual([
1240
+ "does not contain",
1241
+ ]);
1242
+ });
1243
+
1244
+ test("EntityFilter (EntityArray): the bare array 'has any of' writes reads back as 'has any of'", () => {
1245
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1246
+ jest.fn();
1247
+
1248
+ const rendered: ClippedRender = renderInClippingContainer(
1249
+ <EntityFilter<Resource>
1250
+ filter={ENTITY_ARRAY_FILTER}
1251
+ filterData={
1252
+ {
1253
+ labelIds: new IncludesAll(["label-1"]),
1254
+ } as unknown as FilterData<Resource>
1255
+ }
1256
+ onFilterChanged={onFilterChanged}
1257
+ />,
1258
+ );
1259
+
1260
+ pickOperator(openMenu(rendered.container), "has any of");
1261
+
1262
+ const emitted: FilterData<Resource> = written(onFilterChanged);
1263
+
1264
+ rendered.rerender(
1265
+ <EntityFilter<Resource>
1266
+ filter={ENTITY_ARRAY_FILTER}
1267
+ filterData={emitted}
1268
+ onFilterChanged={jest.fn()}
1269
+ />,
1270
+ );
1271
+
1272
+ expect(getTriggerLabel(rendered.container)).toBe("has any of");
1273
+ expect(getSelectedLabels(openMenu(rendered.container))).toEqual([
1274
+ "has any of",
1275
+ ]);
1276
+ });
1277
+ });
1278
+
1279
+ /*
1280
+ * The operator has to stick even when it builds nothing yet — otherwise the
1281
+ * user picks "starts with", the key is deleted from filterData, and the
1282
+ * trigger snaps back to "contains" on the very next render.
1283
+ */
1284
+ describe("picking an operator before a value has been entered", () => {
1285
+ test("TextFilter: the key is dropped but the trigger keeps 'starts with'", () => {
1286
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1287
+ jest.fn();
1288
+
1289
+ const rendered: ClippedRender = renderInClippingContainer(
1290
+ <TextFilter<Resource>
1291
+ filter={TEXT_FILTER}
1292
+ filterData={EMPTY_FILTER_DATA}
1293
+ onFilterChanged={onFilterChanged}
1294
+ />,
1295
+ );
1296
+
1297
+ expect(getTriggerLabel(rendered.container)).toBe("contains");
1298
+
1299
+ pickOperator(openMenu(rendered.container), "starts with");
1300
+
1301
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1302
+ expect(Object.keys(written(onFilterChanged))).not.toContain("name");
1303
+ expect(getTriggerLabel(rendered.container)).toBe("starts with");
1304
+ });
1305
+
1306
+ test("NumberFilter: the key is dropped but the trigger keeps 'is greater than'", () => {
1307
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1308
+ jest.fn();
1309
+
1310
+ const rendered: ClippedRender = renderInClippingContainer(
1311
+ <NumberFilter<Resource>
1312
+ filter={NUMBER_FILTER}
1313
+ filterData={EMPTY_FILTER_DATA}
1314
+ onFilterChanged={onFilterChanged}
1315
+ />,
1316
+ );
1317
+
1318
+ pickOperator(openMenu(rendered.container), "is greater than");
1319
+
1320
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1321
+ expect(Object.keys(written(onFilterChanged))).not.toContain("count");
1322
+ expect(getTriggerLabel(rendered.container)).toBe("is greater than");
1323
+ });
1324
+
1325
+ test("DateFilter: the key is dropped but the trigger keeps 'is before'", () => {
1326
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1327
+ jest.fn();
1328
+
1329
+ const rendered: ClippedRender = renderInClippingContainer(
1330
+ <DateFilter<Resource>
1331
+ filter={DATE_FILTER}
1332
+ filterData={EMPTY_FILTER_DATA}
1333
+ onFilterChanged={onFilterChanged}
1334
+ />,
1335
+ );
1336
+
1337
+ pickOperator(openMenu(rendered.container), "is before");
1338
+
1339
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1340
+ expect(Object.keys(written(onFilterChanged))).not.toContain("createdAt");
1341
+ expect(getTriggerLabel(rendered.container)).toBe("is before");
1342
+ });
1343
+
1344
+ test("EntityFilter (Entity): the key is dropped but the trigger keeps 'is not'", () => {
1345
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1346
+ jest.fn();
1347
+
1348
+ const rendered: ClippedRender = renderInClippingContainer(
1349
+ <EntityFilter<Resource>
1350
+ filter={ENTITY_FILTER}
1351
+ filterData={EMPTY_FILTER_DATA}
1352
+ onFilterChanged={onFilterChanged}
1353
+ />,
1354
+ );
1355
+
1356
+ pickOperator(openMenu(rendered.container), "is not");
1357
+
1358
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1359
+ expect(Object.keys(written(onFilterChanged))).not.toContain("ownerId");
1360
+ expect(getTriggerLabel(rendered.container)).toBe("is not");
1361
+ });
1362
+ });
1363
+
1364
+ describe("the operator decides which value inputs the filter renders", () => {
1365
+ test("TextFilter: 'is empty' hides the text input, and a value operator brings it back", () => {
1366
+ const rendered: ClippedRender = renderInClippingContainer(
1367
+ <TextFilter<Resource>
1368
+ filter={TEXT_FILTER}
1369
+ filterData={
1370
+ { name: new Search("prod") } as unknown as FilterData<Resource>
1371
+ }
1372
+ onFilterChanged={jest.fn()}
1373
+ />,
1374
+ );
1375
+
1376
+ expect(
1377
+ rendered.container.querySelector('input[type="text"]'),
1378
+ ).not.toBeNull();
1379
+
1380
+ pickOperator(openMenu(rendered.container), "is empty");
1381
+
1382
+ expect(rendered.container.querySelector('input[type="text"]')).toBeNull();
1383
+
1384
+ pickOperator(openMenu(rendered.container), "contains");
1385
+
1386
+ expect(
1387
+ rendered.container.querySelector<HTMLInputElement>('input[type="text"]')
1388
+ ?.value,
1389
+ ).toBe("prod");
1390
+ });
1391
+
1392
+ test("EntityFilter: 'is empty' hides the entity dropdown", () => {
1393
+ const rendered: ClippedRender = renderInClippingContainer(
1394
+ <EntityFilter<Resource>
1395
+ filter={ENTITY_ARRAY_FILTER}
1396
+ filterData={EMPTY_FILTER_DATA}
1397
+ onFilterChanged={jest.fn()}
1398
+ />,
1399
+ );
1400
+
1401
+ expect(screen.queryByText("Filter by Labels")).not.toBeNull();
1402
+
1403
+ pickOperator(openMenu(rendered.container), "is empty");
1404
+
1405
+ expect(screen.queryByText("Filter by Labels")).toBeNull();
1406
+ });
1407
+
1408
+ test("NumberFilter: 'is between' opens a second bound and writes nothing until both exist", () => {
1409
+ const onFilterChanged: (filterData: FilterData<Resource>) => void =
1410
+ jest.fn();
1411
+
1412
+ const rendered: ClippedRender = renderInClippingContainer(
1413
+ <NumberFilter<Resource>
1414
+ filter={NUMBER_FILTER}
1415
+ filterData={
1416
+ { count: new EqualTo(5) } as unknown as FilterData<Resource>
1417
+ }
1418
+ onFilterChanged={onFilterChanged}
1419
+ />,
1420
+ );
1421
+
1422
+ expect(rendered.container.querySelectorAll("input")).toHaveLength(1);
1423
+
1424
+ pickOperator(openMenu(rendered.container), "is between");
1425
+
1426
+ // Two bounds now, and the filter is dropped because only one is filled.
1427
+ expect(rendered.container.querySelectorAll("input")).toHaveLength(2);
1428
+ expect(onFilterChanged).toHaveBeenCalledTimes(1);
1429
+ expect(Object.keys(written(onFilterChanged))).not.toContain("count");
1430
+
1431
+ const upperBound: HTMLElement = screen.getByPlaceholderText("To");
1432
+
1433
+ fireEvent.change(upperBound, { target: { value: "9" } });
1434
+
1435
+ expect(onFilterChanged).toHaveBeenCalledTimes(2);
1436
+
1437
+ const value: unknown = writtenAt(onFilterChanged, 1)["count"];
1438
+
1439
+ expect(value).toBeInstanceOf(InBetween);
1440
+ expect((value as InBetween<number>).startValue).toBe(5);
1441
+ expect((value as InBetween<number>).endValue).toBe(9);
1442
+ });
1443
+
1444
+ test("DateFilter: 'is between' opens a second date input", () => {
1445
+ const rendered: ClippedRender = renderInClippingContainer(
1446
+ <DateFilter<Resource>
1447
+ filter={DATE_FILTER}
1448
+ filterData={EMPTY_FILTER_DATA}
1449
+ onFilterChanged={jest.fn()}
1450
+ />,
1451
+ );
1452
+
1453
+ expect(rendered.container.querySelectorAll("input")).toHaveLength(1);
1454
+
1455
+ pickOperator(openMenu(rendered.container), "is between");
1456
+
1457
+ expect(rendered.container.querySelectorAll("input")).toHaveLength(2);
1458
+
1459
+ pickOperator(openMenu(rendered.container), "is empty");
1460
+
1461
+ expect(rendered.container.querySelectorAll("input")).toHaveLength(0);
1462
+ });
1463
+ });
1464
+
1465
+ describe("filters that render no OperatorSelector", () => {
1466
+ test("BooleanFilter renders its yes/no dropdown and no operator trigger", () => {
1467
+ const rendered: ClippedRender = renderInClippingContainer(
1468
+ <BooleanFilter<Resource>
1469
+ filter={BOOLEAN_FILTER}
1470
+ filterData={EMPTY_FILTER_DATA}
1471
+ onFilterChanged={jest.fn()}
1472
+ />,
1473
+ );
1474
+
1475
+ // Positive half: the filter really did render its own control.
1476
+ expect(screen.getByText("Filter by Is Active")).toBeTruthy();
1477
+ expect(getTrigger(rendered.container)).toBeNull();
1478
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
1479
+ });
1480
+
1481
+ test("DropdownFilter renders its dropdown and no operator trigger", () => {
1482
+ const rendered: ClippedRender = renderInClippingContainer(
1483
+ <DropdownFilter<Resource>
1484
+ filter={DROPDOWN_FILTER}
1485
+ filterData={EMPTY_FILTER_DATA}
1486
+ onFilterChanged={jest.fn()}
1487
+ />,
1488
+ );
1489
+
1490
+ expect(screen.getByText("Filter by Status")).toBeTruthy();
1491
+ expect(getTrigger(rendered.container)).toBeNull();
1492
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
1493
+ });
1494
+
1495
+ test("JSONFilter renders its dictionary form and no operator trigger", () => {
1496
+ const rendered: ClippedRender = renderInClippingContainer(
1497
+ <JSONFilter<Resource>
1498
+ filter={JSON_FILTER}
1499
+ filterData={EMPTY_FILTER_DATA}
1500
+ onFilterChanged={jest.fn()}
1501
+ />,
1502
+ );
1503
+
1504
+ expect(screen.getByText("Add Attributes")).toBeTruthy();
1505
+ expect(getTrigger(rendered.container)).toBeNull();
1506
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
1507
+ });
1508
+
1509
+ test("TextFilter renders nothing at all when the filter has dropdown options", () => {
1510
+ const rendered: ClippedRender = renderInClippingContainer(
1511
+ <TextFilter<Resource>
1512
+ filter={DROPDOWN_FILTER}
1513
+ filterData={EMPTY_FILTER_DATA}
1514
+ onFilterChanged={jest.fn()}
1515
+ />,
1516
+ );
1517
+
1518
+ /*
1519
+ * TextFilter bails out entirely for a dropdown-backed filter; FiltersForm
1520
+ * renders DropdownFilter for it instead. So "no operator trigger" here
1521
+ * really does mean "no output".
1522
+ */
1523
+ expect(rendered.container.textContent).toBe("");
1524
+ expect(rendered.container.querySelectorAll("input")).toHaveLength(0);
1525
+ expect(getTrigger(rendered.container)).toBeNull();
1526
+ });
1527
+ });
1528
+
1529
+ describe("OperatorSelector boundaries the filters do not currently reach", () => {
1530
+ test("an empty option list renders a trigger that never opens", () => {
1531
+ const onChange: (value: FilterOperator) => void = jest.fn();
1532
+
1533
+ const rendered: ClippedRender = renderInClippingContainer(
1534
+ <OperatorSelector
1535
+ value={FilterOperator.Contains}
1536
+ options={[]}
1537
+ onChange={onChange}
1538
+ />,
1539
+ );
1540
+
1541
+ const trigger: HTMLButtonElement = requireTrigger(rendered.container);
1542
+
1543
+ expect(trigger.textContent).toContain("contains");
1544
+
1545
+ fireEvent.click(trigger);
1546
+
1547
+ expect(screen.queryByTestId(MENU_TEST_ID)).toBeNull();
1548
+ expect(trigger.getAttribute("aria-expanded")).toBe("false");
1549
+ expect(onChange).not.toHaveBeenCalled();
1550
+ });
1551
+
1552
+ /*
1553
+ * A filter can hold an operator its own option list does not contain — flip
1554
+ * a filter's type from Entity to EntityArray and the locally held operator
1555
+ * outlives the options it came from. The trigger still has to render a
1556
+ * label, and nothing may claim to be selected.
1557
+ *
1558
+ * Where focus lands on open is the keyboard contract and is pinned by
1559
+ * OperatorSelectorKeyboard.test.tsx, so it is deliberately not re-asserted
1560
+ * here.
1561
+ */
1562
+ test("a value outside the option list renders on the trigger but selects nothing", () => {
1563
+ const onChange: (value: FilterOperator) => void = jest.fn();
1564
+
1565
+ const rendered: ClippedRender = renderInClippingContainer(
1566
+ <OperatorSelector
1567
+ value={FilterOperator.IsTrue}
1568
+ options={[
1569
+ FilterOperator.Contains,
1570
+ FilterOperator.DoesNotContain,
1571
+ FilterOperator.IsEmpty,
1572
+ ]}
1573
+ onChange={onChange}
1574
+ />,
1575
+ );
1576
+
1577
+ expect(getTriggerLabel(rendered.container)).toBe("is true");
1578
+
1579
+ const menu: HTMLElement = openMenu(rendered.container);
1580
+
1581
+ expect(getOptionLabels(menu)).toEqual([
1582
+ "contains",
1583
+ "does not contain",
1584
+ "is empty",
1585
+ ]);
1586
+ expect(getSelectedLabels(menu)).toEqual([]);
1587
+
1588
+ // Still a live menu: picking from it reports the operator that was picked.
1589
+ pickOperator(menu, "does not contain");
1590
+
1591
+ expect(onChange).toHaveBeenCalledTimes(1);
1592
+ expect(onChange).toHaveBeenCalledWith(FilterOperator.DoesNotContain);
1593
+ });
1594
+ });
1595
+ });