@oneuptime/common 14.0.3 → 14.0.6

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 (663) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
  3. package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
  4. package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
  5. package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
  6. package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
  7. package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
  8. package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
  9. package/Models/DatabaseModels/Incident.ts +55 -0
  10. package/Models/DatabaseModels/Index.ts +16 -0
  11. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
  12. package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
  13. package/Models/DatabaseModels/TelemetryException.ts +2 -0
  14. package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  18. package/Server/Middleware/MultipartFormData.ts +1 -1
  19. package/Server/Middleware/TelemetryIngest.ts +15 -8
  20. package/Server/Services/AlertReminderRuleService.ts +100 -24
  21. package/Server/Services/DatabaseServerEndpointService.ts +895 -0
  22. package/Server/Services/DatabaseServerFeedService.ts +128 -0
  23. package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
  24. package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
  25. package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
  26. package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
  27. package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
  28. package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
  29. package/Server/Services/DatabaseServerService.ts +4546 -0
  30. package/Server/Services/DatabaseService.ts +14 -8
  31. package/Server/Services/DockerResourceService.ts +43 -2
  32. package/Server/Services/IncidentReminderRuleService.ts +103 -23
  33. package/Server/Services/Index.ts +16 -0
  34. package/Server/Services/InventoryItemService.ts +113 -5
  35. package/Server/Services/IoTDeviceService.ts +145 -37
  36. package/Server/Services/MetricService.ts +1 -1
  37. package/Server/Services/OpenTelemetryIngestService.ts +19 -0
  38. package/Server/Services/PodmanResourceService.ts +43 -2
  39. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
  40. package/Server/Services/ServiceService.ts +1 -1
  41. package/Server/Services/TelemetryUsageBillingService.ts +29 -2
  42. package/Server/Services/UserProjectSsoConsentService.ts +74 -0
  43. package/Server/Services/UserService.ts +14 -1
  44. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
  45. package/Server/Types/Database/QueryHelper.ts +84 -2
  46. package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
  47. package/Server/Utils/AI/Toolbox/ResourceTools.ts +81 -2
  48. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
  49. package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
  50. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
  51. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +192 -9
  52. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  53. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2264 -755
  54. package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
  55. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
  56. package/Server/Utils/Monitor/MonitorResource.ts +47 -11
  57. package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
  58. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
  59. package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
  60. package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
  61. package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
  62. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  63. package/Server/Utils/StartServer.ts +26 -6
  64. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  65. package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
  66. package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
  67. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
  68. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
  69. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
  70. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
  71. package/Server/Utils/Telemetry/Telemetry.ts +16 -0
  72. package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
  73. package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
  74. package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
  75. package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
  76. package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
  77. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
  78. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
  79. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
  80. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
  81. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
  82. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
  83. package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
  84. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
  85. package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
  86. package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
  87. package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
  88. package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
  89. package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
  90. package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
  91. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
  92. package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
  93. package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
  94. package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
  95. package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
  96. package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
  97. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
  98. package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
  99. package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
  100. package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
  101. package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
  102. package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
  103. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
  104. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
  105. package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
  106. package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
  107. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
  108. package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
  109. package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
  110. package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
  111. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
  112. package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
  113. package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
  114. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
  115. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
  116. package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
  117. package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
  118. package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
  119. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
  120. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
  121. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
  122. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
  123. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
  124. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
  125. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
  126. package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
  127. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
  128. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
  129. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
  130. package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
  131. package/Tests/ClassStrings.ts +178 -0
  132. package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
  133. package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
  134. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
  135. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
  136. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  137. package/Tests/RenderedMarkup.ts +518 -0
  138. package/Tests/ResponsiveVisibility.test.ts +490 -4
  139. package/Tests/ResponsiveVisibility.ts +505 -52
  140. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  141. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
  142. package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
  143. package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
  144. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
  145. package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
  146. package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
  147. package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
  148. package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
  149. package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
  150. package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
  151. package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
  152. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
  153. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
  154. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
  155. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
  156. package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
  157. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
  158. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
  159. package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
  160. package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
  161. package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
  162. package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
  163. package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
  164. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
  165. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
  166. package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
  167. package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
  168. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
  169. package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
  170. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  171. package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
  172. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
  173. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
  174. package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
  175. package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
  176. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
  177. package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
  178. package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
  179. package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
  180. package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
  181. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
  182. package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
  183. package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
  184. package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
  185. package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
  186. package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
  187. package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
  188. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
  189. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
  190. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
  191. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
  192. package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
  193. package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
  194. package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
  195. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
  196. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
  197. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
  198. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
  199. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1815 -0
  200. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
  201. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
  202. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
  203. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
  204. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
  205. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
  206. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
  207. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
  208. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
  209. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
  210. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
  211. package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
  212. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
  213. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
  214. package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
  215. package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
  216. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
  217. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
  218. package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
  219. package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
  220. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
  221. package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
  222. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
  223. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
  224. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
  225. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
  226. package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
  227. package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
  228. package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
  229. package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
  230. package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
  231. package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
  232. package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
  233. package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
  234. package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
  235. package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
  236. package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
  237. package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
  238. package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
  239. package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
  240. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
  241. package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
  242. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +292 -0
  243. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +272 -0
  244. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
  245. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
  246. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
  247. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
  248. package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
  249. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
  250. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +202 -0
  251. package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
  252. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
  253. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
  254. package/Tests/UI/Components/Card.test.tsx +93 -2
  255. package/Tests/UI/Components/FeedOptions.test.ts +6 -0
  256. package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
  257. package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
  258. package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
  259. package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
  260. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
  261. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
  262. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  263. package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
  264. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
  265. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
  266. package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
  267. package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
  268. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
  269. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
  270. package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
  271. package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
  272. package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
  273. package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
  274. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +70 -0
  275. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +21 -0
  276. package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
  277. package/Tests/UI/Utils/Navigation.test.ts +102 -0
  278. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
  279. package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
  280. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  281. package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
  282. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
  283. package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
  284. package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
  285. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
  286. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
  287. package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
  288. package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
  289. package/Tests/Utils/ValueFormatter.test.ts +50 -0
  290. package/Tests/tsconfig.json +16 -0
  291. package/Types/AI/AIResourceContext.ts +7 -0
  292. package/Types/BaseDatabase/AggregationType.ts +7 -0
  293. package/Types/Dashboard/DashboardTemplates.ts +22 -8
  294. package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
  295. package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
  296. package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
  297. package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
  298. package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
  299. package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
  300. package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
  301. package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
  302. package/Types/Email/EmailTemplateType.ts +1 -0
  303. package/Types/Monitor/CephAlertTemplates.ts +18 -0
  304. package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
  305. package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
  306. package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
  307. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
  308. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
  309. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
  310. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
  311. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
  312. package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
  313. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
  314. package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
  315. package/Types/Permission.ts +267 -0
  316. package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
  317. package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
  318. package/Types/Rules/RuleRun.ts +12 -0
  319. package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
  320. package/Types/Rum/WebVitals.ts +13 -0
  321. package/Types/Telemetry/EntityType.ts +23 -0
  322. package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
  323. package/Types/Telemetry/ServiceType.ts +8 -0
  324. package/UI/Components/Banner/Banner.tsx +1 -1
  325. package/UI/Components/Card/Card.tsx +1 -1
  326. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
  327. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  328. package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
  329. package/UI/Components/Date/StartAndEndDate.tsx +5 -5
  330. package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
  331. package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
  332. package/UI/Components/Forms/BasicForm.tsx +1 -1
  333. package/UI/Components/Header/Header.tsx +1 -1
  334. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
  335. package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
  336. package/UI/Components/Input/Input.tsx +11 -0
  337. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
  338. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
  339. package/UI/Components/Modal/ModalFooter.tsx +1 -1
  340. package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
  341. package/UI/Components/ModelTable/BaseModelTable.tsx +4 -4
  342. package/UI/Components/Navbar/NavBar.tsx +9 -2
  343. package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
  344. package/UI/Components/Page/Page.tsx +1 -1
  345. package/UI/Components/Pagination/Pagination.tsx +3 -3
  346. package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
  347. package/UI/Components/SideMenu/SideMenu.tsx +4 -1
  348. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
  349. package/UI/Components/StatusPage/ResourceList.tsx +6 -6
  350. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
  351. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
  352. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
  353. package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
  354. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  355. package/UI/Styles/Theme.css +8 -1
  356. package/UI/Utils/Navigation.ts +89 -42
  357. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
  358. package/UI/Utils/TelemetryService.ts +4 -3
  359. package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
  360. package/Utils/LabelRuleImportExport.ts +2 -0
  361. package/Utils/MetricUnitUtil.ts +103 -11
  362. package/Utils/Monitor/MetricValueFormatter.ts +66 -13
  363. package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
  364. package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
  365. package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
  366. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
  367. package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
  368. package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
  369. package/Utils/Telemetry/EntityKey.ts +176 -0
  370. package/Utils/ValueFormatter.ts +16 -0
  371. package/build/dist/Models/DatabaseModels/Alert.js +54 -0
  372. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  373. package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
  374. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
  375. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
  376. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
  377. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
  378. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
  379. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
  380. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
  381. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
  382. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
  383. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
  384. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
  385. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
  386. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
  387. package/build/dist/Models/DatabaseModels/Incident.js +54 -0
  388. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  389. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  390. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  391. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
  393. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
  395. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
  397. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
  398. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
  399. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
  400. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
  401. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
  402. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  403. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  404. package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
  405. package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
  406. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  407. package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
  408. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  409. package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
  410. package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
  411. package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
  412. package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
  413. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
  414. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
  415. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
  416. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
  417. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
  418. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
  419. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
  420. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
  421. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
  422. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
  423. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
  424. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
  425. package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
  426. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
  427. package/build/dist/Server/Services/DatabaseService.js +8 -4
  428. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  429. package/build/dist/Server/Services/DockerResourceService.js +30 -3
  430. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  431. package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
  432. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  433. package/build/dist/Server/Services/Index.js +16 -0
  434. package/build/dist/Server/Services/Index.js.map +1 -1
  435. package/build/dist/Server/Services/InventoryItemService.js +88 -5
  436. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  437. package/build/dist/Server/Services/IoTDeviceService.js +111 -24
  438. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  439. package/build/dist/Server/Services/MetricService.js +1 -1
  440. package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
  441. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  442. package/build/dist/Server/Services/PodmanResourceService.js +30 -3
  443. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  444. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
  445. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  446. package/build/dist/Server/Services/ServiceService.js +1 -1
  447. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  448. package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
  449. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  450. package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
  451. package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
  452. package/build/dist/Server/Services/UserService.js +13 -1
  453. package/build/dist/Server/Services/UserService.js.map +1 -1
  454. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
  455. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  456. package/build/dist/Server/Types/Database/QueryHelper.js +73 -2
  457. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  458. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
  459. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
  460. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +58 -1
  461. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  462. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
  463. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
  464. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
  465. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
  466. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  467. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +135 -14
  468. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  469. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
  470. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  471. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
  472. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  473. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
  474. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  475. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
  476. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  477. package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
  478. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  479. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
  480. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
  481. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
  482. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
  483. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
  484. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
  485. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
  486. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  487. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
  488. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  489. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  490. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  491. package/build/dist/Server/Utils/StartServer.js +20 -4
  492. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  493. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  494. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  495. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
  496. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
  497. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
  498. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  499. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
  500. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  501. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
  502. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  503. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
  504. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  505. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
  506. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
  507. package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
  508. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  509. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
  510. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
  512. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
  513. package/build/dist/Types/AI/AIResourceContext.js +7 -0
  514. package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
  515. package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
  516. package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
  517. package/build/dist/Types/Dashboard/DashboardTemplates.js +22 -8
  518. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  519. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
  520. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
  521. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
  522. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
  523. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
  524. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
  525. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
  526. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
  527. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
  528. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
  529. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
  530. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
  531. package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
  532. package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
  533. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
  534. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
  535. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  536. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  537. package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
  538. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
  539. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
  540. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
  541. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
  542. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  543. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
  544. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
  545. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
  546. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  547. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
  548. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  549. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
  550. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  551. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
  552. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
  553. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
  554. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
  555. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
  556. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
  557. package/build/dist/Types/Permission.js +234 -0
  558. package/build/dist/Types/Permission.js.map +1 -1
  559. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
  560. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  561. package/build/dist/Types/Rules/RuleRun.js +4 -0
  562. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  563. package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
  564. package/build/dist/Types/Rum/WebVitals.js +12 -0
  565. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  566. package/build/dist/Types/Telemetry/EntityType.js +23 -0
  567. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  568. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
  569. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
  570. package/build/dist/Types/Telemetry/ServiceType.js +8 -0
  571. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  572. package/build/dist/UI/Components/Banner/Banner.js +1 -1
  573. package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
  574. package/build/dist/UI/Components/Card/Card.js +1 -1
  575. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  576. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  577. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  578. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  579. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  580. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
  581. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  582. package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
  583. package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
  584. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
  585. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
  586. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
  587. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
  588. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  589. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  590. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  591. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  592. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
  593. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
  594. package/build/dist/UI/Components/Input/Input.js +1 -1
  595. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  596. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
  597. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  598. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  600. package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
  601. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  602. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
  603. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  604. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +4 -4
  605. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  606. package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
  607. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  608. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  609. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  610. package/build/dist/UI/Components/Page/Page.js +1 -1
  611. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  612. package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
  613. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  614. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
  615. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
  616. package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
  617. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  618. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
  619. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
  620. package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
  621. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  622. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  626. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
  627. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
  628. package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
  629. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  630. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  631. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  632. package/build/dist/UI/Utils/Navigation.js +66 -33
  633. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  634. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
  635. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
  636. package/build/dist/UI/Utils/TelemetryService.js +3 -2
  637. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  638. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
  639. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
  640. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  641. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  642. package/build/dist/Utils/MetricUnitUtil.js +77 -13
  643. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  644. package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
  645. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
  646. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
  647. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
  648. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
  649. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
  650. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
  651. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
  652. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
  653. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  654. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
  655. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
  656. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
  657. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
  658. package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
  659. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  660. package/build/dist/Utils/ValueFormatter.js +15 -0
  661. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  662. package/package.json +1 -1
  663. package/tsconfig.tests.json +7 -5
@@ -6,8 +6,10 @@ import CustomCodeMonitoringCriteria from "./Criteria/CustomCodeMonitorCriteria";
6
6
  import IncomingEmailCriteria from "./Criteria/IncomingEmailCriteria";
7
7
  import IncomingRequestCriteria from "./Criteria/IncomingRequestCriteria";
8
8
  import IncomingRequestIncidentGrouping from "./IncomingRequestIncidentGrouping";
9
+ import { IOT_DEVICE_ID_ATTRIBUTE_KEY } from "./IoTDeviceAbsenceSeries";
9
10
  import PerEntityCriteriaFanOut from "./PerEntityCriteriaFanOut";
10
11
  import SSLMonitorCriteria from "./Criteria/SSLMonitorCriteria";
12
+ import CompareCriteria from "./Criteria/CompareCriteria";
11
13
  import ServerMonitorCriteria from "./Criteria/ServerMonitorCriteria";
12
14
  import SyntheticMonitoringCriteria from "./Criteria/SyntheticMonitor";
13
15
  import LogMonitorCriteria from "./Criteria/LogMonitorCriteria";
@@ -49,6 +51,10 @@ import { getProxmoxMetricByMetricName } from "../../../Types/Monitor/ProxmoxMetr
49
51
  import { getVMwareMetricByMetricName } from "../../../Types/Monitor/VMwareMetricCatalog";
50
52
  import { getCephMetricByMetricName } from "../../../Types/Monitor/CephMetricCatalog";
51
53
  import { getDockerSwarmMetricByMetricName } from "../../../Types/Monitor/DockerSwarmMetricCatalog";
