@oneuptime/common 12.0.13 → 12.0.15

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 (561) hide show
  1. package/Models/DatabaseModels/GlobalOidc.ts +20 -0
  2. package/Models/DatabaseModels/GlobalSso.ts +20 -0
  3. package/Models/DatabaseModels/MarketingConversion.ts +19 -9
  4. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +46 -0
  5. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  6. package/Models/DatabaseModels/StatusPage.ts +7 -4
  7. package/Models/DatabaseModels/UserCall.ts +94 -3
  8. package/Models/DatabaseModels/UserEmail.ts +94 -3
  9. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  10. package/Models/DatabaseModels/UserPush.ts +37 -0
  11. package/Models/DatabaseModels/UserSMS.ts +94 -3
  12. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  13. package/Server/API/BaseAPI.ts +79 -14
  14. package/Server/API/StatusPageAPI.ts +29 -4
  15. package/Server/API/UserCallAPI.ts +53 -45
  16. package/Server/API/UserEmailAPI.ts +53 -44
  17. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  18. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  19. package/Server/API/UserPushAPI.ts +126 -2
  20. package/Server/API/UserSmsAPI.ts +53 -44
  21. package/Server/API/UserWhatsAppAPI.ts +53 -50
  22. package/Server/EnvironmentConfig.ts +7 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.ts +19 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.ts +40 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  29. package/Server/Middleware/SlackAuthorization.ts +43 -6
  30. package/Server/Middleware/UserAuthorization.ts +236 -26
  31. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  32. package/Server/Middleware/WhatsAppAuthorization.ts +43 -3
  33. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  34. package/Server/Services/DatabaseService.ts +101 -0
  35. package/Server/Services/GlobalOidcProjectService.ts +126 -1
  36. package/Server/Services/GlobalOidcService.ts +132 -0
  37. package/Server/Services/GlobalSsoProjectService.ts +126 -1
  38. package/Server/Services/GlobalSsoService.ts +132 -0
  39. package/Server/Services/LogAggregationService.ts +5 -0
  40. package/Server/Services/PushNotificationService.ts +127 -12
  41. package/Server/Services/TeamMemberService.ts +53 -13
  42. package/Server/Services/UserCallService.ts +55 -24
  43. package/Server/Services/UserEmailService.ts +83 -17
  44. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  45. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  46. package/Server/Services/UserNotificationRuleService.ts +26 -0
  47. package/Server/Services/UserPushService.ts +89 -0
  48. package/Server/Services/UserSmsService.ts +50 -17
  49. package/Server/Services/UserWhatsAppService.ts +48 -15
  50. package/Server/Types/Database/QueryHelper.ts +30 -0
  51. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  52. package/Server/Utils/ChannelVerification.ts +510 -0
  53. package/Server/Utils/CronTab.ts +73 -0
  54. package/Server/Utils/GlobalSsoAuthorization.ts +175 -0
  55. package/Server/Utils/Marketing/ConversionUploadProvider.ts +35 -5
  56. package/Server/Utils/Marketing/Providers/GoogleAds.ts +1 -1
  57. package/Server/Utils/Marketing/Providers/LinkedIn.ts +1 -1
  58. package/Server/Utils/Marketing/Providers/Meta.ts +1 -1
  59. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +1 -1
  60. package/Server/Utils/Marketing/Providers/Reddit.ts +1 -1
  61. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +48 -33
  62. package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +35 -30
  63. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +33 -28
  64. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +475 -47
  65. package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +35 -30
  66. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +47 -44
  67. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +33 -28
  68. package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +69 -40
  69. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +35 -29
  70. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  71. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +8 -0
  72. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  73. package/Server/Utils/Monitor/MonitorResource.ts +161 -21
  74. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  75. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  76. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  77. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  78. package/Server/Utils/StatusPageContentSecurityPolicy.ts +56 -0
  79. package/Server/Utils/StatusPageCustomizationAccess.ts +117 -0
  80. package/Server/Utils/TotpAuth.ts +137 -9
  81. package/Server/Utils/UserRegistrationToken.ts +182 -0
  82. package/Server/Utils/VerificationCode.ts +134 -0
  83. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  84. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  85. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  86. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  87. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  88. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  89. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  90. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  91. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  92. package/Tests/Server/API/StatusPageCustomizationOrigin.test.ts +348 -0
  93. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  94. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  95. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  96. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  97. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  98. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +265 -0
  99. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +227 -0
  100. package/Tests/Server/Middleware/SlackAuthorization.test.ts +149 -0
  101. package/Tests/Server/Middleware/UserAuthorization.test.ts +27 -13
  102. package/Tests/Server/Middleware/UserAuthorizationGlobalSsoGovernance.test.ts +1455 -0
  103. package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +91 -2
  104. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  105. package/Tests/Server/Middleware/WhatsAppAuthorization.test.ts +233 -0
  106. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  107. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  108. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  109. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  110. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  111. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  112. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  113. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +21 -8
  114. package/Tests/Server/Services/GlobalSsoProviderAuthorization.test.ts +1242 -0
  115. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  116. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  117. package/Tests/Server/Services/MonitorResourceProbeAgreementHydration.test.ts +459 -0
  118. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +10 -0
  119. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  120. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  121. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  122. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  123. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  124. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  125. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  126. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  127. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  128. package/Tests/Server/Types/AnalyticsDatabase/AggregateUtilBucketTimestamp.test.ts +416 -0
  129. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  130. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  131. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  132. package/Tests/Server/Types/Database/QueryHelperManyToManyAndEmptyValues.test.ts +551 -0
  133. package/Tests/Server/Types/Database/SelectUtil.test.ts +348 -0
  134. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  135. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  136. package/Tests/Server/Utils/Browser.test.ts +162 -0
  137. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  138. package/Tests/Server/Utils/CronTab.test.ts +116 -0
  139. package/Tests/Server/Utils/Express.test.ts +371 -0
  140. package/Tests/Server/Utils/GlobalSSOToken.test.ts +581 -0
  141. package/Tests/Server/Utils/GlobalSsoAuthorization.test.ts +1401 -0
  142. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +254 -0
  143. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +1 -1
  144. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +508 -0
  145. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +30 -9
  146. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  147. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  148. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +987 -0
  149. package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +916 -0
  150. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  151. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  152. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +382 -0
  153. package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +1219 -0
  154. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  155. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  156. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  157. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  158. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +86 -0
  159. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  160. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  161. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  162. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  163. package/Tests/Server/Utils/StatusPageContentSecurityPolicy.test.ts +136 -0
  164. package/Tests/Server/Utils/StatusPageCustomizationAccess.test.ts +223 -0
  165. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  166. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  167. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  168. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  169. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  170. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  171. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  172. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  173. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  174. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  175. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  176. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  177. package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +165 -0
  178. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  179. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  180. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  181. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  182. package/Tests/Types/NetworkDevice/NetworkDeviceLinkRuleScope.test.ts +215 -0
  183. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  184. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  185. package/Tests/UI/Components/Button.test.tsx +175 -1
  186. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  187. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  188. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  189. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  190. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  191. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  192. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  193. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  194. package/Tests/UI/Components/Forms/FormSubmitAnalytics.test.tsx +206 -0
  195. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  196. package/Tests/UI/Components/List.test.tsx +9 -2
  197. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  198. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  199. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  200. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  201. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  202. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  203. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  204. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  205. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  206. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  207. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  208. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  209. package/Tests/UI/Components/Toast.test.tsx +130 -7
  210. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  211. package/Tests/UI/Utils/AIChatExport/ConversationMarkdown.test.ts +774 -0
  212. package/Tests/UI/Utils/AIChatExport/MarkdownBlocks.test.ts +791 -0
  213. package/Tests/UI/Utils/AIChatExport/MarkdownSafety.test.ts +544 -0
  214. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  215. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  216. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  217. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  218. package/Tests/Utils/API.test.ts +16 -2
  219. package/Tests/Utils/Analytics.test.ts +187 -0
  220. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  221. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  222. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  223. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +80 -0
  224. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +1291 -0
  225. package/Tests/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.test.ts +214 -0
  226. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  227. package/Types/Email/EmailTemplateType.ts +1 -0
  228. package/Types/Marketing/MarketingConversion.ts +12 -0
  229. package/Types/Monitor/CriteriaFilter.ts +9 -0
  230. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  231. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  232. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  233. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  234. package/Types/NetworkDevice/NetworkDeviceLinkRuleScope.ts +51 -0
  235. package/Types/Permission.ts +35 -0
  236. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  237. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  238. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  239. package/Types/Service/ServiceLanguage.ts +144 -0
  240. package/Types/Text.ts +111 -19
  241. package/UI/Components/Button/Button.tsx +27 -14
  242. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  243. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  244. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  245. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  246. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  247. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  248. package/UI/Components/CommandPalette/Types.ts +49 -0
  249. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  250. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  251. package/UI/Components/Feed/FeedItem.tsx +1 -1
  252. package/UI/Components/FormModal/BasicFormModal.tsx +12 -0
  253. package/UI/Components/Forms/BasicForm.tsx +13 -1
  254. package/UI/Components/Forms/BasicModelForm.tsx +2 -1
  255. package/UI/Components/Forms/ModelForm.tsx +10 -1
  256. package/UI/Components/Forms/Utils/FormAnalyticsName.ts +39 -0
  257. package/UI/Components/List/List.tsx +37 -5
  258. package/UI/Components/List/ListSkeleton.tsx +54 -0
  259. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  260. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  261. package/UI/Components/Modal/Modal.tsx +2 -2
  262. package/UI/Components/ModelFormModal/ModelFormModal.tsx +6 -1
  263. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  264. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  265. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  266. package/UI/Components/Navbar/NavBar.tsx +14 -1
  267. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  268. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  269. package/UI/Components/SideOver/SideOver.tsx +37 -2
  270. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  271. package/UI/Components/Table/Table.tsx +74 -44
  272. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  273. package/UI/Components/Tabs/Tab.tsx +1 -1
  274. package/UI/Components/Toast/Toast.tsx +118 -53
  275. package/UI/Components/Toast/ToastInit.tsx +82 -32
  276. package/UI/Styles/Theme.css +40 -0
  277. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  278. package/UI/Utils/ModelListCache.ts +188 -0
  279. package/UI/Utils/Project.ts +29 -0
  280. package/Utils/API.ts +6 -1
  281. package/Utils/Analytics.ts +55 -2
  282. package/Utils/Monitor/MonitorMetricType.ts +40 -0
  283. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +587 -37
  284. package/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.ts +115 -0
  285. package/build/dist/Models/DatabaseModels/GlobalOidc.js +21 -0
  286. package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
  287. package/build/dist/Models/DatabaseModels/GlobalSso.js +21 -0
  288. package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
  289. package/build/dist/Models/DatabaseModels/MarketingConversion.js +20 -10
  290. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +1 -1
  291. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +47 -0
  292. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -1
  293. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  294. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  295. package/build/dist/Models/DatabaseModels/StatusPage.js +4 -4
  296. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  297. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  298. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  299. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  300. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  301. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  302. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  303. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  304. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  305. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  306. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  307. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  308. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  309. package/build/dist/Server/API/BaseAPI.js +69 -13
  310. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  311. package/build/dist/Server/API/StatusPageAPI.js +25 -4
  312. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  313. package/build/dist/Server/API/UserCallAPI.js +42 -38
  314. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  315. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  316. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  317. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  318. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  319. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  320. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  321. package/build/dist/Server/API/UserPushAPI.js +95 -2
  322. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  323. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  324. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  325. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  326. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  327. package/build/dist/Server/EnvironmentConfig.js +6 -0
  328. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  329. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  330. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  331. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  332. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  333. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  334. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  335. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js +12 -0
  336. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js.map +1 -0
  337. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js +29 -0
  338. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js.map +1 -0
  339. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  340. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  341. package/build/dist/Server/Middleware/SlackAuthorization.js +28 -4
  342. package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
  343. package/build/dist/Server/Middleware/UserAuthorization.js +197 -22
  344. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  345. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  346. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  347. package/build/dist/Server/Middleware/WhatsAppAuthorization.js +32 -2
  348. package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -1
  349. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  350. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  351. package/build/dist/Server/Services/DatabaseService.js +74 -0
  352. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  353. package/build/dist/Server/Services/GlobalOidcProjectService.js +105 -0
  354. package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -1
  355. package/build/dist/Server/Services/GlobalOidcService.js +127 -0
  356. package/build/dist/Server/Services/GlobalOidcService.js.map +1 -1
  357. package/build/dist/Server/Services/GlobalSsoProjectService.js +105 -0
  358. package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -1
  359. package/build/dist/Server/Services/GlobalSsoService.js +127 -0
  360. package/build/dist/Server/Services/GlobalSsoService.js.map +1 -1
  361. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  362. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  363. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  364. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  365. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  366. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  367. package/build/dist/Server/Services/UserCallService.js +54 -22
  368. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  369. package/build/dist/Server/Services/UserEmailService.js +78 -17
  370. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  371. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  372. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  373. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  374. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  375. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  376. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  377. package/build/dist/Server/Services/UserPushService.js +77 -0
  378. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  379. package/build/dist/Server/Services/UserSmsService.js +50 -17
  380. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  381. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  382. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  383. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  384. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  385. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  386. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  387. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  388. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  389. package/build/dist/Server/Utils/CronTab.js +66 -0
  390. package/build/dist/Server/Utils/CronTab.js.map +1 -1
  391. package/build/dist/Server/Utils/GlobalSsoAuthorization.js +112 -0
  392. package/build/dist/Server/Utils/GlobalSsoAuthorization.js.map +1 -0
  393. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +17 -1
  394. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +1 -1
  395. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +1 -1
  396. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +1 -1
  397. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +1 -1
  398. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +1 -1
  399. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +1 -1
  400. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +1 -1
  401. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +1 -1
  402. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +1 -1
  403. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +1 -1
  404. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +1 -1
  405. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +37 -35
  406. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  407. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +21 -25
  408. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -1
  409. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +20 -22
  410. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  411. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +370 -42
  412. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  413. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +21 -25
  414. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -1
  415. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +34 -37
  416. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  417. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +21 -25
  418. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  419. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +64 -52
  420. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
  421. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +25 -29
  422. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  423. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  424. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  425. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +8 -0
  426. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  427. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  428. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  429. package/build/dist/Server/Utils/Monitor/MonitorResource.js +123 -23
  430. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  431. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  432. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  433. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  434. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  435. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  436. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  437. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  438. package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js +39 -0
  439. package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js.map +1 -0
  440. package/build/dist/Server/Utils/StatusPageCustomizationAccess.js +85 -0
  441. package/build/dist/Server/Utils/StatusPageCustomizationAccess.js.map +1 -0
  442. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  443. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  444. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  445. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  446. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  447. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  448. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  449. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  450. package/build/dist/Types/Marketing/MarketingConversion.js +10 -0
  451. package/build/dist/Types/Marketing/MarketingConversion.js.map +1 -1
  452. package/build/dist/Types/Monitor/CriteriaFilter.js +1 -0
  453. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  454. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  455. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  456. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  457. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  458. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  459. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  460. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  461. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  462. package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js +46 -0
  463. package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js.map +1 -0
  464. package/build/dist/Types/Permission.js +33 -0
  465. package/build/dist/Types/Permission.js.map +1 -1
  466. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  467. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  468. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  469. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  470. package/build/dist/Types/Text.js +82 -17
  471. package/build/dist/Types/Text.js.map +1 -1
  472. package/build/dist/UI/Components/Button/Button.js +22 -14
  473. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  474. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  475. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  476. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  477. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  478. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  479. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  480. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  481. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  482. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  483. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  484. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  485. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  486. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  487. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  488. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  489. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  490. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  491. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  492. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  493. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  494. package/build/dist/UI/Components/FormModal/BasicFormModal.js +2 -1
  495. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  496. package/build/dist/UI/Components/Forms/BasicForm.js +10 -1
  497. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  498. package/build/dist/UI/Components/Forms/BasicModelForm.js +2 -1
  499. package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
  500. package/build/dist/UI/Components/Forms/ModelForm.js +4 -1
  501. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  502. package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js +33 -0
  503. package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js.map +1 -0
  504. package/build/dist/UI/Components/List/List.js +19 -5
  505. package/build/dist/UI/Components/List/List.js.map +1 -1
  506. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  507. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  508. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  509. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  510. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  511. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  512. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js +2 -1
  513. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  514. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  515. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  516. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  517. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  518. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  519. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  520. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  521. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  522. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  523. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  524. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  525. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  526. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  527. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  528. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  529. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  530. package/build/dist/UI/Components/Table/Table.js +35 -24
  531. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  532. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  533. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  534. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  535. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  536. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  537. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  538. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  539. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  540. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  541. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  542. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  543. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  544. package/build/dist/UI/Utils/Project.js +24 -1
  545. package/build/dist/UI/Utils/Project.js.map +1 -1
  546. package/build/dist/Utils/API.js +6 -1
  547. package/build/dist/Utils/API.js.map +1 -1
  548. package/build/dist/Utils/Analytics.js +50 -2
  549. package/build/dist/Utils/Analytics.js.map +1 -1
  550. package/build/dist/Utils/Monitor/MonitorMetricType.js +37 -0
  551. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  552. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +318 -37
  553. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -1
  554. package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js +87 -0
  555. package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js.map +1 -0
  556. package/jest.config.json +1 -1
  557. package/package.json +2 -3
  558. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  559. package/Tests/__mocks__/otpauth.js +0 -30
  560. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  561. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { NetworkDeviceLinkRuleScopeUtil } from "../../Types/NetworkDevice/NetworkDeviceLinkRuleScope";
2
+
1
3
  /*
2
4
  * Turning label rules into uplinks.
3
5
  *
@@ -9,12 +11,35 @@
9
11
  * edge to the single device carrying all the parent labels. Matching "devices
10
12
  * that share a label" symmetrically, which is what was originally asked for,
11
13
  * would turn one forty-device site label into 780 edges.
14
+ *
15
+ * "The single device" needs a universe to be single IN, and the rule's scope
16
+ * is what names it. Project scope asks across everything the caller passed;
17
+ * Site scope asks once per site, which is what lets one rule draw the same
18
+ * router-to-switch star in fourteen buildings instead of reporting fourteen
19
+ * candidate routers and drawing nothing anywhere.
12
20
  */
13
21
 
14
22
  export interface LinkRuleDeviceInput {
15
23
  id: string;
16
24
  // Normalised label ids the device carries.
17
25
  labelIds: Array<string>;
26
+ /*
27
+ * Which site the device belongs to. ALWAYS a string, never an ObjectID —
28
+ * grouping keys a Map on it, and an ObjectID instance would compare by
29
+ * identity and put every device in a group of its own. null, undefined, ""
30
+ * and whitespace all normalise to the same "no site" key.
31
+ *
32
+ * Read only when the rule is site-scoped, so a caller that never populates
33
+ * it leaves project-scoped rules resolving exactly as before.
34
+ */
35
+ siteId?: string | null | undefined;
36
+ /*
37
+ * Display name of that site, for the warning text. Carried on the device
38
+ * rather than looked up by the caller so the name comes from the same
39
+ * permission-filtered rows the map itself is drawn from — a viewer can
40
+ * never be told the name of a site none of whose devices they can read.
41
+ */
42
+ siteName?: string | undefined;
18
43
  }