54
+ import PlatformMetricUnitUtil from "../../../Utils/Monitor/PlatformMetricUnitUtil";
55
+ import MetricUnitUtil from "../../../Utils/MetricUnitUtil";
56
+ import PlatformResourceIdentity from "../../../Utils/Monitor/PlatformResourceIdentity";
57
+ import MetricFormulaEvaluator from "../../../Utils/Metrics/MetricFormulaEvaluator";
52
58
  export default class MonitorCriteriaEvaluator {
53
59
  static async processMonitorStep(input) {
54
60
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
@@ -208,6 +214,7 @@ export default class MonitorCriteriaEvaluator {
208
214
  monitorStep: input.monitorStep,
209
215
  monitor: input.monitor,
210
216
  criteriaInstance: criteriaInstance,
217
+ perSeriesMatches: perSeriesMatches,
211
218
  });
212
219
  /*
213
220
  * For metric monitors, render the metric identity (name, unit,
@@ -499,6 +506,9 @@ ${contextBlock}
499
506
  labels: entry.labels,
500
507
  rootCause: rootCauseLines.join("\n"),
501
508
  metricContext: (_e = matched[0]) === null || _e === void 0 ? void 0 : _e.context,
509
+ metricContexts: matched.map((r) => {
510
+ return r.context;
511
+ }),
502
512
  });
503
513
  }
504
514
  return matches;
@@ -927,6 +937,10 @@ ${contextBlock}
927
937
  if (input.monitor.monitorType === MonitorType.Podman) {
928
938
  return MonitorCriteriaEvaluator.buildPodmanRootCauseContext(input);
929
939
  }
940
+ // Handle IoT device monitors with fleet / device context
941
+ if (input.monitor.monitorType === MonitorType.IoTDevice) {
942
+ return MonitorCriteriaEvaluator.buildIoTRootCauseContext(input);
943
+ }
930
944
  // Handle Proxmox monitors with resource context
931
945
  if (input.monitor.monitorType === MonitorType.Proxmox) {
932
946
  return MonitorCriteriaEvaluator.buildProxmoxRootCauseContext(input);
@@ -1026,20 +1040,23 @@ ${contextBlock}
1026
1040
  }
1027
1041
  return sections.join("\n");
1028
1042
  }
1029
- static buildMetricRootCauseContext(input) {
1043
+ /*
1044
+ * The first populated metric context across the instance's filters.
1045
+ * Only metric-value filters populate this at evaluation time, so this
1046
+ * effectively returns the context for the filter that ran.
1047
+ */
1048
+ static getMetricCriteriaContext(criteriaInstance) {
1030
1049
  var _a;
1031
- /*
1032
- * Pick the first populated metric context across the instance's filters.
1033
- * Only metric-value filters populate this at evaluation time, so this
1034
- * effectively returns the context for the filter that ran.
1035
- */
1036
- const ctx = (((_a = input.criteriaInstance.data) === null || _a === void 0 ? void 0 : _a.filters) || [])
1050
+ return (((_a = criteriaInstance === null || criteriaInstance === void 0 ? void 0 : criteriaInstance.data) === null || _a === void 0 ? void 0 : _a.filters) || [])
1037
1051
  .map((f) => {
1038
1052
  return f.metricCriteriaContext;
1039
1053
  })
1040
1054
  .find((c) => {
1041
1055
  return Boolean(c);
1042
1056
  });
1057
+ }
1058
+ static buildMetricRootCauseContext(input) {
1059
+ const ctx = MonitorCriteriaEvaluator.getMetricCriteriaContext(input.criteriaInstance);
1043
1060
  if (!ctx) {
1044
1061
  return null;
1045
1062
  }
@@ -1117,11 +1134,16 @@ ${contextBlock}
1117
1134
  unit: ctx.unit,
1118
1135
  unitHeuristicMetricName: unitHeuristicMetricName,
1119
1136
  components: ctx.components || [],
1137
+ seriesLabelKeys: [
1138
+ ...ctx.groupBy,
1139
+ ...Object.keys(ctx.seriesLabels || {}),
1140
+ ],
1120
1141
  })}`);
1121
1142
  }
1122
1143
  const deepLink = MonitorCriteriaEvaluator.buildMetricExplorerDeepLink({
1123
1144
  monitor: input.monitor,
1124
1145
  ctx,
1146
+ monitorStep: input.monitorStep,
1125
1147
  });
1126
1148
  if (deepLink) {
1127
1149
  sections.push(`\n\n[Open metric in dashboard](${deepLink})`);
@@ -1175,6 +1197,41 @@ ${contextBlock}
1175
1197
  * the client-side markdown viewer can localize them to the viewer's
1176
1198
  * timezone (without losing the canonical instant).
1177
1199
  */
1200
+ /**
1201
+ * The attributes worth printing under a breaching sample.
1202
+ *
1203
+ * A grouped monitor's sample does not carry its group-by values at the
1204
+ * top level: the worker nests the datapoint's whole attribute map under
1205
+ * a single `attributes` key. Printed as-is that became
1206
+ * "`attributes`: `[object Object]`" under every sample — the one line
1207
+ * that should have said which host breached. The series' own label keys
1208
+ * are lifted out of it instead; the rest of the datapoint's attributes
1209
+ * (every resource attribute the exporter stamped) are not what the
1210
+ * reader is looking for here.
1211
+ */
1212
+ static flattenSampleAttributes(input) {
1213
+ const flattened = {};
1214
+ for (const key of Object.keys(input.attributes)) {
1215
+ const value = input.attributes[key];
1216
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1217
+ for (const nestedKey of Object.keys(value)) {
1218
+ const nestedValue = value[nestedKey];
1219
+ if (input.seriesLabelKeys.has(nestedKey) &&
1220
+ flattened[nestedKey] === undefined &&
1221
+ nestedValue !== undefined &&
1222
+ nestedValue !== null &&
1223
+ typeof nestedValue !== "object") {
1224
+ flattened[nestedKey] = nestedValue;
1225
+ }
1226
+ }
1227
+ continue;
1228
+ }
1229
+ if (flattened[key] === undefined) {
1230
+ flattened[key] = value;
1231
+ }
1232
+ }
1233
+ return flattened;
1234
+ }
1178
1235
  static formatBreachingSamplesSection(input) {
1179
1236
  const MAX_SAMPLES_SHOWN = 20;
1180
1237
  // Sort chronologically and de-duplicate any accidental repeats
@@ -1186,14 +1243,22 @@ ${contextBlock}
1186
1243
  * Collect attribute keys that appear on any displayed sample, so every
1187
1244
  * item lists the attributes it has in the same order.
1188
1245
  */
1246
+ const seriesLabelKeys = new Set(input.seriesLabelKeys || []);
1247
+ const sampleAttributes = new Map();
1189
1248
  const attrKeySet = new Set();
1190
1249
  for (const s of displayedSamples) {
1191
- for (const k of Object.keys(s.attributes || {})) {
1250
+ const flattened = MonitorCriteriaEvaluator.flattenSampleAttributes({
1251
+ attributes: s.attributes || {},
1252
+ seriesLabelKeys: seriesLabelKeys,
1253
+ });
1254
+ sampleAttributes.set(s, flattened);
1255
+ for (const k of Object.keys(flattened)) {
1192
1256
  attrKeySet.add(k);
1193
1257
  }
1194
1258
  }
1195
1259
  const attrKeys = Array.from(attrKeySet);
1196
1260
  const items = displayedSamples.map((s) => {
1261
+ var _a;
1197
1262
  const details = [];
1198
1263
  /*
1199
1264
  * What each variable of a formula resolved to at the breach time —
@@ -1233,7 +1298,7 @@ ${contextBlock}
1233
1298
  * RootCauseList.code keeps whatever they contain inside the span.
1234
1299
  */
1235
1300
  for (const k of attrKeys) {
1236
- const v = s.attributes[k];
1301
+ const v = (_a = sampleAttributes.get(s)) === null || _a === void 0 ? void 0 : _a[k];
1237
1302
  if (v === undefined || v === null) {
1238
1303
  continue;
1239
1304
  }
@@ -1290,17 +1355,39 @@ ${contextBlock}
1290
1355
  * for its own URL round-trip — so this deep link can never drift
1291
1356
  * from what the explorer parses.
1292
1357
  */
1293
- const queryConfig = {
1294
- metricQueryData: {
1295
- filterData: Object.assign({ metricName: input.ctx.metricName, attributes: MetricExplorerUrl.sanitizeAttributes(input.ctx.filterAttributes) }, (input.ctx.aggregationType
1296
- ? { aggegationType: input.ctx.aggregationType }
1297
- : {})),
1358
+ let queryConfigs = [
1359
+ {
1360
+ metricQueryData: {
1361
+ filterData: Object.assign({ metricName: input.ctx.metricName, attributes: MetricExplorerUrl.sanitizeAttributes(input.ctx.filterAttributes) }, (input.ctx.aggregationType
1362
+ ? { aggegationType: input.ctx.aggregationType }
1363
+ : {})),
1364
+ },
1298
1365
  },
1299
- };
1366
+ ];
1367
+ let formulaConfigs = [];
1368
+ /*
1369
+ * A formula's metricName is its EXPRESSION, not a metric the explorer
1370
+ * can chart — linking it as one opened an empty chart. Open the
1371
+ * step's own queries and formulas instead, which is what the formula
1372
+ * was computed from. With no step config to rebuild it from, no link
1373
+ * beats a dead one.
1374
+ */
1375
+ if (input.ctx.isFormula) {
1376
+ const metricViewConfig = MonitorStep.getMetricsViewConfig(input.monitorStep);
1377
+ if (!metricViewConfig ||
1378
+ !(metricViewConfig.formulaConfigs || []).some((formula) => {
1379
+ var _a;
1380
+ return ((_a = formula.metricAliasData) === null || _a === void 0 ? void 0 : _a.metricVariable) === input.ctx.alias;
1381
+ })) {
1382
+ return null;
1383
+ }
1384
+ queryConfigs = metricViewConfig.queryConfigs || [];
1385
+ formulaConfigs = metricViewConfig.formulaConfigs || [];
1386
+ }
1300
1387
  const breachWindow = MonitorCriteriaEvaluator.getMetricBreachExplorerWindow(input.ctx);
1301
1388
  const urlParams = MetricExplorerUrl.buildQueryParamsFromMetricViewData({
1302
- queryConfigs: [queryConfig],
1303
- formulaConfigs: [],
1389
+ queryConfigs: queryConfigs,
1390
+ formulaConfigs: formulaConfigs,
1304
1391
  startAndEndDate: new InBetween(breachWindow.startTime, breachWindow.endTime),
1305
1392
  });
1306
1393
  const params = new URLSearchParams();
@@ -1425,7 +1512,8 @@ ${contextBlock}
1425
1512
  return ` _(scope partially carried — not applied: ${dropped.join(", ")})_`;
1426
1513
  }
1427
1514
  /**
1428
- * The unit for a value in a per-platform "Affected Resources" list.
1515
+ * The unit for a value in a per-platform "Affected Resources" list built
1516
+ * from the worker's raw datapoint scan.
1429
1517
  *
1430
1518
  * These rows are NOT the same numbers as the criteria's breaching
1431
1519
  * samples: the worker collects them with a raw `MetricService.findBy`
@@ -1435,50 +1523,632 @@ ${contextBlock}
1435
1523
  * query's legendUnit would therefore attach a confidently wrong unit to
1436
1524
  * a correct number.
1437
1525
  *
1438
- * The catalog is the right source precisely because it also declares
1526
+ * The catalog is the first source precisely because it also declares
1439
1527
  * the native unit. It is the same lookup, on the same metric name, that
1440
1528
  * the worker already ran to resolve `metricFriendlyName`, so the unit
1441
1529
  * shown here can never disagree with the metric name shown beside it.
1530
+ * The unit the worker recorded on the breakdown — the one the exporter
1531
+ * declared — covers every metric the catalog does not know, which is
1532
+ * how k8s.node.allocatable_memory reads "258 GB" instead of the bare
1533
+ * "257760964608" it used to.
1442
1534
  */
1443
1535
  static getPlatformMetricUnit(input) {
1536
+ /*
1537
+ * The vcenter receiver's utilization metrics are already 0–100
1538
+ * percentages (unit "%"), never [0, 1] ratios — the catalog says "%"
1539
+ * and the formatter renders it as-is, so 92.5 reads "92.50%" and
1540
+ * never "9250.00%".
1541
+ */
1542
+ return (PlatformMetricUnitUtil.getCatalogUnit({
1543
+ platform: input.platform,
1544
+ metricName: input.metricName,
1545
+ }) ||
1546
+ input.metricUnit ||
1547
+ undefined);
1548
+ }
1549
+ /**
1550
+ * Render the value of one resource from the worker's raw scan, in the
1551
+ * unit getPlatformMetricUnit resolves. Unemphasised: the list bolds it
1552
+ * (it is the one number on the line a reader scans for), a sentence
1553
+ * may not.
1554
+ */
1555
+ static formatPlatformMetricValue(input) {
1556
+ return MetricValueFormatter.format({
1557
+ value: input.value,
1558
+ unit: MonitorCriteriaEvaluator.getPlatformMetricUnit({
1559
+ platform: input.platform,
1560
+ metricName: input.metricName,
1561
+ metricUnit: input.metricUnit,
1562
+ }),
1563
+ metricName: input.metricName,
1564
+ });
1565
+ }
1566
+ /**
1567
+ * The catalog's human name for a platform metric, or undefined when the
1568
+ * catalog does not know it.
1569
+ */
1570
+ static getPlatformMetricFriendlyName(input) {
1444
1571
  var _a, _b, _c, _d, _e;
1445
1572
  switch (input.platform) {
1446
1573
  case "kubernetes":
1447
- return (_a = getKubernetesMetricByMetricName(input.metricName)) === null || _a === void 0 ? void 0 : _a.unit;
1574
+ return (_a = getKubernetesMetricByMetricName(input.metricName)) === null || _a === void 0 ? void 0 : _a.friendlyName;
1448
1575
  case "proxmox":
1449
- return (_b = getProxmoxMetricByMetricName(input.metricName)) === null || _b === void 0 ? void 0 : _b.unit;
1576
+ return (_b = getProxmoxMetricByMetricName(input.metricName)) === null || _b === void 0 ? void 0 : _b.friendlyName;
1450
1577
  case "vmware":
1451
- /*
1452
- * The vcenter receiver's utilization metrics are already 0–100
1453
- * percentages (unit "%"), never [0, 1] ratios — the catalog
1454
- * says "%" and the formatter renders it as-is, so 92.5 reads
1455
- * "92.50%" and never "9250.00%".
1456
- */
1457
- return (_c = getVMwareMetricByMetricName(input.metricName)) === null || _c === void 0 ? void 0 : _c.unit;
1578
+ return (_c = getVMwareMetricByMetricName(input.metricName)) === null || _c === void 0 ? void 0 : _c.friendlyName;
1458
1579
  case "dockerSwarm":
1459
- return (_d = getDockerSwarmMetricByMetricName(input.metricName)) === null || _d === void 0 ? void 0 : _d.unit;
1580
+ return (_d = getDockerSwarmMetricByMetricName(input.metricName)) === null || _d === void 0 ? void 0 : _d.friendlyName;
1460
1581
  case "ceph":
1461
- return (_e = getCephMetricByMetricName(input.metricName)) === null || _e === void 0 ? void 0 : _e.unit;
1582
+ return (_e = getCephMetricByMetricName(input.metricName)) === null || _e === void 0 ? void 0 : _e.friendlyName;
1462
1583
  default:
1463
1584
  return undefined;
1464
1585
  }
1465
1586
  }
1466
1587
  /**
1467
- * Render the value of one resource in a platform "Affected Resources"
1468
- * list.
1588
+ * "Node Memory Usage (`k8s.node.memory.usage`)", or just the code-quoted
1589
+ * name when there is no friendlier one. The old line always printed
1590
+ * both, which is how the email came to read
1591
+ * "k8s.node.allocatable_memory (`k8s.node.allocatable_memory`)".
1592
+ */
1593
+ static describePlatformMetricName(input) {
1594
+ const friendlyName = (input.friendlyName || "").trim();
1595
+ if (!friendlyName || friendlyName === input.metricName) {
1596
+ return `\`${input.metricName}\``;
1597
+ }
1598
+ return `${friendlyName} (\`${input.metricName}\`)`;
1599
+ }
1600
+ /**
1601
+ * What the matched criteria actually measured: the query or formula its
1602
+ * metric-value filter points at, resolved the same way
1603
+ * MetricMonitorCriteria resolves it (alias match, falling back to the
1604
+ * first query when the alias names nothing).
1469
1605
  *
1470
- * Bold, because that is how the breakdown has always drawn the value —
1471
- * it is the one number on the line a reader scans for.
1606
+ * Returns null when the criteria has no metric-value filter — the
1607
+ * platform context then falls back to describing the breakdown, as it
1608
+ * always has.
1472
1609
  */
1473
- static formatPlatformResourceValue(input) {
1474
- return `**${MetricValueFormatter.format({
1475
- value: input.value,
1476
- unit: MonitorCriteriaEvaluator.getPlatformMetricUnit({
1477
- platform: input.platform,
1478
- metricName: input.metricName,
1610
+ static resolveCriteriaMetricTarget(input) {
1611
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1612
+ /*
1613
+ * The step's own config, exactly as MetricMonitorCriteria reads it,
1614
+ * so the target named here is the one the criteria was evaluated on.
1615
+ */
1616
+ const stepConfig = MonitorStep.getMetricsViewConfig(input.monitorStep);
1617
+ const metricViewConfig = stepConfig && ((_a = stepConfig.queryConfigs) === null || _a === void 0 ? void 0 : _a.length) > 0
1618
+ ? stepConfig
1619
+ : input.metricResponse.metricViewConfig || stepConfig;
1620
+ const metricFilters = (((_c = (_b = input.criteriaInstance) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.filters) || []).filter((f) => {
1621
+ return f.checkOn === CheckOn.MetricValue;
1622
+ });
1623
+ if (metricFilters.length === 0) {
1624
+ return null;
1625
+ }
1626
+ /*
1627
+ * The filter that actually fired, when the criteria has several. An
1628
+ * "Any" criteria (ceph-pg-damaged: PG_DAMAGED > 0 OR OSD_SCRUB_ERRORS
1629
+ * > 0) is described — and its list taken from — whichever of them
1630
+ * breached. Following the first filter instead found no scan for an
1631
+ * inactive PG_DAMAGED check and dropped the list the scrub errors had.
1632
+ * The evaluation leaves each filter's context on it; one with
1633
+ * breaching samples is one that fired.
1634
+ */
1635
+ const firedFilter = metricFilters.find((f) => {
1636
+ const context = f.metricCriteriaContext;
1637
+ return Boolean(((context === null || context === void 0 ? void 0 : context.breachingSamples) && context.breachingSamples.length > 0) ||
1638
+ (context === null || context === void 0 ? void 0 : context.breachingSample));
1639
+ });
1640
+ const filter = firedFilter ||
1641
+ metricFilters.find((f) => {
1642
+ var _a;
1643
+ return Boolean((_a = f.metricMonitorOptions) === null || _a === void 0 ? void 0 : _a.metricAlias);
1644
+ }) ||
1645
+ metricFilters[0];
1646
+ const thresholdUnit = ((_d = filter.metricMonitorOptions) === null || _d === void 0 ? void 0 : _d.thresholdUnit) || undefined;
1647
+ const alias = ((_e = filter.metricMonitorOptions) === null || _e === void 0 ? void 0 : _e.metricAlias) || "";
1648
+ const queryConfigs = (metricViewConfig === null || metricViewConfig === void 0 ? void 0 : metricViewConfig.queryConfigs) || [];
1649
+ const formulaConfigs = (metricViewConfig === null || metricViewConfig === void 0 ? void 0 : metricViewConfig.formulaConfigs) || [];
1650
+ const aliasOf = (config) => {
1651
+ var _a;
1652
+ return ((_a = config.metricAliasData) === null || _a === void 0 ? void 0 : _a.metricVariable) || "";
1653
+ };
1654
+ const query = alias
1655
+ ? queryConfigs.find((q) => {
1656
+ return aliasOf(q) === alias;
1657
+ })
1658
+ : undefined;
1659
+ const formula = !query && alias
1660
+ ? formulaConfigs.find((f) => {
1661
+ return aliasOf(f) === alias;
1662
+ })
1663
+ : undefined;
1664
+ if (formula) {
1665
+ const expression = ((_f = formula.metricFormulaData) === null || _f === void 0 ? void 0 : _f.metricFormula) || "";
1666
+ const components = MetricFormulaEvaluator.getReferencedVariables(expression).map((variable) => {
1667
+ var _a, _b;
1668
+ const componentQuery = queryConfigs.find((q) => {
1669
+ return aliasOf(q).toLowerCase() === variable.toLowerCase();
1670
+ });
1671
+ return {
1672
+ alias: componentQuery ? aliasOf(componentQuery) : variable,
1673
+ metricName: ((_b = (_a = componentQuery === null || componentQuery === void 0 ? void 0 : componentQuery.metricQueryData) === null || _a === void 0 ? void 0 : _a.filterData) === null || _b === void 0 ? void 0 : _b.metricName) || undefined,
1674
+ };
1675
+ });
1676
+ return {
1677
+ alias: alias,
1678
+ isFormula: true,
1679
+ comparisonUnit: thresholdUnit || ((_g = formula.metricAliasData) === null || _g === void 0 ? void 0 : _g.legendUnit) || undefined,
1680
+ metricName: undefined,
1681
+ displayName: MetricMonitorCriteria.getAliasDisplayName({
1682
+ aliasData: formula.metricAliasData,
1683
+ metricAlias: alias,
1684
+ }),
1685
+ formulaExpression: expression || undefined,
1686
+ components: components,
1687
+ };
1688
+ }
1689
+ const matchedQuery = query || queryConfigs[0];
1690
+ if (!matchedQuery) {
1691
+ return null;
1692
+ }
1693
+ return {
1694
+ alias: aliasOf(matchedQuery) || alias,
1695
+ isFormula: false,
1696
+ comparisonUnit: thresholdUnit || ((_h = matchedQuery.metricAliasData) === null || _h === void 0 ? void 0 : _h.legendUnit) || undefined,
1697
+ metricName: ((_k = (_j = matchedQuery.metricQueryData) === null || _j === void 0 ? void 0 : _j.filterData) === null || _k === void 0 ? void 0 : _k.metricName) || undefined,
1698
+ displayName: MetricMonitorCriteria.getAliasDisplayName({
1699
+ aliasData: matchedQuery.metricAliasData,
1700
+ metricAlias: aliasOf(matchedQuery) || alias,
1479
1701
  }),
1480
- metricName: input.metricName,
1481
- })}**`;
1702
+ formulaExpression: undefined,
1703
+ components: [],
1704
+ };
1705
+ }
1706
+ /**
1707
+ * The metric the platform's root-cause analysis should reason about. For
1708
+ * a plain query that is the query's metric. A formula has no metric name
1709
+ * of its own, so it is analysed as its first query component — the
1710
+ * numerator of every ratio template (k8s.node.memory.usage for
1711
+ * "used ÷ allocatable"), which is what the analysis branches key on.
1712
+ */
1713
+ static getAnalysisMetricName(target) {
1714
+ var _a;
1715
+ if (!target) {
1716
+ return undefined;
1717
+ }
1718
+ if (!target.isFormula) {
1719
+ return target.metricName;
1720
+ }
1721
+ return (_a = target.components.find((component) => {
1722
+ return Boolean(component.metricName);
1723
+ })) === null || _a === void 0 ? void 0 : _a.metricName;
1724
+ }
1725
+ /**
1726
+ * The "- Metric:" lines of a platform's details block.
1727
+ *
1728
+ * They name what the criteria compared. A formula is named by its
1729
+ * legend and spelled out with the metric behind each variable, because
1730
+ * "Node Memory Utilization (%)" is what the threshold was about and the
1731
+ * two metrics are what an engineer greps for. With no criteria to go on,
1732
+ * the breakdown's own metric is named, as before.
1733
+ */
1734
+ static describeCriteriaMetric(input) {
1735
+ const target = input.target;
1736
+ const describeMetric = (metricName) => {
1737
+ var _a;
1738
+ const breakdownFriendlyName = ((_a = input.breakdown) === null || _a === void 0 ? void 0 : _a.metricName) === metricName
1739
+ ? input.breakdown.metricFriendlyName
1740
+ : undefined;
1741
+ return MonitorCriteriaEvaluator.describePlatformMetricName({
1742
+ metricName: metricName,
1743
+ friendlyName: breakdownFriendlyName ||
1744
+ MonitorCriteriaEvaluator.getPlatformMetricFriendlyName({
1745
+ platform: input.platform,
1746
+ metricName: metricName,
1747
+ }),
1748
+ });
1749
+ };
1750
+ if (target === null || target === void 0 ? void 0 : target.isFormula) {
1751
+ const lines = [
1752
+ `- Metric: ${target.displayName || `\`${target.alias}\``}`,
1753
+ ];
1754
+ if (target.formulaExpression) {
1755
+ lines.push(`- Formula: \`${target.formulaExpression}\``);
1756
+ }
1757
+ for (const component of target.components) {
1758
+ if (component.metricName) {
1759
+ lines.push(` - \`${component.alias}\` = ${describeMetric(component.metricName)}`);
1760
+ }
1761
+ }
1762
+ return lines;
1763
+ }
1764
+ if (target === null || target === void 0 ? void 0 : target.metricName) {
1765
+ return [`- Metric: ${describeMetric(target.metricName)}`];
1766
+ }
1767
+ if (input.breakdown) {
1768
+ return [
1769
+ `- Metric: ${MonitorCriteriaEvaluator.describePlatformMetricName({
1770
+ metricName: input.breakdown.metricName,
1771
+ friendlyName: input.breakdown.metricFriendlyName,
1772
+ })}`,
1773
+ ];
1774
+ }
1775
+ if (input.fallbackMetricName) {
1776
+ return [`- Metric: \`${input.fallbackMetricName}\``];
1777
+ }
1778
+ return [];
1779
+ }
1780
+ /**
1781
+ * Pick the raw-scan breakdown that measured what the criteria compared.
1782
+ *
1783
+ * The worker returns one breakdown per query, tagged with the query's
1784
+ * alias. Only the one scanned for the criteria's own query may be shown:
1785
+ * a sibling query's scan is a different quantity (allocatable bytes
1786
+ * under a utilization threshold), and a formula has no raw scan at all
1787
+ * — its per-resource values only exist per series.
1788
+ *
1789
+ * A breakdown built before the tag existed is trusted the way it always
1790
+ * was, unless the criteria targets a formula.
1791
+ */
1792
+ static selectPlatformBreakdown(input) {
1793
+ const candidates = input.breakdowns && input.breakdowns.length > 0
1794
+ ? input.breakdowns
1795
+ : input.legacyBreakdown
1796
+ ? [input.legacyBreakdown]
1797
+ : [];
1798
+ if (candidates.length === 0) {
1799
+ return undefined;
1800
+ }
1801
+ const target = input.target;
1802
+ if (!target) {
1803
+ return candidates.length === 1 ? candidates[0] : undefined;
1804
+ }
1805
+ if (target.isFormula) {
1806
+ return undefined;
1807
+ }
1808
+ const byAlias = candidates.find((b) => {
1809
+ return (Boolean(b.metricAlias) &&
1810
+ b.metricAlias.toLowerCase() === target.alias.toLowerCase());
1811
+ });
1812
+ if (byAlias) {
1813
+ return byAlias;
1814
+ }
1815
+ return candidates.find((b) => {
1816
+ return !b.metricAlias;
1817
+ });
1818
+ }
1819
+ /**
1820
+ * The "Affected Resources" rows of a GROUPED platform monitor: one per
1821
+ * series that satisfied the matched criteria.
1822
+ *
1823
+ * This is the list the criteria actually produced. Each row is valued
1824
+ * with its series' worst breaching sample, in the same display unit —
1825
+ * and formatted by the same formatter — as the "Filter Conditions Met"
1826
+ * sentence above it, so the node the sentence says "ranged from 86.26%
1827
+ * to 88.82%" is listed at 88.82%, not at its 257760964608 bytes of
1828
+ * allocatable memory. Series that did not breach are not listed at all.
1829
+ *
1830
+ * The resource is named from the series' group-by labels, the same
1831
+ * attribute keys the worker's raw scan reads, then given whatever
1832
+ * context those labels imply but do not carry (a pod's node and
1833
+ * workload) from one of the series' own datapoints.
1834
+ */
1835
+ static buildSeriesAffectedRows(input) {
1836
+ const targetAlias = (input.targetAlias || "").toLowerCase();
1837
+ const rows = input.perSeriesMatches.map((match) => {
1838
+ var _a;
1839
+ /*
1840
+ * Value the row with the filter the list is about. Under an "Any"
1841
+ * criteria a series may have matched on another filter only; its
1842
+ * value is then a different metric in a different unit, so it is
1843
+ * shown with that metric named and never ranked against the rest.
1844
+ */
1845
+ const matchedContexts = match.metricContexts && match.metricContexts.length > 0
1846
+ ? match.metricContexts
1847
+ : match.metricContext
1848
+ ? [match.metricContext]
1849
+ : [];
1850
+ const targetContext = targetAlias
1851
+ ? matchedContexts.find((c) => {
1852
+ return (c.alias || "").toLowerCase() === targetAlias;
1853
+ })
1854
+ : matchedContexts[0];
1855
+ const context = targetContext || matchedContexts[0];
1856
+ const valueNote = !targetContext && context
1857
+ ? context.displayName || `\`${context.alias}\``
1858
+ : undefined;
1859
+ const seriesIdentity = input.toIdentity(PlatformResourceIdentity.withBothAttributeSpellings(match.labels || {}));
1860
+ const sample = ((_a = context === null || context === void 0 ? void 0 : context.breachingSamples) === null || _a === void 0 ? void 0 : _a[0]) || (context === null || context === void 0 ? void 0 : context.breachingSample);
1861
+ const sampleAttributes = MonitorCriteriaEvaluator.getSampleRawAttributes(sample);
1862
+ /*
1863
+ * The breaching sample's own datapoint first; then a datapoint of
1864
+ * each of the series' queries, because a FORMULA's sample carries
1865
+ * only the group-by labels — a Proxmox ratio over `id` would
1866
+ * otherwise never learn whether the id is a node or a guest.
1867
+ * with<Platform>Context only ever borrows what the series' labels
1868
+ * guarantee is shared, so a row from any of them is safe.
1869
+ */
1870
+ const contexts = [
1871
+ ...(sampleAttributes ? [sampleAttributes] : []),
1872
+ ...input.seriesContextAttributes(match.fingerprint),
1873
+ ];
1874
+ const identity = contexts.reduce((current, attributes) => {
1875
+ return input.withContext(current, input.toIdentity(PlatformResourceIdentity.withBothAttributeSpellings(attributes)));
1876
+ }, seriesIdentity);
1877
+ const value = MonitorCriteriaEvaluator.getWorstValue({
1878
+ context: context,
1879
+ worstIsLowest: input.worstIsLowest,
1880
+ });
1881
+ const seriesLabels = !input.namesObject(identity)
1882
+ ? MonitorCriteriaEvaluator.getNonEmptyLabels(match.labels)
1883
+ : undefined;
1884
+ return Object.assign(Object.assign(Object.assign({ identity: identity, value: value }, (valueNote ? { valueNote: valueNote } : {})), (seriesLabels ? { seriesLabels: seriesLabels } : {})), { formattedValue: value === null
1885
+ ? "no data"
1886
+ : MetricValueFormatter.format({
1887
+ value: value,
1888
+ unit: context === null || context === void 0 ? void 0 : context.unit,
1889
+ metricName: MonitorCriteriaEvaluator.metricNameForUnitHeuristics({
1890
+ metricName: context === null || context === void 0 ? void 0 : context.metricName,
1891
+ isFormula: context === null || context === void 0 ? void 0 : context.isFormula,
1892
+ }),
1893
+ }) });
1894
+ });
1895
+ return MonitorCriteriaEvaluator.sortAffectedRows({
1896
+ rows: rows,
1897
+ worstIsLowest: input.worstIsLowest,
1898
+ });
1899
+ }
1900
+ /**
1901
+ * The "Affected Resources" rows of an UNGROUPED platform monitor, from
1902
+ * the worker's raw datapoint scan of the criteria's own query: every
1903
+ * resource the matched criteria's direction says breached, worst first.
1904
+ */
1905
+ static buildScanAffectedRows(input) {
1906
+ /*
1907
+ * The scan's values are in the metric's own unit, but the criteria
1908
+ * compared its threshold in the filter's threshold unit (or the
1909
+ * query's legendUnit). Judge each row in that unit — a "< 1 min"
1910
+ * uptime criteria must not test 30 seconds against 1 — and show it in
1911
+ * its own.
1912
+ */
1913
+ const valueUnit = PlatformMetricUnitUtil.getMetricUnit({
1914
+ platform: input.platform,
1915
+ metricName: input.breakdown.metricName,
1916
+ declaredUnit: input.breakdown.metricUnit,
1917
+ });
1918
+ const toComparisonUnit = (value) => {
1919
+ if (!valueUnit ||
1920
+ !input.comparisonUnit ||
1921
+ valueUnit === input.comparisonUnit) {
1922
+ return value;
1923
+ }
1924
+ return MetricUnitUtil.convertToMetricUnit({
1925
+ value: value,
1926
+ fromUnit: valueUnit,
1927
+ metricUnit: input.comparisonUnit,
1928
+ });
1929
+ };
1930
+ const collect = (matches) => {
1931
+ return MonitorCriteriaEvaluator.collectScanRows({
1932
+ platform: input.platform,
1933
+ breakdown: input.breakdown,
1934
+ breach: input.breach,
1935
+ matches: matches,
1936
+ toComparisonUnit: toComparisonUnit,
1937
+ });
1938
+ };
1939
+ let rows = collect(input.breach.matches);
1940
+ if (rows.length === 0 && input.breach.fallbackMatches) {
1941
+ rows = collect(input.breach.fallbackMatches);
1942
+ }
1943
+ return MonitorCriteriaEvaluator.sortAffectedRows({
1944
+ rows: rows,
1945
+ worstIsLowest: input.breach.worstIsLowest,
1946
+ });
1947
+ }
1948
+ /*
1949
+ * One pass of buildScanAffectedRows: the resources `matches` accepts,
1950
+ * each judged in the comparison unit and shown in its own.
1951
+ */
1952
+ static collectScanRows(input) {
1953
+ var _a;
1954
+ const toComparisonUnit = input.toComparisonUnit;
1955
+ const rows = [];
1956
+ for (const resource of input.breakdown.affectedResources) {
1957
+ const highest = resource.metricValue;
1958
+ const lowest = (_a = resource.lowestMetricValue) !== null && _a !== void 0 ? _a : resource.metricValue;
1959
+ /*
1960
+ * A fall criteria breached on the resource's lowest sample in the
1961
+ * window, not its highest.
1962
+ */
1963
+ const end = input.breach.pickEnd
1964
+ ? input.breach.pickEnd({
1965
+ highest: toComparisonUnit(highest),
1966
+ lowest: toComparisonUnit(lowest),
1967
+ })
1968
+ : input.breach.worstIsLowest
1969
+ ? "lowest"
1970
+ : "highest";
1971
+ const value = end === "lowest" ? lowest : highest;
1972
+ if (!input.matches(toComparisonUnit(value))) {
1973
+ continue;
1974
+ }
1975
+ rows.push({
1976
+ identity: resource,
1977
+ value: value,
1978
+ formattedValue: MonitorCriteriaEvaluator.formatPlatformMetricValue({
1979
+ platform: input.platform,
1980
+ metricName: input.breakdown.metricName,
1981
+ metricUnit: input.breakdown.metricUnit,
1982
+ value: value,
1983
+ }),
1984
+ });
1985
+ }
1986
+ return rows;
1987
+ }
1988
+ /*
1989
+ * Worst first. Rows valued by another filter (valueNote) follow, in the
1990
+ * order they came — their numbers are in another unit and cannot be
1991
+ * ranked against these — and a series with no value (a no-data trigger)
1992
+ * comes last.
1993
+ */
1994
+ static sortAffectedRows(input) {
1995
+ const group = (row) => {
1996
+ if (row.value === null) {
1997
+ return 2;
1998
+ }
1999
+ return row.valueNote ? 1 : 0;
2000
+ };
2001
+ return [...input.rows].sort((a, b) => {
2002
+ if (group(a) !== group(b)) {
2003
+ return group(a) - group(b);
2004
+ }
2005
+ if (group(a) !== 0 || a.value === null || b.value === null) {
2006
+ return 0;
2007
+ }
2008
+ return input.worstIsLowest ? a.value - b.value : b.value - a.value;
2009
+ });
2010
+ }
2011
+ /*
2012
+ * The value cell of a platform list entry: bold, and — for a row valued
2013
+ * by another filter than the list's — followed by that filter's metric.
2014
+ */
2015
+ static renderAffectedRowValue(row) {
2016
+ return row.valueNote
2017
+ ? `**${row.formattedValue}** (${row.valueNote})`
2018
+ : `**${row.formattedValue}**`;
2019
+ }
2020
+ static getNonEmptyLabels(labels) {
2021
+ const result = {};
2022
+ for (const key of Object.keys(labels || {})) {
2023
+ const value = (labels || {})[key];
2024
+ if (value !== undefined && value !== null && value !== "") {
2025
+ result[key] = value;
2026
+ }
2027
+ }
2028
+ return Object.keys(result).length > 0 ? result : undefined;
2029
+ }
2030
+ /*
2031
+ * A list entry for a series whose labels name no platform object: titled
2032
+ * "Series" and named by its labels (their stored `resource.` prefix
2033
+ * dropped), so rows stay distinguishable.
2034
+ */
2035
+ static getSeriesLabelEntry(input) {
2036
+ const name = Object.keys(input.labels)
2037
+ .map((key) => {
2038
+ const displayKey = key.startsWith("resource.")
2039
+ ? key.substring("resource.".length)
2040
+ : key;
2041
+ return AffectedResourceList.code(`${displayKey}=${String(input.labels[key])}`);
2042
+ })
2043
+ .join(", ");
2044
+ return {
2045
+ kind: "Series",
2046
+ name: name,
2047
+ value: input.value,
2048
+ details: [],
2049
+ };
2050
+ }
2051
+ /**
2052
+ * A series' worst value in the display unit: its worst breaching sample,
2053
+ * or — when the criteria compared an aggregate no single sample crossed
2054
+ * (a Sum) — its worst sample in the window. Null for a series that
2055
+ * matched with no samples at all (the no-data policy).
2056
+ */
2057
+ static getWorstValue(input) {
2058
+ var _a, _b, _c;
2059
+ const breachingValues = (((_a = input.context) === null || _a === void 0 ? void 0 : _a.breachingSamples) ||
2060
+ (((_b = input.context) === null || _b === void 0 ? void 0 : _b.breachingSample) ? [input.context.breachingSample] : []))
2061
+ .map((sample) => {
2062
+ return sample.value;
2063
+ })
2064
+ .filter((value) => {
2065
+ return typeof value === "number" && Number.isFinite(value);
2066
+ });
2067
+ if (breachingValues.length > 0) {
2068
+ return input.worstIsLowest
2069
+ ? Math.min(...breachingValues)
2070
+ : Math.max(...breachingValues);
2071
+ }
2072
+ const range = (_c = input.context) === null || _c === void 0 ? void 0 : _c.sampleValueRange;
2073
+ if (range) {
2074
+ return input.worstIsLowest ? range.min : range.max;
2075
+ }
2076
+ return null;
2077
+ }
2078
+ /**
2079
+ * One datapoint's attributes from each QUERY slot of a series (the
2080
+ * formula slots after them carry only group-by labels). Per-series query
2081
+ * rows keep their datapoint's full attribute map under `attributes`.
2082
+ */
2083
+ static getSeriesQueryRowAttributes(input) {
2084
+ var _a, _b;
2085
+ const series = (input.seriesBreakdown || []).find((s) => {
2086
+ return s.fingerprint === input.fingerprint;
2087
+ });
2088
+ if (!series) {
2089
+ return [];
2090
+ }
2091
+ const result = [];
2092
+ for (let i = 0; i < input.queryCount; i++) {
2093
+ const row = (_b = (_a = series.aggregatedResults[i]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b[0];
2094
+ const attributes = row === null || row === void 0 ? void 0 : row["attributes"];
2095
+ if (attributes && typeof attributes === "object") {
2096
+ result.push(attributes);
2097
+ }
2098
+ }
2099
+ return result;
2100
+ }
2101
+ /**
2102
+ * The raw datapoint attributes a breaching sample carries. A sample from
2103
+ * a per-series query row keeps the datapoint's full attribute map under
2104
+ * `attributes`; a formula row carries only its group attributes there.
2105
+ */
2106
+ static getSampleRawAttributes(sample) {
2107
+ if (!sample || !sample.attributes) {
2108
+ return undefined;
2109
+ }
2110
+ const nested = sample.attributes["attributes"];
2111
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
2112
+ return nested;
2113
+ }
2114
+ return sample.attributes;
2115
+ }
2116
+ /**
2117
+ * The rows of a platform's "Affected Resources" list for the matched
2118
+ * criteria, from whichever source measured what the criteria compared:
2119
+ * the per-series matches of a grouped monitor, else the raw scan of the
2120
+ * criteria's own query. Null when neither applies (an ungrouped formula
2121
+ * has no per-resource values to list).
2122
+ */
2123
+ static getPlatformAffectedRows(input) {
2124
+ var _a, _b, _c, _d;
2125
+ if (input.perSeriesMatches && input.perSeriesMatches.length > 0) {
2126
+ const queryCount = ((_b = (_a = MonitorStep.getMetricsViewConfig(input.monitorStep)) === null || _a === void 0 ? void 0 : _a.queryConfigs) === null || _b === void 0 ? void 0 : _b.length) || 0;
2127
+ return MonitorCriteriaEvaluator.buildSeriesAffectedRows({
2128
+ perSeriesMatches: input.perSeriesMatches,
2129
+ worstIsLowest: input.breach.worstIsLowest,
2130
+ toIdentity: input.toIdentity,
2131
+ withContext: input.withContext,
2132
+ namesObject: input.namesObject,
2133
+ targetAlias: (_c = input.target) === null || _c === void 0 ? void 0 : _c.alias,
2134
+ seriesContextAttributes: (fingerprint) => {
2135
+ return MonitorCriteriaEvaluator.getSeriesQueryRowAttributes({
2136
+ seriesBreakdown: input.metricResponse.seriesBreakdown,
2137
+ fingerprint: fingerprint,
2138
+ queryCount: queryCount,
2139
+ });
2140
+ },
2141
+ });
2142
+ }
2143
+ if (!input.breakdown || input.breakdown.affectedResources.length === 0) {
2144
+ return null;
2145
+ }
2146
+ return MonitorCriteriaEvaluator.buildScanAffectedRows({
2147
+ platform: input.platform,
2148
+ breakdown: input.breakdown,
2149
+ breach: input.breach,
2150
+ comparisonUnit: (_d = input.target) === null || _d === void 0 ? void 0 : _d.comparisonUnit,
2151
+ });
1482
2152
  }
1483
2153
  /*
1484
2154
  * One Kubernetes resource as an "Affected Resources" list item.
@@ -1553,247 +2223,372 @@ ${contextBlock}
1553
2223
  };
1554
2224
  }
1555
2225
  static async buildKubernetesRootCauseContext(input) {
1556
- var _a;
2226
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1557
2227
  const metricResponse = input.dataToProcess;
1558
- const breakdown = metricResponse.kubernetesResourceBreakdown;
1559
- if (!breakdown) {
2228
+ const target = MonitorCriteriaEvaluator.resolveCriteriaMetricTarget({
2229
+ criteriaInstance: input.criteriaInstance,
2230
+ monitorStep: input.monitorStep,
2231
+ metricResponse: metricResponse,
2232
+ });
2233
+ const breakdown = MonitorCriteriaEvaluator.selectPlatformBreakdown({
2234
+ breakdowns: metricResponse.kubernetesResourceBreakdowns,
2235
+ legacyBreakdown: metricResponse.kubernetesResourceBreakdown,
2236
+ target: target,
2237
+ });
2238
+ const hasSeries = Boolean(input.perSeriesMatches && input.perSeriesMatches.length > 0);
2239
+ /*
2240
+ * Nothing to say only when there is neither a list nor a monitor to
2241
+ * describe. An ungrouped formula criteria has no raw scan of its own
2242
+ * and no series, but its cluster and formula are still worth naming.
2243
+ */
2244
+ if (!breakdown &&
2245
+ !hasSeries &&
2246
+ !((_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.kubernetesMonitor)) {
1560
2247
  return null;
1561
2248
  }
2249
+ const clusterName = (breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) ||
2250
+ ((_c = (_b = input.monitorStep.data) === null || _b === void 0 ? void 0 : _b.kubernetesMonitor) === null || _c === void 0 ? void 0 : _c.clusterIdentifier) ||
2251
+ "Unknown";
2252
+ /*
2253
+ * The metric the analysis below reasons about: the criteria's own
2254
+ * query, or a formula's numerator. Without a criteria to go on, the
2255
+ * breakdown's metric, as before.
2256
+ */
2257
+ const analysisMetricName = MonitorCriteriaEvaluator.getAnalysisMetricName(target) ||
2258
+ (breakdown === null || breakdown === void 0 ? void 0 : breakdown.metricName) ||
2259
+ "";
1562
2260
  const sections = [];
1563
2261
  // Cluster context
1564
2262
  const clusterDetails = [];
1565
- clusterDetails.push(`- Cluster: ${breakdown.clusterName}`);
1566
- clusterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
1567
- if (breakdown.attributes["k8s.namespace.name"]) {
1568
- clusterDetails.push(`- Namespace: ${breakdown.attributes["k8s.namespace.name"]}`);
2263
+ clusterDetails.push(`- Cluster: ${clusterName}`);
2264
+ clusterDetails.push(...MonitorCriteriaEvaluator.describeCriteriaMetric({
2265
+ platform: "kubernetes",
2266
+ target: target,
2267
+ breakdown: breakdown,
2268
+ }));
2269
+ /*
2270
+ * The worker records the query's attribute filters under the stored,
2271
+ * `resource.`-prefixed key; the bare key never occurs, which is why
2272
+ * this line never rendered.
2273
+ */
2274
+ const namespaceFilter = (breakdown === null || breakdown === void 0 ? void 0 : breakdown.attributes["resource.k8s.namespace.name"]) ||
2275
+ (breakdown === null || breakdown === void 0 ? void 0 : breakdown.attributes["k8s.namespace.name"]) ||
2276
+ ((_f = (_e = (_d = input.monitorStep.data) === null || _d === void 0 ? void 0 : _d.kubernetesMonitor) === null || _e === void 0 ? void 0 : _e.resourceFilters) === null || _f === void 0 ? void 0 : _f.namespace);
2277
+ if (namespaceFilter) {
2278
+ clusterDetails.push(`- Namespace: ${namespaceFilter}`);
1569
2279
  }
1570
2280
  sections.push(`**Kubernetes Cluster Details**\n${clusterDetails.join("\n")}`);
1571
- // Affected resources
1572
- if (breakdown.affectedResources && breakdown.affectedResources.length > 0) {
1573
- /*
1574
- * Keep the rows that satisfy the criteria that just matched, worst
1575
- * first — NOT simply the non-zero rows. k8s-node-not-ready fires on
1576
- * `k8s.node.condition_ready = 0`, so the zero rows are the NotReady
1577
- * nodes; the old hardcoded `> 0` dropped exactly those and named a
1578
- * healthy node in the analysis below.
1579
- */
1580
- const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
1581
- criteriaInstance: input.criteriaInstance,
1582
- floorEqualityFiresOnFall: true,
1583
- });
1584
- const sortedResources = breakdown.affectedResources
1585
- .map((r) => {
1586
- var _a;
1587
- /*
1588
- * A fall criteria breached on the resource's lowest sample
1589
- * in the window, not its highest.
1590
- */
1591
- return breach.worstIsLowest
1592
- ? Object.assign(Object.assign({}, r), { metricValue: (_a = r.lowestMetricValue) !== null && _a !== void 0 ? _a : r.metricValue }) : r;
1593
- })
1594
- .filter((r) => {
1595
- return breach.matches(r.metricValue);
1596
- })
1597
- .sort((a, b) => {
1598
- return breach.worstIsLowest
1599
- ? a.metricValue - b.metricValue
1600
- : b.metricValue - a.metricValue;
1601
- });
1602
- if (sortedResources.length === 0) {
1603
- return sections.join("\n");
1604
- }
1605
- // Show top 10 affected resources
1606
- const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
1607
- sections.push(AffectedResourceList.render({
1608
- heading: "Affected Resources",
1609
- overflowNoun: "affected resources",
1610
- totalCount: sortedResources.length,
1611
- entries: resourcesToShow.map((resource) => {
1612
- return MonitorCriteriaEvaluator.getKubernetesAffectedResourceEntry({
1613
- resource: resource,
1614
- clusterName: breakdown.clusterName,
1615
- value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
1616
- platform: "kubernetes",
1617
- metricName: breakdown.metricName,
1618
- value: resource.metricValue,
1619
- }),
2281
+ /*
2282
+ * Keep the rows that satisfy the criteria that just matched, worst
2283
+ * first — NOT simply the non-zero rows. k8s-node-not-ready fires on
2284
+ * `k8s.node.condition_ready = 0`, so the zero rows are the NotReady
2285
+ * nodes; the old hardcoded `> 0` dropped exactly those and named a
2286
+ * healthy node in the analysis below.
2287
+ */
2288
+ const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
2289
+ criteriaInstance: input.criteriaInstance,
2290
+ floorEqualityFiresOnFall: true,
2291
+ });
2292
+ const rows = MonitorCriteriaEvaluator.getPlatformAffectedRows({
2293
+ platform: "kubernetes",
2294
+ perSeriesMatches: input.perSeriesMatches,
2295
+ namesObject: (identity) => {
2296
+ return Boolean(identity.containerName ||
2297
+ identity.podName ||
2298
+ identity.workloadName ||
2299
+ identity.nodeName ||
2300
+ identity.namespace);
2301
+ },
2302
+ metricResponse: metricResponse,
2303
+ monitorStep: input.monitorStep,
2304
+ target: target,
2305
+ breakdown: breakdown,
2306
+ breach: breach,
2307
+ toIdentity: PlatformResourceIdentity.kubernetes,
2308
+ withContext: PlatformResourceIdentity.withKubernetesContext,
2309
+ });
2310
+ if (!rows || rows.length === 0) {
2311
+ return sections.join("\n");
2312
+ }
2313
+ // Show top 10 affected resources
2314
+ const rowsToShow = rows.slice(0, AffectedResourceList.MAX_ENTRIES);
2315
+ sections.push(AffectedResourceList.render({
2316
+ heading: "Affected Resources",
2317
+ overflowNoun: "affected resources",
2318
+ totalCount: rows.length,
2319
+ entries: rowsToShow.map((row) => {
2320
+ return row.seriesLabels
2321
+ ? MonitorCriteriaEvaluator.getSeriesLabelEntry({
2322
+ labels: row.seriesLabels,
2323
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
2324
+ })
2325
+ : MonitorCriteriaEvaluator.getKubernetesAffectedResourceEntry({
2326
+ resource: row.identity,
2327
+ clusterName: clusterName,
2328
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
1620
2329
  });
1621
- }),
1622
- }));
1623
- // Add root cause analysis based on metric type
1624
- const analysis = MonitorCriteriaEvaluator.buildKubernetesRootCauseAnalysis({
1625
- breakdown: breakdown,
1626
- topResource: resourcesToShow[0],
2330
+ }),
2331
+ }));
2332
+ const topRow = rowsToShow[0];
2333
+ const topResource = Object.assign(Object.assign({}, topRow.identity), { metricValue: (_g = topRow.value) !== null && _g !== void 0 ? _g : 0 });
2334
+ /*
2335
+ * Add root cause analysis based on metric type — unless the worst row
2336
+ * is a series that matched with no data at all, or one valued by a
2337
+ * different filter than the one analysed, which no sentence below
2338
+ * ("memory usage is at ...") can describe.
2339
+ */
2340
+ const analysis = topRow.value === null || topRow.valueNote
2341
+ ? null
2342
+ : MonitorCriteriaEvaluator.buildKubernetesRootCauseAnalysis({
2343
+ breakdown: {
2344
+ clusterName: clusterName,
2345
+ metricName: analysisMetricName,
2346
+ metricFriendlyName: ((target === null || target === void 0 ? void 0 : target.isFormula) ? target.displayName : undefined) ||
2347
+ ((breakdown === null || breakdown === void 0 ? void 0 : breakdown.metricName) === analysisMetricName
2348
+ ? breakdown.metricFriendlyName
2349
+ : undefined) ||
2350
+ MonitorCriteriaEvaluator.getPlatformMetricFriendlyName({
2351
+ platform: "kubernetes",
2352
+ metricName: analysisMetricName,
2353
+ }) ||
2354
+ analysisMetricName,
2355
+ affectedResources: [],
2356
+ attributes: (breakdown === null || breakdown === void 0 ? void 0 : breakdown.attributes) || {},
2357
+ metricUnit: breakdown === null || breakdown === void 0 ? void 0 : breakdown.metricUnit,
2358
+ },
2359
+ topResource: topResource,
2360
+ topResourceValue: topRow.formattedValue,
2361
+ target: target,
1627
2362
  });
1628
- if (analysis) {
1629
- sections.push(`\n\n**Root Cause Analysis**\n${analysis}`);
1630
- }
1631
- // Fetch recent container logs for the top affected resource during CrashLoopBackOff
1632
- if ((breakdown.metricName === "k8s.container.restarts" ||
1633
- breakdown.metricName.includes("restart")) &&
1634
- input.monitor.projectId) {
1635
- const topResource = resourcesToShow[0];
1636
- try {
1637
- const logAttributes = {};
1638
- if (breakdown.clusterName) {
1639
- logAttributes["resource.k8s.cluster.name"] = breakdown.clusterName;
1640
- }
1641
- if (topResource.podName) {
1642
- logAttributes["resource.k8s.pod.name"] = topResource.podName;
1643
- }
1644
- if (topResource.containerName) {
1645
- logAttributes["resource.k8s.container.name"] =
1646
- topResource.containerName;
1647
- }
1648
- if (topResource.namespace) {
1649
- logAttributes["resource.k8s.namespace.name"] =
1650
- topResource.namespace;
1651
- }
1652
- const now = OneUptimeDate.getCurrentDate();
1653
- const fifteenMinutesAgo = OneUptimeDate.addRemoveMinutes(now, -15);
1654
- const logs = await LogAggregationService.getExportLogs({
1655
- projectId: input.monitor.projectId,
1656
- startTime: fifteenMinutesAgo,
1657
- endTime: now,
1658
- limit: 50,
1659
- attributes: logAttributes,
1660
- });
1661
- if (logs.length > 0) {
1662
- const logLines = logs.map((log) => {
1663
- const timestamp = log["time"] ? String(log["time"]) : "";
1664
- const severity = log["severityText"]
1665
- ? String(log["severityText"])
1666
- : "INFO";
1667
- const body = log["body"] ? String(log["body"]) : "";
1668
- return `\`${timestamp}\` **${severity}** ${body}`;
1669
- });
1670
- sections.push(`\n\n**Recent Container Logs** (${topResource.podName || "unknown pod"} / ${topResource.containerName || "unknown container"}, last 15 minutes)\n\n${logLines.join("\n\n")}`);
1671
- }
2363
+ if (analysis) {
2364
+ sections.push(`\n\n**Root Cause Analysis**\n${analysis}`);
2365
+ }
2366
+ // Fetch recent container logs for the top affected resource during CrashLoopBackOff
2367
+ if ((analysisMetricName === "k8s.container.restarts" ||
2368
+ analysisMetricName.includes("restart")) &&
2369
+ input.monitor.projectId) {
2370
+ try {
2371
+ const logAttributes = {};
2372
+ if (clusterName) {
2373
+ logAttributes["resource.k8s.cluster.name"] = clusterName;
1672
2374
  }
1673
- catch (err) {
1674
- const k8sLogAttributes = {
1675
- projectId: (_a = input.monitor.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
1676
- };
1677
- logger.error("Failed to fetch container logs for root cause context", k8sLogAttributes);
1678
- logger.error(err, k8sLogAttributes);
2375
+ if (topResource.podName) {
2376
+ logAttributes["resource.k8s.pod.name"] = topResource.podName;
2377
+ }
2378
+ if (topResource.containerName) {
2379
+ logAttributes["resource.k8s.container.name"] =
2380
+ topResource.containerName;
1679
2381
  }
2382
+ if (topResource.namespace) {
2383
+ logAttributes["resource.k8s.namespace.name"] = topResource.namespace;
2384
+ }
2385
+ const now = OneUptimeDate.getCurrentDate();
2386
+ const fifteenMinutesAgo = OneUptimeDate.addRemoveMinutes(now, -15);
2387
+ const logs = await LogAggregationService.getExportLogs({
2388
+ projectId: input.monitor.projectId,
2389
+ startTime: fifteenMinutesAgo,
2390
+ endTime: now,
2391
+ limit: 50,
2392
+ attributes: logAttributes,
2393
+ });
2394
+ if (logs.length > 0) {
2395
+ const logLines = logs.map((log) => {
2396
+ const timestamp = log["time"] ? String(log["time"]) : "";
2397
+ const severity = log["severityText"]
2398
+ ? String(log["severityText"])
2399
+ : "INFO";
2400
+ const body = log["body"] ? String(log["body"]) : "";
2401
+ return `\`${timestamp}\` **${severity}** ${body}`;
2402
+ });
2403
+ sections.push(`\n\n**Recent Container Logs** (${topResource.podName || "unknown pod"} / ${topResource.containerName || "unknown container"}, last 15 minutes)\n\n${logLines.join("\n\n")}`);
2404
+ }
2405
+ }
2406
+ catch (err) {
2407
+ const k8sLogAttributes = {
2408
+ projectId: (_h = input.monitor.projectId) === null || _h === void 0 ? void 0 : _h.toString(),
2409
+ };
2410
+ logger.error("Failed to fetch container logs for root cause context", k8sLogAttributes);
2411
+ logger.error(err, k8sLogAttributes);
1680
2412
  }
1681
2413
  }
1682
2414
  return sections.join("\n");
1683
2415
  }
1684
- static buildDockerRootCauseContext(input) {
1685
- var _a, _b, _c, _d, _e, _f, _g;
1686
- const metricResponse = input.dataToProcess;
2416
+ /**
2417
+ * The "- Metric:" line of a telemetry resource's details block: what
2418
+ * the matched criteria actually compared.
2419
+ *
2420
+ * It used to name `queryConfigs[0]` unconditionally, so a criteria on a
2421
+ * formula — Host CPU busy `(host_cpu_user + host_cpu_system) * 100`,
2422
+ * Docker restarts `max - min` — was reported as its first operand. The
2423
+ * metric context MetricMonitorCriteria records at evaluation time
2424
+ * already says whether the criteria resolved to a query or a formula
2425
+ * (including its fall back to the first query when the alias names
2426
+ * nothing), so the line is read from there.
2427
+ *
2428
+ * A formula is named by its legend, which is what the threshold was
2429
+ * written against ("CPU Busy (%)"); its expression and the metric behind
2430
+ * each variable are spelled out in the Metric Details below. With no
2431
+ * metric context — a criteria with no metric-value filter — the step's
2432
+ * first query is named, as before.
2433
+ */
2434
+ static describeCriteriaMetricLine(input) {
2435
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2436
+ const ctx = input.ctx;
2437
+ if (ctx === null || ctx === void 0 ? void 0 : ctx.isFormula) {
2438
+ const formula = (((_a = input.metricViewConfig) === null || _a === void 0 ? void 0 : _a.formulaConfigs) || []).find((f) => {
2439
+ var _a;
2440
+ return ((_a = f.metricAliasData) === null || _a === void 0 ? void 0 : _a.metricVariable) === ctx.alias;
2441
+ });
2442
+ const legend = (((_b = formula === null || formula === void 0 ? void 0 : formula.metricAliasData) === null || _b === void 0 ? void 0 : _b.legend) ||
2443
+ ((_c = formula === null || formula === void 0 ? void 0 : formula.metricAliasData) === null || _c === void 0 ? void 0 : _c.title) ||
2444
+ "").trim();
2445
+ if (legend && legend !== ctx.alias) {
2446
+ return `- Metric: ${legend}`;
2447
+ }
2448
+ return `- Metric: \`${ctx.alias}\` (formula)`;
2449
+ }
2450
+ if (ctx === null || ctx === void 0 ? void 0 : ctx.metricName) {
2451
+ return `- Metric: \`${ctx.metricName}\``;
2452
+ }
2453
+ const firstQueryMetricName = (_h = (_g = (_f = (_e = (_d = input.metricViewConfig) === null || _d === void 0 ? void 0 : _d.queryConfigs) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.metricQueryData) === null || _g === void 0 ? void 0 : _g.filterData) === null || _h === void 0 ? void 0 : _h.metricName;
2454
+ return firstQueryMetricName
2455
+ ? `- Metric: \`${firstQueryMetricName}\``
2456
+ : null;
2457
+ }
2458
+ /**
2459
+ * Root-cause context for a telemetry monitor that watches one host,
2460
+ * container set or device fleet: the resource it is scoped to, then the
2461
+ * same Metric Details and Breaching Samples a Metrics monitor gets.
2462
+ *
2463
+ * These monitors used to follow the identity lines with a "Metric
2464
+ * Summary" that only counted the data points in each result — one line
2465
+ * per query AND per formula, so the Host CPU template printed three
2466
+ * identical "- 5 metric data point(s) returned" lines and never the
2467
+ * value that breached. MetricMonitorCriteria fills the filter's metric
2468
+ * context for every metric-backed monitor type, so the unit, formula,
2469
+ * components and unit-formatted samples were there all along.
2470
+ *
2471
+ * `identityLines` is null when the step carries no config for the
2472
+ * monitor type; the metric details are still rendered from the context.
2473
+ */
2474
+ static buildTelemetryResourceRootCauseContext(input) {
1687
2475
  const sections = [];
2476
+ if (input.identityLines) {
2477
+ const metricLine = MonitorCriteriaEvaluator.describeCriteriaMetricLine({
2478
+ ctx: MonitorCriteriaEvaluator.getMetricCriteriaContext(input.criteriaInstance),
2479
+ metricViewConfig: MonitorStep.getMetricsViewConfig(input.monitorStep),
2480
+ });
2481
+ const lines = metricLine
2482
+ ? [...input.identityLines, metricLine]
2483
+ : input.identityLines;
2484
+ sections.push(`**${input.heading}**\n${lines.join("\n")}`);
2485
+ }
2486
+ const metricDetails = input.criteriaInstance
2487
+ ? MonitorCriteriaEvaluator.buildMetricRootCauseContext({
2488
+ criteriaInstance: input.criteriaInstance,
2489
+ monitor: input.monitor,
2490
+ monitorStep: input.monitorStep,
2491
+ })
2492
+ : null;
2493
+ if (metricDetails) {
2494
+ sections.push(sections.length > 0 ? `\n\n${metricDetails}` : metricDetails);
2495
+ }
2496
+ return sections.length > 0 ? sections.join("\n") : null;
2497
+ }
2498
+ static buildDockerRootCauseContext(input) {
2499
+ var _a, _b, _c;
1688
2500
  // Docker host context
1689
2501
  const dockerMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.dockerMonitor;
2502
+ let hostDetails = null;
1690
2503
  if (dockerMonitor) {
1691
- const hostDetails = [];
1692
- hostDetails.push(`- Host: ${dockerMonitor.hostIdentifier || "Unknown"}`);
2504
+ hostDetails = [`- Host: ${dockerMonitor.hostIdentifier || "Unknown"}`];
1693
2505
  if ((_b = dockerMonitor.containerFilters) === null || _b === void 0 ? void 0 : _b.containerName) {
1694
2506
  hostDetails.push(`- Container Name Filter: ${dockerMonitor.containerFilters.containerName}`);
1695
2507
  }
1696
2508
  if ((_c = dockerMonitor.containerFilters) === null || _c === void 0 ? void 0 : _c.containerImage) {
1697
2509
  hostDetails.push(`- Container Image Filter: ${dockerMonitor.containerFilters.containerImage}`);
1698
2510
  }
1699
- // Add metric name from the query config
1700
- if (((_e = (_d = dockerMonitor.metricViewConfig) === null || _d === void 0 ? void 0 : _d.queryConfigs) === null || _e === void 0 ? void 0 : _e.length) > 0 &&
1701
- dockerMonitor.metricViewConfig.queryConfigs[0]) {
1702
- const metricName = (_g = (_f = dockerMonitor.metricViewConfig
1703
- .queryConfigs[0].metricQueryData) === null || _f === void 0 ? void 0 : _f.filterData) === null || _g === void 0 ? void 0 : _g.metricName;
1704
- if (metricName) {
1705
- hostDetails.push(`- Metric: \`${metricName}\``);
1706
- }
1707
- }
1708
- sections.push(`**Docker Host Details**\n${hostDetails.join("\n")}`);
1709
2511
  }
1710
- // Metric results summary
1711
- if (metricResponse.metricResult && metricResponse.metricResult.length > 0) {
1712
- const resultDetails = [];
1713
- for (const result of metricResponse.metricResult) {
1714
- if (result.data && result.data.length > 0) {
1715
- resultDetails.push(`- ${result.data.length} metric data point(s) returned`);
1716
- }
1717
- }
1718
- if (resultDetails.length > 0) {
1719
- sections.push(`\n\n**Metric Summary**\n${resultDetails.join("\n")}`);
1720
- }
1721
- }
1722
- return sections.length > 0 ? sections.join("\n") : null;
2512
+ return MonitorCriteriaEvaluator.buildTelemetryResourceRootCauseContext({
2513
+ heading: "Docker Host Details",
2514
+ identityLines: hostDetails,
2515
+ monitor: input.monitor,
2516
+ monitorStep: input.monitorStep,
2517
+ criteriaInstance: input.criteriaInstance,
2518
+ });
1723
2519
  }
1724
2520
  static buildHostRootCauseContext(input) {
1725
- var _a, _b, _c, _d, _e;
1726
- const metricResponse = input.dataToProcess;
1727
- const sections = [];
2521
+ var _a;
1728
2522
  // Host context
1729
2523
  const hostMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.hostMonitor;
1730
- if (hostMonitor) {
1731
- const hostDetails = [];
1732
- hostDetails.push(`- Host: ${hostMonitor.hostIdentifier || "Unknown"}`);
1733
- // Add metric name from the query config
1734
- if (((_c = (_b = hostMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
1735
- hostMonitor.metricViewConfig.queryConfigs[0]) {
1736
- const metricName = (_e = (_d = hostMonitor.metricViewConfig.queryConfigs[0]
1737
- .metricQueryData) === null || _d === void 0 ? void 0 : _d.filterData) === null || _e === void 0 ? void 0 : _e.metricName;
1738
- if (metricName) {
1739
- hostDetails.push(`- Metric: \`${metricName}\``);
1740
- }
1741
- }
1742
- sections.push(`**Host Details**\n${hostDetails.join("\n")}`);
1743
- }
1744
- // Metric results summary
1745
- if (metricResponse.metricResult && metricResponse.metricResult.length > 0) {
1746
- const resultDetails = [];
1747
- for (const result of metricResponse.metricResult) {
1748
- if (result.data && result.data.length > 0) {
1749
- resultDetails.push(`- ${result.data.length} metric data point(s) returned`);
1750
- }
1751
- }
1752
- if (resultDetails.length > 0) {
1753
- sections.push(`\n\n**Metric Summary**\n${resultDetails.join("\n")}`);
1754
- }
1755
- }
1756
- return sections.length > 0 ? sections.join("\n") : null;
2524
+ return MonitorCriteriaEvaluator.buildTelemetryResourceRootCauseContext({
2525
+ heading: "Host Details",
2526
+ identityLines: hostMonitor
2527
+ ? [`- Host: ${hostMonitor.hostIdentifier || "Unknown"}`]
2528
+ : null,
2529
+ monitor: input.monitor,
2530
+ monitorStep: input.monitorStep,
2531
+ criteriaInstance: input.criteriaInstance,
2532
+ });
1757
2533
  }
1758
2534
  static buildPodmanRootCauseContext(input) {
1759
- var _a, _b, _c, _d, _e, _f, _g;
1760
- const metricResponse = input.dataToProcess;
1761
- const sections = [];
2535
+ var _a, _b, _c;
1762
2536
  // Podman host context
1763
2537
  const podmanMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.podmanMonitor;
2538
+ let hostDetails = null;
1764
2539
  if (podmanMonitor) {
1765
- const hostDetails = [];
1766
- hostDetails.push(`- Host: ${podmanMonitor.hostIdentifier || "Unknown"}`);
2540
+ hostDetails = [`- Host: ${podmanMonitor.hostIdentifier || "Unknown"}`];
1767
2541
  if ((_b = podmanMonitor.containerFilters) === null || _b === void 0 ? void 0 : _b.containerName) {
1768
2542
  hostDetails.push(`- Container Name Filter: ${podmanMonitor.containerFilters.containerName}`);
1769
2543
  }
1770
2544
  if ((_c = podmanMonitor.containerFilters) === null || _c === void 0 ? void 0 : _c.containerImage) {
1771
2545
  hostDetails.push(`- Container Image Filter: ${podmanMonitor.containerFilters.containerImage}`);
1772
2546
  }
1773
- // Add metric name from the query config
1774
- if (((_e = (_d = podmanMonitor.metricViewConfig) === null || _d === void 0 ? void 0 : _d.queryConfigs) === null || _e === void 0 ? void 0 : _e.length) > 0 &&
1775
- podmanMonitor.metricViewConfig.queryConfigs[0]) {
1776
- const metricName = (_g = (_f = podmanMonitor.metricViewConfig
1777
- .queryConfigs[0].metricQueryData) === null || _f === void 0 ? void 0 : _f.filterData) === null || _g === void 0 ? void 0 : _g.metricName;
1778
- if (metricName) {
1779
- hostDetails.push(`- Metric: \`${metricName}\``);
1780
- }
1781
- }
1782
- sections.push(`**Podman Host Details**\n${hostDetails.join("\n")}`);
1783
2547
  }
1784
- // Metric results summary
1785
- if (metricResponse.metricResult && metricResponse.metricResult.length > 0) {
1786
- const resultDetails = [];
1787
- for (const result of metricResponse.metricResult) {
1788
- if (result.data && result.data.length > 0) {
1789
- resultDetails.push(`- ${result.data.length} metric data point(s) returned`);
1790
- }
2548
+ return MonitorCriteriaEvaluator.buildTelemetryResourceRootCauseContext({
2549
+ heading: "Podman Host Details",
2550
+ identityLines: hostDetails,
2551
+ monitor: input.monitor,
2552
+ monitorStep: input.monitorStep,
2553
+ criteriaInstance: input.criteriaInstance,
2554
+ });
2555
+ }
2556
+ static buildIoTRootCauseContext(input) {
2557
+ var _a, _b, _c, _d, _e, _f;
2558
+ // IoT fleet / device context
2559
+ const iotMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.iotMonitor;
2560
+ let deviceDetails = null;
2561
+ if (iotMonitor) {
2562
+ deviceDetails = [`- Fleet: ${iotMonitor.fleetIdentifier || "Unknown"}`];
2563
+ /*
2564
+ * The device that breached. Every IoT template groups by the
2565
+ * `device.id` label, so the matched series names it; an ungrouped
2566
+ * monitor has no series labels and falls back to the step's own
2567
+ * device filter below.
2568
+ */
2569
+ const breachingDeviceId = (_c = (_b = MonitorCriteriaEvaluator.getMetricCriteriaContext(input.criteriaInstance)) === null || _b === void 0 ? void 0 : _b.seriesLabels) === null || _c === void 0 ? void 0 : _c[IOT_DEVICE_ID_ATTRIBUTE_KEY];
2570
+ if (breachingDeviceId !== undefined &&
2571
+ breachingDeviceId !== null &&
2572
+ String(breachingDeviceId) !== "") {
2573
+ deviceDetails.push(`- Device: ${AffectedResourceList.code(String(breachingDeviceId))}`);
1791
2574
  }
1792
- if (resultDetails.length > 0) {
1793
- sections.push(`\n\n**Metric Summary**\n${resultDetails.join("\n")}`);
2575
+ if ((_d = iotMonitor.resourceFilters) === null || _d === void 0 ? void 0 : _d.deviceId) {
2576
+ deviceDetails.push(`- Device ID Filter: ${iotMonitor.resourceFilters.deviceId}`);
2577
+ }
2578
+ if ((_e = iotMonitor.resourceFilters) === null || _e === void 0 ? void 0 : _e.deviceType) {
2579
+ deviceDetails.push(`- Device Type Filter: ${iotMonitor.resourceFilters.deviceType}`);
2580
+ }
2581
+ if ((_f = iotMonitor.resourceFilters) === null || _f === void 0 ? void 0 : _f.scope) {
2582
+ deviceDetails.push(`- Scope Filter: ${iotMonitor.resourceFilters.scope}`);
1794
2583
  }
1795
2584
  }
1796
- return sections.length > 0 ? sections.join("\n") : null;
2585
+ return MonitorCriteriaEvaluator.buildTelemetryResourceRootCauseContext({
2586
+ heading: "IoT Device Details",
2587
+ identityLines: deviceDetails,
2588
+ monitor: input.monitor,
2589
+ monitorStep: input.monitorStep,
2590
+ criteriaInstance: input.criteriaInstance,
2591
+ });
1797
2592
  }
1798
2593
  /*
1799
2594
  * What a Proxmox resource is, from the `pve.type` / `pve.scope` the
@@ -1865,87 +2660,100 @@ ${contextBlock}
1865
2660
  };
1866
2661
  }
1867
2662
  static buildProxmoxRootCauseContext(input) {
1868
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2663
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1869
2664
  const metricResponse = input.dataToProcess;
1870
- const breakdown = metricResponse.proxmoxResourceBreakdown;
1871
- const sections = [];
1872
2665
  // Proxmox cluster context
1873
2666
  const proxmoxMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.proxmoxMonitor;
2667
+ const target = MonitorCriteriaEvaluator.resolveCriteriaMetricTarget({
2668
+ criteriaInstance: input.criteriaInstance,
2669
+ monitorStep: input.monitorStep,
2670
+ metricResponse: metricResponse,
2671
+ });
2672
+ const breakdown = MonitorCriteriaEvaluator.selectPlatformBreakdown({
2673
+ breakdowns: metricResponse.proxmoxResourceBreakdowns,
2674
+ legacyBreakdown: metricResponse.proxmoxResourceBreakdown,
2675
+ target: target,
2676
+ });
2677
+ const clusterName = (breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) || (proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.clusterIdentifier) || "Unknown";
2678
+ const sections = [];
1874
2679
  if (proxmoxMonitor || breakdown) {
1875
2680
  const clusterDetails = [];
1876
- clusterDetails.push(`- Cluster: ${(breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) || (proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.clusterIdentifier) || "Unknown"}`);
1877
- if (breakdown) {
1878
- clusterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
1879
- }
1880
- else if (proxmoxMonitor &&
1881
- ((_c = (_b = proxmoxMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
1882
- proxmoxMonitor.metricViewConfig.queryConfigs[0]) {
1883
- const metricName = (_e = (_d = proxmoxMonitor.metricViewConfig
1884
- .queryConfigs[0].metricQueryData) === null || _d === void 0 ? void 0 : _d.filterData) === null || _e === void 0 ? void 0 : _e.metricName;
1885
- if (metricName) {
1886
- clusterDetails.push(`- Metric: \`${metricName}\``);
1887
- }
1888
- }
1889
- if ((_f = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _f === void 0 ? void 0 : _f.scope) {
2681
+ clusterDetails.push(`- Cluster: ${clusterName}`);
2682
+ clusterDetails.push(...MonitorCriteriaEvaluator.describeCriteriaMetric({
2683
+ platform: "proxmox",
2684
+ target: target,
2685
+ breakdown: breakdown,
2686
+ fallbackMetricName: (_f = (_e = (_d = (_c = (_b = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.metricQueryData) === null || _e === void 0 ? void 0 : _e.filterData) === null || _f === void 0 ? void 0 : _f.metricName,
2687
+ }));
2688
+ if ((_g = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.scope) {
1890
2689
  clusterDetails.push(`- Scope Filter: ${proxmoxMonitor.resourceFilters.scope}`);
1891
2690
  }
1892
- if ((_g = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.pveId) {
2691
+ if ((_h = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.pveId) {
1893
2692
  clusterDetails.push(`- Resource ID Filter: ${proxmoxMonitor.resourceFilters.pveId}`);
1894
2693
  }
1895
- if ((_h = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.nodeName) {
2694
+ if ((_j = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _j === void 0 ? void 0 : _j.nodeName) {
1896
2695
  clusterDetails.push(`- Node Filter: ${proxmoxMonitor.resourceFilters.nodeName}`);
1897
2696
  }
1898
- if ((_j = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _j === void 0 ? void 0 : _j.guestId) {
2697
+ if ((_k = proxmoxMonitor === null || proxmoxMonitor === void 0 ? void 0 : proxmoxMonitor.resourceFilters) === null || _k === void 0 ? void 0 : _k.guestId) {
1899
2698
  clusterDetails.push(`- Guest ID Filter: ${proxmoxMonitor.resourceFilters.guestId}`);
1900
2699
  }
1901
2700
  sections.push(`**Proxmox Cluster Details**\n${clusterDetails.join("\n")}`);
1902
2701
  }
1903
2702
  // Affected resources: a ranked list of kind, name and node
1904
2703
  let renderedBreakdownList = false;
1905
- if (breakdown && breakdown.affectedResources.length > 0) {
1906
- /*
1907
- * K8s-parity render: drop zero-value rows, worst (highest)
1908
- * first, top 10. Note that for availability metrics (pve_up)
1909
- * zero-valued rows ARE the down resources — those still drive
1910
- * alerting through the per-series criteria evaluation; this
1911
- * list is supplementary context only.
1912
- */
1913
- const sortedResources = [
1914
- ...breakdown.affectedResources,
1915
- ]
1916
- .filter((r) => {
1917
- return r.metricValue > 0;
1918
- })
1919
- .sort((a, b) => {
1920
- return b.metricValue - a.metricValue;
1921
- });
1922
- /*
1923
- * Skip the list when no row carries any identity label
1924
- * (cluster-wide series) — it would add nothing.
1925
- */
1926
- const hasIdentity = sortedResources.some((r) => {
1927
- return r.resourceId || r.resourceName || r.nodeName;
1928
- });
1929
- if (sortedResources.length > 0 && hasIdentity) {
1930
- const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
1931
- sections.push(AffectedResourceList.render({
1932
- heading: "Affected Resources",
1933
- overflowNoun: "affected resources",
1934
- totalCount: sortedResources.length,
1935
- entries: resourcesToShow.map((resource) => {
1936
- return MonitorCriteriaEvaluator.getProxmoxAffectedResourceEntry({
1937
- resource: resource,
1938
- clusterName: breakdown.clusterName,
1939
- value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
1940
- platform: "proxmox",
1941
- metricName: breakdown.metricName,
1942
- value: resource.metricValue,
1943
- }),
2704
+ /*
2705
+ * The resources the matched criteria breached, worst first, top 10 —
2706
+ * NOT simply the non-zero rows. pve-node-offline fires on `pve_up < 1`,
2707
+ * so the zero rows ARE the down nodes; the old `> 0` listed every
2708
+ * healthy node under it instead.
2709
+ */
2710
+ const rows = MonitorCriteriaEvaluator.getPlatformAffectedRows({
2711
+ platform: "proxmox",
2712
+ perSeriesMatches: input.perSeriesMatches,
2713
+ namesObject: (identity) => {
2714
+ return Boolean(identity.resourceId || identity.resourceName || identity.nodeName);
2715
+ },
2716
+ metricResponse: metricResponse,
2717
+ monitorStep: input.monitorStep,
2718
+ target: target,
2719
+ breakdown: breakdown,
2720
+ breach: MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
2721
+ criteriaInstance: input.criteriaInstance,
2722
+ }),
2723
+ toIdentity: PlatformResourceIdentity.proxmox,
2724
+ withContext: PlatformResourceIdentity.withProxmoxContext,
2725
+ });
2726
+ /*
2727
+ * Skip the list when no row carries any identity label
2728
+ * (cluster-wide series) — it would add nothing.
2729
+ */
2730
+ const hasIdentity = (rows || []).some((row) => {
2731
+ return Boolean(row.seriesLabels ||
2732
+ row.identity.resourceId ||
2733
+ row.identity.resourceName ||
2734
+ row.identity.nodeName);
2735
+ });
2736
+ if (rows && rows.length > 0 && hasIdentity) {
2737
+ sections.push(AffectedResourceList.render({
2738
+ heading: "Affected Resources",
2739
+ overflowNoun: "affected resources",
2740
+ totalCount: rows.length,
2741
+ entries: rows
2742
+ .slice(0, AffectedResourceList.MAX_ENTRIES)
2743
+ .map((row) => {
2744
+ return row.seriesLabels
2745
+ ? MonitorCriteriaEvaluator.getSeriesLabelEntry({
2746
+ labels: row.seriesLabels,
2747
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
2748
+ })
2749
+ : MonitorCriteriaEvaluator.getProxmoxAffectedResourceEntry({
2750
+ resource: row.identity,
2751
+ clusterName: clusterName,
2752
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
1944
2753
  });
1945
- }),
1946
- }));
1947
- renderedBreakdownList = true;
1948
- }
2754
+ }),
2755
+ }));
2756
+ renderedBreakdownList = true;
1949
2757
  }
1950
2758
  // Metric results summary (fallback context when no list rendered)
1951
2759
  if (!renderedBreakdownList &&
@@ -2072,102 +2880,111 @@ ${contextBlock}
2072
2880
  };
2073
2881
  }
2074
2882
  static buildVMwareRootCauseContext(input) {
2075
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
2883
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
2076
2884
  const metricResponse = input.dataToProcess;
2077
- const breakdown = metricResponse.vmwareResourceBreakdown;
2078
- const sections = [];
2079
2885
  // vCenter context
2080
2886
  const vmwareMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.vmwareMonitor;
2887
+ const target = MonitorCriteriaEvaluator.resolveCriteriaMetricTarget({
2888
+ criteriaInstance: input.criteriaInstance,
2889
+ monitorStep: input.monitorStep,
2890
+ metricResponse: metricResponse,
2891
+ });
2892
+ const breakdown = MonitorCriteriaEvaluator.selectPlatformBreakdown({
2893
+ breakdowns: metricResponse.vmwareResourceBreakdowns,
2894
+ legacyBreakdown: metricResponse.vmwareResourceBreakdown,
2895
+ target: target,
2896
+ });
2897
+ const vcenterName = (breakdown === null || breakdown === void 0 ? void 0 : breakdown.vcenterName) || (vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.vcenterIdentifier) || "Unknown";
2898
+ const sections = [];
2081
2899
  if (vmwareMonitor || breakdown) {
2082
2900
  const vcenterDetails = [];
2083
- vcenterDetails.push(`- vCenter: ${(breakdown === null || breakdown === void 0 ? void 0 : breakdown.vcenterName) || (vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.vcenterIdentifier) || "Unknown"}`);
2084
- if (breakdown) {
2085
- vcenterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
2086
- }
2087
- else if (vmwareMonitor &&
2088
- ((_c = (_b = vmwareMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
2089
- vmwareMonitor.metricViewConfig.queryConfigs[0]) {
2090
- const metricName = (_e = (_d = vmwareMonitor.metricViewConfig
2091
- .queryConfigs[0].metricQueryData) === null || _d === void 0 ? void 0 : _d.filterData) === null || _e === void 0 ? void 0 : _e.metricName;
2092
- if (metricName) {
2093
- vcenterDetails.push(`- Metric: \`${metricName}\``);
2094
- }
2095
- }
2901
+ vcenterDetails.push(`- vCenter: ${vcenterName}`);
2902
+ vcenterDetails.push(...MonitorCriteriaEvaluator.describeCriteriaMetric({
2903
+ platform: "vmware",
2904
+ target: target,
2905
+ breakdown: breakdown,
2906
+ fallbackMetricName: (_f = (_e = (_d = (_c = (_b = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.metricQueryData) === null || _e === void 0 ? void 0 : _e.filterData) === null || _f === void 0 ? void 0 : _f.metricName,
2907
+ }));
2096
2908
  /*
2097
2909
  * The worker maps each resource filter to an equality on the
2098
2910
  * matching `resource.vcenter.*` attribute; surface the same
2099
2911
  * filters so the incident shows WHAT was scoped.
2100
2912
  */
2101
- if ((_f = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _f === void 0 ? void 0 : _f.datacenterName) {
2913
+ if ((_g = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.datacenterName) {
2102
2914
  vcenterDetails.push(`- Datacenter Filter: ${vmwareMonitor.resourceFilters.datacenterName}`);
2103
2915
  }
2104
- if ((_g = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.clusterName) {
2916
+ if ((_h = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.clusterName) {
2105
2917
  vcenterDetails.push(`- Cluster Filter: ${vmwareMonitor.resourceFilters.clusterName}`);
2106
2918
  }
2107
- if ((_h = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.hostName) {
2919
+ if ((_j = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _j === void 0 ? void 0 : _j.hostName) {
2108
2920
  vcenterDetails.push(`- Host Filter: ${vmwareMonitor.resourceFilters.hostName}`);
2109
2921
  }
2110
- if ((_j = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _j === void 0 ? void 0 : _j.vmName) {
2922
+ if ((_k = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _k === void 0 ? void 0 : _k.vmName) {
2111
2923
  vcenterDetails.push(`- Virtual Machine Filter: ${vmwareMonitor.resourceFilters.vmName}`);
2112
2924
  }
2113
- if ((_k = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _k === void 0 ? void 0 : _k.datastoreName) {
2925
+ if ((_l = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _l === void 0 ? void 0 : _l.datastoreName) {
2114
2926
  vcenterDetails.push(`- Datastore Filter: ${vmwareMonitor.resourceFilters.datastoreName}`);
2115
2927
  }
2116
- if ((_l = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _l === void 0 ? void 0 : _l.resourcePoolPath) {
2928
+ if ((_m = vmwareMonitor === null || vmwareMonitor === void 0 ? void 0 : vmwareMonitor.resourceFilters) === null || _m === void 0 ? void 0 : _m.resourcePoolPath) {
2117
2929
  vcenterDetails.push(`- Resource Pool Filter: ${vmwareMonitor.resourceFilters.resourcePoolPath}`);
2118
2930
  }
2119
2931
  sections.push(`**vCenter Details**\n${vcenterDetails.join("\n")}`);
2120
2932
  }
2121
2933
  // Affected resources: a ranked list of object, host and cluster
2122
2934
  let renderedBreakdownList = false;
2123
- if (breakdown && breakdown.affectedResources.length > 0) {
2124
- /*
2125
- * K8s/Proxmox-parity render: drop zero-value rows, worst (highest)
2126
- * first, top 10. For count metrics filtered to an unhealthy state
2127
- * (`vcenter.datacenter.host.count{status=red}`) the non-zero rows
2128
- * ARE the objects with a problem, so the list reads correctly;
2129
- * a zero row means "nothing in that state" and adds nothing. The
2130
- * per-series criteria evaluation is what actually alerts; this
2131
- * list is supplementary context only.
2132
- */
2133
- const sortedResources = [
2134
- ...breakdown.affectedResources,
2135
- ]
2136
- .filter((r) => {
2137
- return r.metricValue > 0;
2138
- })
2139
- .sort((a, b) => {
2140
- return b.metricValue - a.metricValue;
2141
- });
2142
- /*
2143
- * Skip the list when no row carries any identity attribute — it
2144
- * would add nothing. Every vcenter-receiver series carries at
2145
- * least `vcenter.datacenter.name`, so this only trips on a
2146
- * breakdown the worker could not attribute at all.
2147
- */
2148
- const hasIdentity = sortedResources.some((r) => {
2149
- return Boolean(MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(r));
2150
- });
2151
- if (sortedResources.length > 0 && hasIdentity) {
2152
- const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
2153
- sections.push(AffectedResourceList.render({
2154
- heading: "Affected Resources",
2155
- overflowNoun: "affected resources",
2156
- totalCount: sortedResources.length,
2157
- entries: resourcesToShow.map((resource) => {
2158
- return MonitorCriteriaEvaluator.getVMwareAffectedResourceEntry({
2159
- resource: resource,
2160
- vcenterName: breakdown.vcenterName,
2161
- value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
2162
- platform: "vmware",
2163
- metricName: breakdown.metricName,
2164
- value: resource.metricValue,
2165
- }),
2935
+ /*
2936
+ * The objects the matched criteria breached, worst first, top 10. For
2937
+ * count metrics filtered to an unhealthy state
2938
+ * (`vcenter.datacenter.host.count{status=red}`) the non-zero rows ARE
2939
+ * the objects with a problem.
2940
+ */
2941
+ const rows = MonitorCriteriaEvaluator.getPlatformAffectedRows({
2942
+ platform: "vmware",
2943
+ perSeriesMatches: input.perSeriesMatches,
2944
+ namesObject: (identity) => {
2945
+ return Boolean(MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(identity));
2946
+ },
2947
+ metricResponse: metricResponse,
2948
+ monitorStep: input.monitorStep,
2949
+ target: target,
2950
+ breakdown: breakdown,
2951
+ breach: MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
2952
+ criteriaInstance: input.criteriaInstance,
2953
+ }),
2954
+ toIdentity: PlatformResourceIdentity.vmware,
2955
+ withContext: PlatformResourceIdentity.withVMwareContext,
2956
+ });
2957
+ /*
2958
+ * Skip the list when no row carries any identity attribute — it
2959
+ * would add nothing. Every vcenter-receiver series carries at
2960
+ * least `vcenter.datacenter.name`, so this only trips on a
2961
+ * breakdown the worker could not attribute at all.
2962
+ */
2963
+ const hasIdentity = (rows || []).some((row) => {
2964
+ return Boolean(row.seriesLabels ||
2965
+ MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(row.identity));
2966
+ });
2967
+ if (rows && rows.length > 0 && hasIdentity) {
2968
+ sections.push(AffectedResourceList.render({
2969
+ heading: "Affected Resources",
2970
+ overflowNoun: "affected resources",
2971
+ totalCount: rows.length,
2972
+ entries: rows
2973
+ .slice(0, AffectedResourceList.MAX_ENTRIES)
2974
+ .map((row) => {
2975
+ return row.seriesLabels
2976
+ ? MonitorCriteriaEvaluator.getSeriesLabelEntry({
2977
+ labels: row.seriesLabels,
2978
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
2979
+ })
2980
+ : MonitorCriteriaEvaluator.getVMwareAffectedResourceEntry({
2981
+ resource: row.identity,
2982
+ vcenterName: vcenterName,
2983
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
2166
2984
  });
2167
- }),
2168
- }));
2169
- renderedBreakdownList = true;
2170
- }
2985
+ }),
2986
+ }));
2987
+ renderedBreakdownList = true;
2171
2988
  }
2172
2989
  // Metric results summary (fallback context when no list rendered)
2173
2990
  if (!renderedBreakdownList &&
@@ -2226,87 +3043,103 @@ ${contextBlock}
2226
3043
  };
2227
3044
  }
2228
3045
  static buildDockerSwarmRootCauseContext(input) {
2229
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3046
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
2230
3047
  const metricResponse = input.dataToProcess;
2231
- const breakdown = metricResponse.dockerSwarmResourceBreakdown;
2232
- const sections = [];
2233
3048
  // Docker Swarm cluster context
2234
3049
  const dockerSwarmMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.dockerSwarmMonitor;
3050
+ const target = MonitorCriteriaEvaluator.resolveCriteriaMetricTarget({
3051
+ criteriaInstance: input.criteriaInstance,
3052
+ monitorStep: input.monitorStep,
3053
+ metricResponse: metricResponse,
3054
+ });
3055
+ const breakdown = MonitorCriteriaEvaluator.selectPlatformBreakdown({
3056
+ breakdowns: metricResponse.dockerSwarmResourceBreakdowns,
3057
+ legacyBreakdown: metricResponse.dockerSwarmResourceBreakdown,
3058
+ target: target,
3059
+ });
3060
+ const clusterName = (breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) ||
3061
+ (dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.clusterIdentifier) ||
3062
+ "Unknown";
3063
+ const sections = [];
2235
3064
  if (dockerSwarmMonitor || breakdown) {
2236
3065
  const clusterDetails = [];
2237
- clusterDetails.push(`- Cluster: ${(breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) || (dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.clusterIdentifier) || "Unknown"}`);
2238
- if (breakdown) {
2239
- clusterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
2240
- }
2241
- else if (dockerSwarmMonitor &&
2242
- ((_c = (_b = dockerSwarmMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
2243
- dockerSwarmMonitor.metricViewConfig.queryConfigs[0]) {
2244
- const metricName = (_e = (_d = dockerSwarmMonitor.metricViewConfig
2245
- .queryConfigs[0].metricQueryData) === null || _d === void 0 ? void 0 : _d.filterData) === null || _e === void 0 ? void 0 : _e.metricName;
2246
- if (metricName) {
2247
- clusterDetails.push(`- Metric: \`${metricName}\``);
2248
- }
2249
- }
2250
- if ((_f = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _f === void 0 ? void 0 : _f.serviceName) {
3066
+ clusterDetails.push(`- Cluster: ${clusterName}`);
3067
+ clusterDetails.push(...MonitorCriteriaEvaluator.describeCriteriaMetric({
3068
+ platform: "dockerSwarm",
3069
+ target: target,
3070
+ breakdown: breakdown,
3071
+ fallbackMetricName: (_f = (_e = (_d = (_c = (_b = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.metricQueryData) === null || _e === void 0 ? void 0 : _e.filterData) === null || _f === void 0 ? void 0 : _f.metricName,
3072
+ }));
3073
+ if ((_g = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.serviceName) {
2251
3074
  clusterDetails.push(`- Service Filter: ${dockerSwarmMonitor.resourceFilters.serviceName}`);
2252
3075
  }
2253
- if ((_g = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.nodeName) {
3076
+ if ((_h = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.nodeName) {
2254
3077
  clusterDetails.push(`- Node Filter: ${dockerSwarmMonitor.resourceFilters.nodeName}`);
2255
3078
  }
2256
- if ((_h = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.containerName) {
3079
+ if ((_j = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _j === void 0 ? void 0 : _j.containerName) {
2257
3080
  clusterDetails.push(`- Container Name Filter: ${dockerSwarmMonitor.resourceFilters.containerName}`);
2258
3081
  }
2259
- if ((_j = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _j === void 0 ? void 0 : _j.containerImage) {
3082
+ if ((_k = dockerSwarmMonitor === null || dockerSwarmMonitor === void 0 ? void 0 : dockerSwarmMonitor.resourceFilters) === null || _k === void 0 ? void 0 : _k.containerImage) {
2260
3083
  clusterDetails.push(`- Container Image Filter: ${dockerSwarmMonitor.resourceFilters.containerImage}`);
2261
3084
  }
2262
3085
  sections.push(`**Docker Swarm Cluster Details**\n${clusterDetails.join("\n")}`);
2263
3086
  }
2264
3087
  // Affected tasks: a ranked list of task, service and node
2265
3088
  let renderedBreakdownList = false;
2266
- if (breakdown && breakdown.affectedResources.length > 0) {
2267
- /*
2268
- * K8s/Proxmox-parity render: drop zero-value rows, worst (highest)
2269
- * first, top 10. For the task-down template the breaching rows ARE
2270
- * the zero-uptime ones — those still drive alerting through the
2271
- * per-series criteria evaluation; this list is supplementary
2272
- * context only.
2273
- */
2274
- const sortedResources = [
2275
- ...breakdown.affectedResources,
2276
- ]
2277
- .filter((r) => {
2278
- return r.metricValue > 0;
2279
- })
2280
- .sort((a, b) => {
2281
- return b.metricValue - a.metricValue;
2282
- });
2283
- /*
2284
- * Skip the list when no row carries any identity label
2285
- * (cluster-wide series) — it would add nothing.
2286
- */
2287
- const hasIdentity = sortedResources.some((r) => {
2288
- return r.containerName || r.serviceName || r.nodeName;
2289
- });
2290
- if (sortedResources.length > 0 && hasIdentity) {
2291
- const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
2292
- sections.push(AffectedResourceList.render({
2293
- heading: "Affected Tasks",
2294
- overflowNoun: "affected tasks",
2295
- totalCount: sortedResources.length,
2296
- entries: resourcesToShow.map((resource) => {
2297
- return MonitorCriteriaEvaluator.getDockerSwarmAffectedResourceEntry({
2298
- resource: resource,
2299
- clusterName: breakdown.clusterName,
2300
- value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
2301
- platform: "dockerSwarm",
2302
- metricName: breakdown.metricName,
2303
- value: resource.metricValue,
2304
- }),
3089
+ /*
3090
+ * The tasks the matched criteria breached, worst first, top 10. The
3091
+ * task-down template fires on `container.uptime < 60`, so the worst
3092
+ * task is the one with the LOWEST uptime; the old `> 0`,
3093
+ * highest-first list put the healthiest, longest-running tasks at the
3094
+ * top and pushed the restarting one off the end.
3095
+ */
3096
+ const rows = MonitorCriteriaEvaluator.getPlatformAffectedRows({
3097
+ platform: "dockerSwarm",
3098
+ perSeriesMatches: input.perSeriesMatches,
3099
+ namesObject: (identity) => {
3100
+ return Boolean(identity.containerName || identity.serviceName || identity.nodeName);
3101
+ },
3102
+ metricResponse: metricResponse,
3103
+ monitorStep: input.monitorStep,
3104
+ target: target,
3105
+ breakdown: breakdown,
3106
+ breach: MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
3107
+ criteriaInstance: input.criteriaInstance,
3108
+ }),
3109
+ toIdentity: PlatformResourceIdentity.dockerSwarm,
3110
+ withContext: PlatformResourceIdentity.withDockerSwarmContext,
3111
+ });
3112
+ /*
3113
+ * Skip the list when no row carries any identity label
3114
+ * (cluster-wide series) — it would add nothing.
3115
+ */
3116
+ const hasIdentity = (rows || []).some((row) => {
3117
+ return Boolean(row.seriesLabels ||
3118
+ row.identity.containerName ||
3119
+ row.identity.serviceName ||
3120
+ row.identity.nodeName);
3121
+ });
3122
+ if (rows && rows.length > 0 && hasIdentity) {
3123
+ sections.push(AffectedResourceList.render({
3124
+ heading: "Affected Tasks",
3125
+ overflowNoun: "affected tasks",
3126
+ totalCount: rows.length,
3127
+ entries: rows
3128
+ .slice(0, AffectedResourceList.MAX_ENTRIES)
3129
+ .map((row) => {
3130
+ return row.seriesLabels
3131
+ ? MonitorCriteriaEvaluator.getSeriesLabelEntry({
3132
+ labels: row.seriesLabels,
3133
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
3134
+ })
3135
+ : MonitorCriteriaEvaluator.getDockerSwarmAffectedResourceEntry({
3136
+ resource: row.identity,
3137
+ clusterName: clusterName,
3138
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
2305
3139
  });
2306
- }),
2307
- }));
2308
- renderedBreakdownList = true;
2309
- }
3140
+ }),
3141
+ }));
3142
+ renderedBreakdownList = true;
2310
3143
  }
2311
3144
  // Metric results summary (fallback context when no list rendered)
2312
3145
  if (!renderedBreakdownList &&
@@ -2353,40 +3186,114 @@ ${contextBlock}
2353
3186
  */
2354
3187
  static getAffectedResourceBreachPredicate(input) {
2355
3188
  var _a, _b, _c, _d, _e, _f;
2356
- const metricFilters = (((_b = (_a = input.criteriaInstance) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.filters) || []).filter((f) => {
2357
- return f.checkOn === CheckOn.MetricValue && typeof f.value === "number";
3189
+ /*
3190
+ * Thresholds read exactly as the criteria evaluation reads them. The
3191
+ * dashboard's criteria form saves the threshold as a string ("60"), so
3192
+ * a `typeof === "number"` test dropped every filter a user had edited
3193
+ * and quietly put their criteria back on the `> 0`, highest-first rule.
3194
+ */
3195
+ const metricFilters = (((_b = (_a = input.criteriaInstance) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.filters) || [])
3196
+ .filter((f) => {
3197
+ return f.checkOn === CheckOn.MetricValue;
3198
+ })
3199
+ .map((f) => {
3200
+ return {
3201
+ filter: f,
3202
+ threshold: CompareCriteria.convertMetricThresholdToNumber(f.value),
3203
+ };
3204
+ })
3205
+ .filter((f) => {
3206
+ return f.threshold !== null;
2358
3207
  });
2359
3208
  const opensIncidentOrAlert = ((_d = (_c = input.criteriaInstance) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.createIncidents) === true ||
2360
3209
  ((_f = (_e = input.criteriaInstance) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.createAlerts) === true;
2361
3210
  const firesWhenMetricFalls = metricFilters.length > 0 &&
2362
3211
  metricFilters.every((f) => {
2363
- if (f.filterType === FilterType.LessThan ||
2364
- f.filterType === FilterType.LessThanOrEqualTo) {
3212
+ if (f.filter.filterType === FilterType.LessThan ||
3213
+ f.filter.filterType === FilterType.LessThanOrEqualTo) {
2365
3214
  return true;
2366
3215
  }
2367
3216
  return (input.floorEqualityFiresOnFall === true &&
2368
3217
  opensIncidentOrAlert &&
2369
- f.filterType === FilterType.EqualTo &&
2370
- f.value <= 0);
3218
+ f.filter.filterType === FilterType.EqualTo &&
3219
+ f.threshold <= 0);
2371
3220
  });
2372
- if (!firesWhenMetricFalls) {
3221
+ /*
3222
+ * A firing criteria that asks for one exact, non-zero value —
3223
+ * k8s-pod-pending fires on phase `= 1` (Pending) — lists exactly the
3224
+ * rows that were at that value. The `> 0` rule below listed every pod
3225
+ * with a phase, which is all of them, led by the one with the highest
3226
+ * code (Unknown), and sent the reader to `kubectl describe` the wrong
3227
+ * pod.
3228
+ *
3229
+ * A row was at the value if either end of its window was: pod-pending
3230
+ * watches the phase at its Min (a pod that has since started Running
3231
+ * still breached), a memory-pressure `= 1` watches the Max. `= 0` stays
3232
+ * with the floor rule above, which each platform opts into.
3233
+ */
3234
+ const exactValues = metricFilters.map((f) => {
3235
+ return f.threshold;
3236
+ });
3237
+ const firesOnExactValue = !firesWhenMetricFalls &&
3238
+ opensIncidentOrAlert &&
3239
+ metricFilters.length > 0 &&
3240
+ metricFilters.every((f) => {
3241
+ return f.filter.filterType === FilterType.EqualTo && f.threshold > 0;
3242
+ });
3243
+ if (firesOnExactValue) {
2373
3244
  return {
2374
3245
  matches: (value) => {
2375
- return value > 0;
3246
+ return exactValues.includes(value);
2376
3247
  },
2377
3248
  worstIsLowest: false,
3249
+ pickEnd: (ends) => {
3250
+ return exactValues.includes(ends.highest) ? "highest" : "lowest";
3251
+ },
3252
+ };
3253
+ }
3254
+ /*
3255
+ * A criteria that fires when the metric RISES lists the rows past its
3256
+ * threshold. The blanket `> 0` listed every resource with any value at
3257
+ * all — the healthy nodes at 50% under a "> 90%" alert. When no single
3258
+ * row is past it (the criteria compared a sum or an average across
3259
+ * resources) the list still falls back to the non-zero rows.
3260
+ */
3261
+ const firesWhenMetricRises = metricFilters.length > 0 &&
3262
+ metricFilters.every((f) => {
3263
+ return (f.filter.filterType === FilterType.GreaterThan ||
3264
+ f.filter.filterType === FilterType.GreaterThanOrEqualTo);
3265
+ });
3266
+ const isNonZero = (value) => {
3267
+ return value > 0;
3268
+ };
3269
+ if (firesWhenMetricRises && !firesWhenMetricFalls) {
3270
+ return {
3271
+ matches: (value) => {
3272
+ return metricFilters.some((f) => {
3273
+ return f.filter.filterType === FilterType.GreaterThan
3274
+ ? value > f.threshold
3275
+ : value >= f.threshold;
3276
+ });
3277
+ },
3278
+ worstIsLowest: false,
3279
+ fallbackMatches: isNonZero,
3280
+ };
3281
+ }
3282
+ if (!firesWhenMetricFalls) {
3283
+ return {
3284
+ matches: isNonZero,
3285
+ worstIsLowest: false,
2378
3286
  };
2379
3287
  }
2380
3288
  return {
2381
3289
  matches: (value) => {
2382
3290
  return metricFilters.some((f) => {
2383
- const threshold = f.value;
2384
- if (f.filterType === FilterType.EqualTo) {
2385
- return value === threshold;
3291
+ if (f.filter.filterType === FilterType.EqualTo) {
3292
+ return value === f.threshold;
2386
3293
  }
2387
- return f.filterType === FilterType.LessThan
2388
- ? value < threshold
2389
- : value <= threshold;
3294
+ return f.filter.filterType === FilterType.LessThan
3295
+ ? value < f.threshold
3296
+ : value <= f.threshold;
2390
3297
  });
2391
3298
  },
2392
3299
  /*
@@ -2462,87 +3369,107 @@ ${contextBlock}
2462
3369
  };
2463
3370
  }
2464
3371
  static buildCephRootCauseContext(input) {
2465
- var _a, _b, _c, _d, _e, _f, _g;
3372
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2466
3373
  const metricResponse = input.dataToProcess;
2467
- const breakdown = metricResponse.cephResourceBreakdown;
2468
- const sections = [];
2469
3374
  // Ceph cluster context
2470
3375
  const cephMonitor = (_a = input.monitorStep.data) === null || _a === void 0 ? void 0 : _a.cephMonitor;
3376
+ const target = MonitorCriteriaEvaluator.resolveCriteriaMetricTarget({
3377
+ criteriaInstance: input.criteriaInstance,
3378
+ monitorStep: input.monitorStep,
3379
+ metricResponse: metricResponse,
3380
+ });
3381
+ const breakdown = MonitorCriteriaEvaluator.selectPlatformBreakdown({
3382
+ breakdowns: metricResponse.cephResourceBreakdowns,
3383
+ legacyBreakdown: metricResponse.cephResourceBreakdown,
3384
+ target: target,
3385
+ });
3386
+ const clusterName = (breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) || (cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.clusterIdentifier) || "Unknown";
3387
+ const sections = [];
2471
3388
  if (cephMonitor || breakdown) {
2472
3389
  const clusterDetails = [];
2473
- clusterDetails.push(`- Cluster: ${(breakdown === null || breakdown === void 0 ? void 0 : breakdown.clusterName) || (cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.clusterIdentifier) || "Unknown"}`);
2474
- if (breakdown) {
2475
- clusterDetails.push(`- Metric: ${breakdown.metricFriendlyName} (\`${breakdown.metricName}\`)`);
2476
- }
2477
- else if (cephMonitor &&
2478
- ((_c = (_b = cephMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
2479
- cephMonitor.metricViewConfig.queryConfigs[0]) {
2480
- const metricName = (_e = (_d = cephMonitor.metricViewConfig.queryConfigs[0]
2481
- .metricQueryData) === null || _d === void 0 ? void 0 : _d.filterData) === null || _e === void 0 ? void 0 : _e.metricName;
2482
- if (metricName) {
2483
- clusterDetails.push(`- Metric: \`${metricName}\``);
2484
- }
2485
- }
2486
- if ((_f = cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.resourceFilters) === null || _f === void 0 ? void 0 : _f.osdId) {
3390
+ clusterDetails.push(`- Cluster: ${clusterName}`);
3391
+ clusterDetails.push(...MonitorCriteriaEvaluator.describeCriteriaMetric({
3392
+ platform: "ceph",
3393
+ target: target,
3394
+ breakdown: breakdown,
3395
+ fallbackMetricName: (_f = (_e = (_d = (_c = (_b = cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.metricViewConfig) === null || _b === void 0 ? void 0 : _b.queryConfigs) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.metricQueryData) === null || _e === void 0 ? void 0 : _e.filterData) === null || _f === void 0 ? void 0 : _f.metricName,
3396
+ }));
3397
+ if ((_g = cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.osdId) {
2487
3398
  clusterDetails.push(`- OSD Filter: ${cephMonitor.resourceFilters.osdId}`);
2488
3399
  }
2489
- if ((_g = cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.resourceFilters) === null || _g === void 0 ? void 0 : _g.poolId) {
3400
+ if ((_h = cephMonitor === null || cephMonitor === void 0 ? void 0 : cephMonitor.resourceFilters) === null || _h === void 0 ? void 0 : _h.poolId) {
2490
3401
  clusterDetails.push(`- Pool ID Filter: ${cephMonitor.resourceFilters.poolId}`);
2491
3402
  }
2492
3403
  sections.push(`**Ceph Cluster Details**\n${clusterDetails.join("\n")}`);
2493
3404
  }
2494
3405
  // Affected resources: a ranked list of daemon, pool and host
2495
3406
  let renderedBreakdownList = false;
2496
- if (breakdown && breakdown.affectedResources.length > 0) {
2497
- /*
2498
- * Keep the rows that satisfy the criteria that just matched, worst
2499
- * first, top 10 — NOT simply the non-zero rows. For ceph_osd_up /
2500
- * ceph_osd_in / ceph_mon_quorum_status the criteria is `< 1`, so
2501
- * the zero rows are the whole point of the incident.
2502
- */
2503
- const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
3407
+ /*
3408
+ * Keep the rows that satisfy the criteria that just matched, worst
3409
+ * first, top 10 — NOT simply the non-zero rows. For ceph_osd_up /
3410
+ * ceph_osd_in / ceph_mon_quorum_status the criteria is `< 1`, so
3411
+ * the zero rows are the whole point of the incident.
3412
+ */
3413
+ const rows = MonitorCriteriaEvaluator.getPlatformAffectedRows({
3414
+ platform: "ceph",
3415
+ perSeriesMatches: input.perSeriesMatches,
3416
+ namesObject: (identity) => {
3417
+ return Boolean(identity.daemon ||
3418
+ identity.poolId ||
3419
+ identity.poolName ||
3420
+ identity.hostname);
3421
+ },
3422
+ metricResponse: metricResponse,
3423
+ monitorStep: input.monitorStep,
3424
+ target: target,
3425
+ breakdown: breakdown,
3426
+ breach: MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
2504
3427
  criteriaInstance: input.criteriaInstance,
2505
- });
2506
- const sortedResources = [
2507
- ...breakdown.affectedResources,
2508
- ]
2509
- .filter((r) => {
2510
- return breach.matches(r.metricValue);
2511
- })
2512
- .sort((a, b) => {
2513
- return breach.worstIsLowest
2514
- ? a.metricValue - b.metricValue
2515
- : b.metricValue - a.metricValue;
2516
- });
2517
- /*
2518
- * Skip the list when no row carries any identity label
2519
- * (cluster-wide series like ceph_health_status) — it would add
2520
- * nothing.
2521
- */
2522
- const hasIdentity = sortedResources.some((r) => {
2523
- return r.daemon || r.poolId || r.poolName || r.hostname;
2524
- });
2525
- if (sortedResources.length > 0 && hasIdentity) {
2526
- const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
2527
- sections.push(AffectedResourceList.render({
2528
- heading: "Affected Resources",
2529
- overflowNoun: "affected resources",
2530
- totalCount: sortedResources.length,
2531
- entries: resourcesToShow.map((resource) => {
2532
- return MonitorCriteriaEvaluator.getCephAffectedResourceEntry({
2533
- resource: resource,
2534
- clusterName: breakdown.clusterName,
2535
- metricName: breakdown.metricName,
2536
- value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
2537
- platform: "ceph",
2538
- metricName: breakdown.metricName,
2539
- value: resource.metricValue,
2540
- }),
3428
+ }),
3429
+ toIdentity: PlatformResourceIdentity.ceph,
3430
+ withContext: PlatformResourceIdentity.withCephContext,
3431
+ });
3432
+ /*
3433
+ * Skip the list when no row carries any identity label
3434
+ * (cluster-wide series like ceph_health_status) — it would add
3435
+ * nothing.
3436
+ */
3437
+ const hasIdentity = (rows || []).some((row) => {
3438
+ return Boolean(row.seriesLabels ||
3439
+ row.identity.daemon ||
3440
+ row.identity.poolId ||
3441
+ row.identity.poolName ||
3442
+ row.identity.hostname);
3443
+ });
3444
+ /*
3445
+ * Which metric the rows are about, for the entry's "Health Check"
3446
+ * titling: the criteria's own query, else the breakdown's.
3447
+ */
3448
+ const rowsMetricName = MonitorCriteriaEvaluator.getAnalysisMetricName(target) ||
3449
+ (breakdown === null || breakdown === void 0 ? void 0 : breakdown.metricName) ||
3450
+ "";
3451
+ if (rows && rows.length > 0 && hasIdentity) {
3452
+ sections.push(AffectedResourceList.render({
3453
+ heading: "Affected Resources",
3454
+ overflowNoun: "affected resources",
3455
+ totalCount: rows.length,
3456
+ entries: rows
3457
+ .slice(0, AffectedResourceList.MAX_ENTRIES)
3458
+ .map((row) => {
3459
+ return row.seriesLabels
3460
+ ? MonitorCriteriaEvaluator.getSeriesLabelEntry({
3461
+ labels: row.seriesLabels,
3462
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
3463
+ })
3464
+ : MonitorCriteriaEvaluator.getCephAffectedResourceEntry({
3465
+ resource: row.identity,
3466
+ clusterName: clusterName,
3467
+ metricName: rowsMetricName,
3468
+ value: MonitorCriteriaEvaluator.renderAffectedRowValue(row),
2541
3469
  });
2542
- }),
2543
- }));
2544
- renderedBreakdownList = true;
2545
- }
3470
+ }),
3471
+ }));
3472
+ renderedBreakdownList = true;
2546
3473
  }
2547
3474
  // Metric results summary (fallback context when no list rendered)
2548
3475
  if (!renderedBreakdownList &&
@@ -2576,14 +3503,13 @@ ${contextBlock}
2576
3503
  * kubeletstats cores gauge that four other places in this repo warn
2577
3504
  * is misnamed, so 1.4 cores in use read as "1.4% CPU utilization".
2578
3505
  */
2579
- const topResourceValue = MetricValueFormatter.format({
2580
- value: topResource.metricValue,
2581
- unit: MonitorCriteriaEvaluator.getPlatformMetricUnit({
3506
+ const topResourceValue = input.topResourceValue ||
3507
+ MonitorCriteriaEvaluator.formatPlatformMetricValue({
2582
3508
  platform: "kubernetes",
2583
3509
  metricName: metricName,
2584
- }),
2585
- metricName: metricName,
2586
- });
3510
+ metricUnit: breakdown.metricUnit,
3511
+ value: topResource.metricValue,
3512
+ });
2587
3513
  if (metricName === "k8s.container.restarts" ||
2588
3514
  metricName.includes("restart")) {
2589
3515
  lines.push(`Container restart count is elevated, indicating a potential CrashLoopBackOff condition.`);
@@ -2620,6 +3546,12 @@ ${contextBlock}
2620
3546
  lines.push(`Recommended actions: Check node conditions with \`kubectl describe node ${topResource.nodeName || "<node-name>"}\` and verify kubelet status on the node.`);
2621
3547
  }
2622
3548
  else if (metricName === "k8s.node.cpu.utilization" ||
3549
+ /*
3550
+ * The numerator of the "High Node CPU Utilization" ratio template
3551
+ * (k8s.node.cpu.usage ÷ k8s.node.allocatable_cpu × 100), which is
3552
+ * what a formula criteria is analysed as.
3553
+ */
3554
+ metricName === "k8s.node.cpu.usage" ||
2623
3555
  /*
2624
3556
  * Node-scoped ONLY. The bare substring test used to swallow every
2625
3557
  * pod- and container-scoped CPU metric — `k8s.pod.cpu.utilization`,
@@ -2651,8 +3583,16 @@ ${contextBlock}
2651
3583
  lines.push(`Common causes: memory leaks in applications, insufficient memory limits on pods, too many pods scheduled on the node, or growing dataset sizes.`);
2652
3584
  lines.push(`Recommended actions: Check memory consumers with \`kubectl top pods --all-namespaces --sort-by=memory\` and review pod memory limits. Consider scaling the cluster or adding nodes with more memory.`);
2653
3585
  }
2654
- else if (metricName === "k8s.deployment.unavailable_replicas" ||
2655
- metricName.includes("unavailable")) {
3586
+ else if (
3587
+ /*
3588
+ * The k8s_cluster receiver has no unavailable-replicas series. The
3589
+ * replica-mismatch template is a formula (`desired - available`),
3590
+ * analysed as its first operand, k8s.deployment.desired, so it lands
3591
+ * in the generic branch below, which names the formula. What still
3592
+ * reaches this one is a custom monitor on an "unavailable" series
3593
+ * such as kube-state-metrics' `kube_deployment_status_replicas_unavailable`.
3594
+ */
3595
+ metricName.includes("unavailable")) {
2656
3596
  lines.push(`Deployment has unavailable replicas, indicating a mismatch between desired and available replicas.`);
2657
3597
  if (topResource.workloadName) {
2658
3598
  lines.push(`${topResource.workloadType || "Deployment"} \`${topResource.workloadName}\` has **${topResourceValue}** unavailable replica(s).`);
@@ -2689,13 +3629,38 @@ ${contextBlock}
2689
3629
  lines.push(`Recommended actions: Check DaemonSet status with \`kubectl describe daemonset ${topResource.workloadName || "<daemonset>"}\` and verify node labels and taints.`);
2690
3630
  }
2691
3631
  else {
2692
- // Generic Kubernetes context
2693
- lines.push(`Kubernetes metric \`${metricName}\` (${breakdown.metricFriendlyName}) has breached the configured threshold.`);
3632
+ /*
3633
+ * Generic Kubernetes context. It names what the criteria compared —
3634
+ * a formula by its legend and expression, since the metric the
3635
+ * branches above key on is only its numerator — and the worst
3636
+ * resource's value, so the reader does not have to find it in the
3637
+ * list above.
3638
+ */
3639
+ const target = input.target;
3640
+ if (target === null || target === void 0 ? void 0 : target.isFormula) {
3641
+ const formulaName = target.displayName || `\`${target.alias}\``;
3642
+ const expression = target.formulaExpression
3643
+ ? ` (\`${target.formulaExpression}\`)`
3644
+ : "";
3645
+ lines.push(`${formulaName}${expression} has breached the configured threshold.`);
3646
+ }
3647
+ else {
3648
+ const friendlyName = breakdown.metricFriendlyName &&
3649
+ breakdown.metricFriendlyName !== metricName
3650
+ ? ` (${breakdown.metricFriendlyName})`
3651
+ : "";
3652
+ lines.push(`Kubernetes metric \`${metricName}\`${friendlyName} has breached the configured threshold.`);
3653
+ }
2694
3654
  if (topResource.podName) {
2695
- lines.push(`Most affected pod: \`${topResource.podName}\``);
3655
+ lines.push(`Most affected pod: \`${topResource.podName}\` (**${topResourceValue}**)`);
3656
+ }
3657
+ else if (topResource.workloadName) {
3658
+ lines.push(`Most affected ${topResource.workloadType || "workload"}: \`${topResource.workloadName}\` (**${topResourceValue}**)`);
2696
3659
  }
2697
3660
  if (topResource.nodeName) {
2698
- lines.push(`Most affected node: \`${topResource.nodeName}\``);
3661
+ lines.push(topResource.podName
3662
+ ? `Most affected node: \`${topResource.nodeName}\``
3663
+ : `Most affected node: \`${topResource.nodeName}\` (**${topResourceValue}**)`);
2699
3664
  }
2700
3665
  lines.push(`Recommended actions: Investigate the affected resources using \`kubectl describe\` and \`kubectl logs\` commands.`);
2701
3666
  }