19
44
 
20
45
  export interface LinkRuleInput {
@@ -23,6 +48,19 @@ export interface LinkRuleInput {
23
48
  isEnabled?: boolean | undefined;
24
49
  childLabelIds: Array<string>;
25
50
  parentLabelIds: Array<string>;
51
+ /*
52
+ * Raw column value. Always read through NetworkDeviceLinkRuleScopeUtil.parse
53
+ * so undefined and NULL read as Project — every rule written before the
54
+ * column existed means "one parent for the whole project", and defaulting
55
+ * the other way would silently redraw somebody's map.
56
+ */
57
+ scope?: string | undefined;
58
+ }
59
+
60
+ // Structurally identical to the inline type it replaces.
61
+ export interface LinkRuleLink {
62
+ fromDeviceId: string;
63
+ toDeviceId: string;
26
64
  }
27
65
 
28
66
  /*
@@ -36,36 +74,233 @@ export type LinkRuleSkipReason =
36
74
  | "noParentLabels"
37
75
  | "noParentMatched"
38
76
  | "ambiguousParent"
39
- | "noChildrenMatched";
77
+ | "noChildrenMatched"
78
+ /*
79
+ * Site scope only: sites were in play and not one of them resolved. Its own
80
+ * reason because the per-site failures can be MIXED — one site with no
81
+ * parent, another with two — which none of the reasons above states.
82
+ */
83
+ | "noSiteResolved";
84
+
85
+ /*
86
+ * The only two ways ONE site can fail in a way worth reporting. Deliberately
87
+ * narrower than LinkRuleSkipReason: `noChildrenMatched` is unreachable here
88
+ * (a site with nothing to place is never considered at all), and the three
89
+ * rule-level reasons are settled before any device is looked at.
90
+ */
91
+ export type LinkRuleGroupSkipReason = "noParentMatched" | "ambiguousParent";
92
+
93
+ /*
94
+ * Warnings have a slightly wider vocabulary than skips: "some matching devices
95
+ * have no site" is not a reason the RULE drew nothing, so it has no business
96
+ * in LinkRuleSkipReason, which describeOutcome switches over.
97
+ */
98
+ export type LinkRuleWarningReason = LinkRuleSkipReason | "devicesWithoutSite";
99
+
100
+ /*
101
+ * One site this rule was clearly meant to cover and could not. Independent of
102
+ * outcome.links.length — thirteen sites can draw while the fourteenth is
103
+ * listed here.
104
+ */
105
+ export interface LinkRuleGroupFailure {
106
+ siteId: string;
107
+ siteName?: string | undefined;
108
+ reason: LinkRuleGroupSkipReason;
109
+ // Devices in THIS site carrying all the parent labels. 0 or >= 2.
110
+ matchedParentCount: number;
111
+ /*
112
+ * Devices in THIS site carrying all the child labels — the damage count,
113
+ * "how many nodes are floating on the map right now". No parent exclusion
114
+ * is applied because there is no resolved parent to exclude.
115
+ */
116
+ matchedChildCount: number;
117
+ }
40
118
 
41
119
  export interface LinkRuleOutcome {
42
120
  ruleId: string;
43
121
  ruleName?: string | undefined;
44
122
  // Device pairs this rule contributes, parent second.
45
- links: Array<{ fromDeviceId: string; toDeviceId: string }>;
46
- // Set when links is empty and the rule had something to say about why.
123
+ links: Array<LinkRuleLink>;
124
+ /*
125
+ * Set when links is empty and the rule had something to say about why.
126
+ * INVARIANT, both scopes: links.length === 0 <=> skipReason !== undefined.
127
+ * A site-scoped rule that drew in thirteen of fourteen sites has NOT been
128
+ * skipped; the fourteenth lives in groupFailures.
129
+ */
47
130
  skipReason?: LinkRuleSkipReason | undefined;
48
- // How many devices the parent labels matched — the ambiguity evidence.
131
+ /*
132
+ * How many devices the parent labels matched — the ambiguity evidence.
133
+ * Project scope: unchanged. Site scope: summed over the sites the rule
134
+ * actually applies to.
135
+ */
49
136
  matchedParentCount: number;
137
+ // INVARIANT, both scopes: matchedChildCount === links.length.
50
138
  matchedChildCount: number;
139
+
140
+ /*
141
+ * The three fields below are SITE SCOPE ONLY. On the project path the keys
142
+ * are not written at all, so a project-scoped outcome is the exact object
143
+ * it has always been, key for key.
144
+ */
145
+
146
+ // Sites the rule was meant to cover and could not. Omitted when empty.
147
+ groupFailures?: Array<LinkRuleGroupFailure> | undefined;
148
+ /*
149
+ * Sites this rule applies to: the ones holding at least one device it wanted
150
+ * to place. The coverage denominator, and deliberately not "every site in
151
+ * the project" — a healthy rule reading "drawing in 12 of 74 sites" would
152
+ * make the coverage sentence itself the noise.
153
+ */
154
+ applicableSiteCount?: number | undefined;
155
+ // Devices carrying ALL the child labels that have no site. Omitted when 0.
156
+ unsitedChildDeviceCount?: number | undefined;
157
+ }
158
+
159
+ /** One bullet in the topology map's warning banner. At most one per rule. */
160
+ export interface LinkRuleWarning {
161
+ ruleId: string;
162
+ ruleName?: string | undefined;
163
+ reason: LinkRuleWarningReason;
164
+ message: string;
165
+ }
166
+
167
+ /*
168
+ * Devices are normalised once per resolveRule call: the label Set is built
169
+ * here rather than allocated afresh inside every match, and the group key is
170
+ * stringified and trimmed here so null, undefined, "" and " " cannot become
171
+ * four different sites. Nothing here mutates the caller's array, which matters
172
+ * because resolveRules hands the same array to every rule in turn.
173
+ */
174
+ interface NormalizedDevice {
175
+ device: LinkRuleDeviceInput;
176
+ labelSet: Set<string>;
177
+ // "" means the device has no site.
178
+ siteKey: string;
179
+ }
180
+
181
+ /*
182
+ * One site's verdict — the same decision matrix as always, asked inside a
183
+ * narrower device set. Used by both scopes, so the parent/child matching
184
+ * exists exactly once.
185
+ */
186
+ interface LinkRuleGroupResult {
187
+ // Empty on every failure path.
188
+ links: Array<LinkRuleLink>;
189
+ // 0, 1, or >= 2.
190
+ parentMatchCount: number;
191
+ // Devices carrying all the child labels, BEFORE the unique-parent exclusion.
192
+ childMatchCount: number;
193
+ // null when links is non-empty.
194
+ failure: "noParentMatched" | "ambiguousParent" | "noChildrenMatched" | null;
51
195
  }
52
196
 
53
197
  export default class NetworkDeviceLinkRuleUtil {
198
+ /*
199
+ * Appended by the caller when the device list hit its row cap. Lives here so
200
+ * every operator-facing string this feature produces stays in one file.
201
+ */
202
+ public static readonly TRUNCATED_DEVICE_LIST_NOTE: string =
203
+ "The device list was truncated, so some of these may be false alarms.";
204
+
205
+ // Three names is enough to recognise a pattern; fourteen is a wall of text.
206
+ private static readonly MAX_NAMED_SITES: number = 3;
207
+ // Site names are ShortText and can run to 100 characters.
208
+ private static readonly MAX_SITE_NAME_LENGTH: number = 40;
209
+ private static readonly UNNAMED_SITE: string = "an unnamed site";
210
+
54
211
  /*
55
212
  * ALL of the rule's labels must be present on the device, not any. "Devices
56
213
  * that are both an access point AND on floor 1" is the question an operator
57
214
  * is asking; any-of would sweep in every access point in the building.
58
215
  */
59
- private static deviceMatches(
60
- device: LinkRuleDeviceInput,
216
+ private static matches(
217
+ labelSet: Set<string>,
61
218
  requiredLabelIds: Array<string>,
62
219
  ): boolean {
63
- const owned: Set<string> = new Set<string>(device.labelIds);
64
220
  return requiredLabelIds.every((labelId: string) => {
65
- return owned.has(labelId);
221
+ return labelSet.has(labelId);
66
222
  });
67
223
  }
68
224
 
225
+ private static resolveGroup(
226
+ rule: LinkRuleInput,
227
+ group: Array<NormalizedDevice>,
228
+ ): LinkRuleGroupResult {
229
+ const parents: Array<NormalizedDevice> = group.filter(
230
+ (entry: NormalizedDevice) => {
231
+ return NetworkDeviceLinkRuleUtil.matches(
232
+ entry.labelSet,
233
+ rule.parentLabelIds,
234
+ );
235
+ },
236
+ );
237
+
238
+ /*
239
+ * Counted before the parent branch, not after: a site that failed to
240
+ * resolve still has to report how many devices are floating there, and
241
+ * that number is the operator's damage assessment.
242
+ */
243
+ const childCandidates: Array<NormalizedDevice> = group.filter(
244
+ (entry: NormalizedDevice) => {
245
+ return NetworkDeviceLinkRuleUtil.matches(
246
+ entry.labelSet,
247
+ rule.childLabelIds,
248
+ );
249
+ },
250
+ );
251
+ const childMatchCount: number = childCandidates.length;
252
+
253
+ if (parents.length === 0) {
254
+ return {
255
+ links: [],
256
+ parentMatchCount: 0,
257
+ childMatchCount: childMatchCount,
258
+ failure: "noParentMatched",
259
+ };
260
+ }
261
+
262
+ /*
263
+ * Two candidate parents is not a tie to break — it is a question the
264
+ * labels do not answer. Drawing to either one would assert a cabling
265
+ * fact nobody stated.
266
+ */
267
+ if (parents.length > 1) {
268
+ return {
269
+ links: [],
270
+ parentMatchCount: parents.length,
271
+ childMatchCount: childMatchCount,
272
+ failure: "ambiguousParent",
273
+ };
274
+ }
275
+
276
+ const parent: NormalizedDevice = parents[0]!;
277
+
278
+ // The parent is excluded even when it matches both sets: no self-links.
279
+ const children: Array<NormalizedDevice> = childCandidates.filter(
280
+ (entry: NormalizedDevice) => {
281
+ return entry.device.id !== parent.device.id;
282
+ },
283
+ );
284
+
285
+ if (children.length === 0) {
286
+ return {
287
+ links: [],
288
+ parentMatchCount: 1,
289
+ childMatchCount: childMatchCount,
290
+ failure: "noChildrenMatched",
291
+ };
292
+ }
293
+
294
+ return {
295
+ links: children.map((child: NormalizedDevice) => {
296
+ return { fromDeviceId: child.device.id, toDeviceId: parent.device.id };
297
+ }),
298
+ parentMatchCount: 1,
299
+ childMatchCount: childMatchCount,
300
+ failure: null,
301
+ };
302
+ }
303
+
69
304
  public static resolveRule(
70
305
  rule: LinkRuleInput,
71
306
  devices: Array<LinkRuleDeviceInput>,
@@ -89,6 +324,13 @@ export default class NetworkDeviceLinkRuleUtil {
89
324
  };
90
325
  };
91
326
 
327
+ /*
328
+ * The three checks below are properties of the rule's TEXT, true in every
329
+ * site, so they are settled before scope is even read. A disabled rule is
330
+ * not "disabled in Unit 7", and partitioning a rule with an empty parent
331
+ * label set would make every site in the project ambiguous at once — the
332
+ * worst noise outcome available.
333
+ */
92
334
  if (rule.isEnabled === false) {
93
335
  return empty("disabled");
94
336
  }
@@ -105,53 +347,233 @@ export default class NetworkDeviceLinkRuleUtil {
105
347
  return empty("noParentLabels");
106
348
  }
107
349
 
108
- const parents: Array<LinkRuleDeviceInput> = devices.filter(
350
+ const normalized: Array<NormalizedDevice> = devices.map(
109
351
  (device: LinkRuleDeviceInput) => {
110
- return NetworkDeviceLinkRuleUtil.deviceMatches(
111
- device,
112
- rule.parentLabelIds,
113
- );
352
+ return {
353
+ device: device,
354
+ labelSet: new Set<string>(device.labelIds),
355
+ siteKey: (device.siteId || "").toString().trim(),
356
+ };
114
357
  },
115
358
  );
116
359
 
117
- if (parents.length === 0) {
360
+ if (NetworkDeviceLinkRuleScopeUtil.isSiteScoped(rule.scope)) {
361
+ return NetworkDeviceLinkRuleUtil.resolvePerSite(rule, normalized);
362
+ }
363
+
364
+ return NetworkDeviceLinkRuleUtil.resolveProject(rule, normalized, empty);
365
+ }
366
+
367
+ private static resolveProject(
368
+ rule: LinkRuleInput,
369
+ normalized: Array<NormalizedDevice>,
370
+ empty: (
371
+ reason: LinkRuleSkipReason,
372
+ parents?: number,
373
+ children?: number,
374
+ ) => LinkRuleOutcome,
375
+ ): LinkRuleOutcome {
376
+ const result: LinkRuleGroupResult = NetworkDeviceLinkRuleUtil.resolveGroup(
377
+ rule,
378
+ normalized,
379
+ );
380
+
381
+ if (result.failure === "noParentMatched") {
118
382
  return empty("noParentMatched", 0);
119
383
  }
384
+ if (result.failure === "ambiguousParent") {
385
+ return empty("ambiguousParent", result.parentMatchCount);
386
+ }
387
+ if (result.failure === "noChildrenMatched") {
388
+ return empty("noChildrenMatched", 1);
389
+ }
390
+
391
+ return {
392
+ ruleId: rule.id,
393
+ ruleName: rule.name,
394
+ links: result.links,
395
+ matchedParentCount: 1,
396
+ matchedChildCount: result.links.length,
397
+ };
398
+ }
120
399
 
400
+ private static resolvePerSite(
401
+ rule: LinkRuleInput,
402
+ normalized: Array<NormalizedDevice>,
403
+ ): LinkRuleOutcome {
121
404
  /*
122
- * Two candidate parents is not a tie to break — it is a question the
123
- * labels do not answer. Drawing to either one would assert a cabling
124
- * fact nobody stated.
405
+ * Bucketed in ONE pass in first-appearance order. Never
406
+ * `devices.filter(...)` inside a loop over sites: that is O(devices x
407
+ * sites) per rule, and this endpoint runs at ten thousand devices.
125
408
  */
126
- if (parents.length > 1) {
127
- return empty("ambiguousParent", parents.length);
409
+ const buckets: Map<string, Array<NormalizedDevice>> = new Map<
410
+ string,
411
+ Array<NormalizedDevice>
412
+ >();
413
+ let unsitedChildDeviceCount: number = 0;
414
+
415
+ for (const entry of normalized) {
416
+ /*
417
+ * A device with no site is EXCLUDED, not pooled with the other unsited
418
+ * ones and not given a group of its own. Pooling would ask "exactly one
419
+ * parent among all the unsited devices", a question nobody posed, and
420
+ * would draw an edge between two boxes whose only relationship is that
421
+ * neither has been assigned a site yet. Counted instead, so the map can
422
+ * say how many the rule walked past.
423
+ */
424
+ if (entry.siteKey === "") {
425
+ if (
426
+ NetworkDeviceLinkRuleUtil.matches(entry.labelSet, rule.childLabelIds)
427
+ ) {
428
+ unsitedChildDeviceCount++;
429
+ }
430
+ continue;
431
+ }
432
+
433
+ const bucket: Array<NormalizedDevice> | undefined = buckets.get(
434
+ entry.siteKey,
435
+ );
436
+ if (bucket) {
437
+ bucket.push(entry);
438
+ } else {
439
+ buckets.set(entry.siteKey, [entry]);
440
+ }
128
441
  }
129
442
 
130
- const parent: LinkRuleDeviceInput = parents[0]!;
443
+ const links: Array<LinkRuleLink> = [];
444
+ const groupFailures: Array<LinkRuleGroupFailure> = [];
445
+ let applicableSiteCount: number = 0;
446
+ let matchedParentCount: number = 0;
131
447
 
132
- const children: Array<LinkRuleDeviceInput> = devices.filter(
133
- (device: LinkRuleDeviceInput) => {
134
- // The parent is excluded even when it matches both sets: no self-links.
135
- return (
136
- device.id !== parent.id &&
137
- NetworkDeviceLinkRuleUtil.deviceMatches(device, rule.childLabelIds)
138
- );
139
- },
140
- );
448
+ for (const [siteId, group] of buckets) {
449
+ const result: LinkRuleGroupResult =
450
+ NetworkDeviceLinkRuleUtil.resolveGroup(rule, group);
141
451
 
142
- if (children.length === 0) {
143
- return empty("noChildrenMatched", 1);
452
+ if (result.links.length > 0) {
453
+ applicableSiteCount++;
454
+ matchedParentCount += result.parentMatchCount;
455
+ links.push(...result.links);
456
+ continue;
457
+ }
458
+
459
+ /*
460
+ * A site speaks only when it holds devices this rule wanted to place.
461
+ * Sites with nothing to place are not misconfigured — the rule simply
462
+ * does not apply there, and a parent label like `SubCategory:Router` is
463
+ * present in nearly every site by construction. Without this gate, site
464
+ * scoping would trade one loud failure for two hundred quiet ones and
465
+ * the banner would be ignored inside a week.
466
+ *
467
+ * `noChildrenMatched` lands here too: the only child candidate was the
468
+ * parent itself, so after the self-link exclusion there is nothing to
469
+ * place and nothing to report.
470
+ */
471
+ if (
472
+ result.childMatchCount === 0 ||
473
+ result.failure === null ||
474
+ result.failure === "noChildrenMatched"
475
+ ) {
476
+ continue;
477
+ }
478
+
479
+ applicableSiteCount++;
480
+ matchedParentCount += result.parentMatchCount;
481
+ groupFailures.push({
482
+ siteId: siteId,
483
+ siteName: NetworkDeviceLinkRuleUtil.siteNameOf(group),
484
+ reason: result.failure,
485
+ matchedParentCount: result.parentMatchCount,
486
+ matchedChildCount: result.childMatchCount,
487
+ });
144
488
  }
145
489
 
146
- return {
490
+ const outcome: LinkRuleOutcome = {
147
491
  ruleId: rule.id,
148
492
  ruleName: rule.name,
149
- links: children.map((child: LinkRuleDeviceInput) => {
150
- return { fromDeviceId: child.id, toDeviceId: parent.id };
151
- }),
152
- matchedParentCount: 1,
153
- matchedChildCount: children.length,
493
+ links: links,
494
+ matchedParentCount: matchedParentCount,
495
+ matchedChildCount: links.length,
496
+ applicableSiteCount: applicableSiteCount,
154
497
  };
498
+
499
+ /*
500
+ * Keeps `links.length === 0 <=> skipReason defined` true with no gaps, so
501
+ * describeOutcome can never fall through to "Draws 0 uplinks" on an empty
502
+ * outcome. The second arm covers both an empty device list and a rule
503
+ * whose labels match nothing anywhere.
504
+ */
505
+ if (links.length === 0) {
506
+ outcome.skipReason =
507
+ groupFailures.length > 0 ? "noSiteResolved" : "noChildrenMatched";
508
+ }
509
+
510
+ if (groupFailures.length > 0) {
511
+ outcome.groupFailures = groupFailures;
512
+ }
513
+ if (unsitedChildDeviceCount > 0) {
514
+ outcome.unsitedChildDeviceCount = unsitedChildDeviceCount;
515
+ }
516
+
517
+ return outcome;
518
+ }
519
+
520
+ /** First non-empty site name among a site's devices, if any of them carry one. */
521
+ private static siteNameOf(
522
+ group: Array<NormalizedDevice>,
523
+ ): string | undefined {
524
+ for (const entry of group) {
525
+ if (entry.device.siteName) {
526
+ return entry.device.siteName;
527
+ }
528
+ }
529
+ return undefined;
530
+ }
531
+
532
+ /*
533
+ * "Unit 4, Unit 9 and 2 more sites".
534
+ *
535
+ * Sorted before naming, and that is load-bearing rather than cosmetic: the
536
+ * live view refetches every sixty seconds and the device query has no ORDER
537
+ * BY, so an unsorted list would reshuffle the banner on every poll and read
538
+ * as a change that did not happen. The count stays exact even when the names
539
+ * are elided, so the operator always knows the true blast radius.
540
+ */
541
+ private static describeSiteList(
542
+ failures: Array<LinkRuleGroupFailure>,
543
+ ): string {
544
+ const sorted: Array<LinkRuleGroupFailure> = [...failures].sort(
545
+ (a: LinkRuleGroupFailure, b: LinkRuleGroupFailure) => {
546
+ return (
547
+ (a.siteName || "").localeCompare(b.siteName || "") ||
548
+ a.siteId.localeCompare(b.siteId)
549
+ );
550
+ },
551
+ );
552
+
553
+ const names: Array<string> = sorted
554
+ .slice(0, NetworkDeviceLinkRuleUtil.MAX_NAMED_SITES)
555
+ .map((failure: LinkRuleGroupFailure) => {
556
+ const name: string =
557
+ failure.siteName || NetworkDeviceLinkRuleUtil.UNNAMED_SITE;
558
+ return name.length > NetworkDeviceLinkRuleUtil.MAX_SITE_NAME_LENGTH
559
+ ? `${name.slice(
560
+ 0,
561
+ NetworkDeviceLinkRuleUtil.MAX_SITE_NAME_LENGTH - 1,
562
+ )}…`
563
+ : name;
564
+ });
565
+
566
+ const remaining: number = sorted.length - names.length;
567
+
568
+ if (remaining > 0) {
569
+ return `${names.join(", ")} and ${remaining} more site${
570
+ remaining === 1 ? "" : "s"
571
+ }`;
572
+ }
573
+ if (names.length === 1) {
574
+ return names[0]!;
575
+ }
576
+ return `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]!}`;
155
577
  }
156
578
 
157
579
  public static resolveRules(
@@ -178,10 +600,138 @@ export default class NetworkDeviceLinkRuleUtil {
178
600
  return `${outcome.matchedParentCount} devices carry the parent labels. Exactly one must, or there is no way to tell which is the uplink.`;
179
601
  case "noChildrenMatched":
180
602
  return "No device carries the child labels yet.";
603
+ case "noSiteResolved":
604
+ return "No site resolved this rule.";
181
605
  default:
182
606
  return `Draws ${outcome.matchedChildCount} uplink${
183
607
  outcome.matchedChildCount === 1 ? "" : "s"
184
608
  }.`;
185
609
  }
186
610
  }
611
+
612
+ /**
613
+ * The operator-facing warning, or null when the rule needs no explanation.
614
+ *
615
+ * Singular on purpose: ONE bullet per rule, never one per site. The banner's
616
+ * length is then bounded by the number of rules the operator wrote — a small
617
+ * number they control — rather than by the number of sites in their project.
618
+ *
619
+ * This is also the only sanctioned "did this rule fail" predicate. A caller
620
+ * must not go back to testing `outcome.links.length === 0`, which a rule
621
+ * drawing thirteen of fourteen sites passes while genuinely failing.
622
+ */
623
+ public static getWarning(outcome: LinkRuleOutcome): LinkRuleWarning | null {
624
+ const failures: Array<LinkRuleGroupFailure> = outcome.groupFailures || [];
625
+ const unsited: number = outcome.unsitedChildDeviceCount || 0;
626
+
627
+ /*
628
+ * Project scope always lands here, and this branch is what the topology
629
+ * API used to compute inline: warn iff the rule drew nothing, using
630
+ * describeOutcome's own words. The `!skipReason` guard makes the old
631
+ * code's implicit assumption explicit rather than emitting a warning with
632
+ * an undefined reason.
633
+ */
634
+ if (failures.length === 0 && unsited === 0) {
635
+ if (outcome.links.length > 0 || !outcome.skipReason) {
636
+ return null;
637
+ }
638
+ return {
639
+ ruleId: outcome.ruleId,
640
+ ruleName: outcome.ruleName,
641
+ reason: outcome.skipReason,
642
+ message: NetworkDeviceLinkRuleUtil.describeOutcome(outcome),
643
+ };
644
+ }
645
+
646
+ const parts: Array<string> = [];
647
+ const applicableSiteCount: number = outcome.applicableSiteCount || 0;
648
+
649
+ if (outcome.links.length === 0) {
650
+ parts.push("No site resolved this rule.");
651
+ } else if (applicableSiteCount > 1) {
652
+ // A single applicable site needs no coverage fraction.
653
+ parts.push(
654
+ `Drawing in ${
655
+ applicableSiteCount - failures.length
656
+ } of ${applicableSiteCount} sites.`,
657
+ );
658
+ }
659
+
660
+ const ambiguous: Array<LinkRuleGroupFailure> = failures.filter(
661
+ (failure: LinkRuleGroupFailure) => {
662
+ return failure.reason === "ambiguousParent";
663
+ },
664
+ );
665
+ const parentless: Array<LinkRuleGroupFailure> = failures.filter(
666
+ (failure: LinkRuleGroupFailure) => {
667
+ return failure.reason === "noParentMatched";
668
+ },
669
+ );
670
+
671
+ if (ambiguous.length === 1) {
672
+ /*
673
+ * The trailing sentence deliberately echoes the project-scope wording so
674
+ * an operator's vocabulary does not fork between the two scopes.
675
+ */
676
+ parts.push(
677
+ `${
678
+ ambiguous[0]!.matchedParentCount
679
+ } devices carry the parent labels in ${NetworkDeviceLinkRuleUtil.describeSiteList(
680
+ ambiguous,
681
+ )}. Exactly one must, or there is no way to tell which is the uplink.`,
682
+ );
683
+ } else if (ambiguous.length > 1) {
684
+ parts.push(
685
+ `Several devices carry the parent labels in ${NetworkDeviceLinkRuleUtil.describeSiteList(
686
+ ambiguous,
687
+ )}. Exactly one per site must, or there is no way to tell which is the uplink.`,
688
+ );
689
+ }
690
+
691
+ if (parentless.length > 0) {
692
+ const stranded: number = parentless.reduce(
693
+ (sum: number, failure: LinkRuleGroupFailure) => {
694
+ return sum + failure.matchedChildCount;
695
+ },
696
+ 0,
697
+ );
698
+ parts.push(
699
+ `No device carries the parent labels in ${NetworkDeviceLinkRuleUtil.describeSiteList(
700
+ parentless,
701
+ )}, so ${stranded} ${stranded === 1 ? "device" : "devices"} there ${
702
+ stranded === 1 ? "has" : "have"
703
+ } no uplink.`,
704
+ );
705
+ }
706
+
707
+ /*
708
+ * Always last and always its own sentence: the fix here is "assign these
709
+ * devices to a site", not "fix your labels", so they are never folded into
710
+ * a site-name list.
711
+ */
712
+ if (unsited > 0) {
713
+ parts.push(
714
+ `${unsited} ${
715
+ unsited === 1 ? "device" : "devices"
716
+ } carrying the child labels ${
717
+ unsited === 1 ? "is" : "are"
718
+ } not assigned to a site, so this site-scoped rule skips ${
719
+ unsited === 1 ? "it" : "them"
720
+ }.`,
721
+ );
722
+ }
723
+
724
+ return {
725
+ ruleId: outcome.ruleId,
726
+ ruleName: outcome.ruleName,
727
+ // Highest-ranked condition present. Ambiguity outranks a missing parent.
728
+ reason:
729
+ ambiguous.length > 0
730
+ ? "ambiguousParent"
731
+ : parentless.length > 0
732
+ ? "noParentMatched"
733
+ : "devicesWithoutSite",
734
+ message: parts.join(" "),
735
+ };
736
+ }
187
737
  }