@oneuptime/common 12.0.13 → 12.0.14

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 (376) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  2. package/Models/DatabaseModels/UserCall.ts +94 -3
  3. package/Models/DatabaseModels/UserEmail.ts +94 -3
  4. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  5. package/Models/DatabaseModels/UserPush.ts +37 -0
  6. package/Models/DatabaseModels/UserSMS.ts +94 -3
  7. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  8. package/Server/API/BaseAPI.ts +79 -14
  9. package/Server/API/UserCallAPI.ts +53 -45
  10. package/Server/API/UserEmailAPI.ts +53 -44
  11. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  12. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  13. package/Server/API/UserPushAPI.ts +126 -2
  14. package/Server/API/UserSmsAPI.ts +53 -44
  15. package/Server/API/UserWhatsAppAPI.ts +53 -50
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  21. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  22. package/Server/Services/DatabaseService.ts +101 -0
  23. package/Server/Services/LogAggregationService.ts +5 -0
  24. package/Server/Services/PushNotificationService.ts +127 -12
  25. package/Server/Services/TeamMemberService.ts +53 -13
  26. package/Server/Services/UserCallService.ts +55 -24
  27. package/Server/Services/UserEmailService.ts +83 -17
  28. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  29. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  30. package/Server/Services/UserNotificationRuleService.ts +26 -0
  31. package/Server/Services/UserPushService.ts +89 -0
  32. package/Server/Services/UserSmsService.ts +50 -17
  33. package/Server/Services/UserWhatsAppService.ts +48 -15
  34. package/Server/Types/Database/QueryHelper.ts +30 -0
  35. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  36. package/Server/Utils/ChannelVerification.ts +510 -0
  37. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
  38. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  39. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  40. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  41. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  42. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  43. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  44. package/Server/Utils/TotpAuth.ts +137 -9
  45. package/Server/Utils/UserRegistrationToken.ts +182 -0
  46. package/Server/Utils/VerificationCode.ts +134 -0
  47. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  48. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  49. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  50. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  51. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  52. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  53. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  54. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  55. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  56. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  57. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  58. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  59. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  60. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  61. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
  62. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  63. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  64. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  65. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  66. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  67. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  68. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  69. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  70. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  71. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  72. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  73. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  74. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  75. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  76. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  77. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  78. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  79. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  80. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  81. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  82. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  83. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  84. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  85. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  86. package/Tests/Server/Utils/Browser.test.ts +162 -0
  87. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  88. package/Tests/Server/Utils/Express.test.ts +371 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  91. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  92. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  93. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  94. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  95. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  96. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  97. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  98. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  99. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  100. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  101. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  102. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  103. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  104. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  105. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  106. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  107. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  108. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  109. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  110. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  111. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  112. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  113. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  114. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  115. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  116. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  117. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  118. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  119. package/Tests/UI/Components/Button.test.tsx +175 -1
  120. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  121. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  122. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  123. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  124. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  125. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  126. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  127. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  128. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  129. package/Tests/UI/Components/List.test.tsx +9 -2
  130. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  131. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  132. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  133. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  134. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  135. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  136. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  137. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  138. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  139. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  140. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  141. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  142. package/Tests/UI/Components/Toast.test.tsx +130 -7
  143. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  144. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  145. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  146. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  147. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  148. package/Tests/Utils/API.test.ts +16 -2
  149. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  150. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  151. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  152. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  153. package/Types/Email/EmailTemplateType.ts +1 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  155. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  156. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  157. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  158. package/Types/Permission.ts +35 -0
  159. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  160. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  161. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  162. package/Types/Service/ServiceLanguage.ts +144 -0
  163. package/Types/Text.ts +111 -19
  164. package/UI/Components/Button/Button.tsx +27 -14
  165. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  166. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  167. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  168. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  169. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  170. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  171. package/UI/Components/CommandPalette/Types.ts +49 -0
  172. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  173. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  174. package/UI/Components/Feed/FeedItem.tsx +1 -1
  175. package/UI/Components/List/List.tsx +37 -5
  176. package/UI/Components/List/ListSkeleton.tsx +54 -0
  177. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  178. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  179. package/UI/Components/Modal/Modal.tsx +2 -2
  180. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  181. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  182. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  183. package/UI/Components/Navbar/NavBar.tsx +14 -1
  184. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  185. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  186. package/UI/Components/SideOver/SideOver.tsx +37 -2
  187. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  188. package/UI/Components/Table/Table.tsx +74 -44
  189. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  190. package/UI/Components/Tabs/Tab.tsx +1 -1
  191. package/UI/Components/Toast/Toast.tsx +118 -53
  192. package/UI/Components/Toast/ToastInit.tsx +82 -32
  193. package/UI/Styles/Theme.css +40 -0
  194. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  195. package/UI/Utils/ModelListCache.ts +188 -0
  196. package/UI/Utils/Project.ts +29 -0
  197. package/Utils/API.ts +6 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  201. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  203. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  205. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  207. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  209. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  211. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  212. package/build/dist/Server/API/BaseAPI.js +69 -13
  213. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  214. package/build/dist/Server/API/UserCallAPI.js +42 -38
  215. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  216. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  217. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  218. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  219. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  220. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  221. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  222. package/build/dist/Server/API/UserPushAPI.js +95 -2
  223. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  224. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  225. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  226. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  227. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  236. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  237. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  239. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  240. package/build/dist/Server/Services/DatabaseService.js +74 -0
  241. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  242. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  243. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  244. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  245. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  246. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  247. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  248. package/build/dist/Server/Services/UserCallService.js +54 -22
  249. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  250. package/build/dist/Server/Services/UserEmailService.js +78 -17
  251. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  252. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  253. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  254. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  255. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  256. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  257. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  258. package/build/dist/Server/Services/UserPushService.js +77 -0
  259. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  260. package/build/dist/Server/Services/UserSmsService.js +50 -17
  261. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  262. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  263. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  264. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  265. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  266. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  267. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  268. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  269. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  270. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
  271. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  272. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  273. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  274. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  275. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  276. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  277. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  278. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  279. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  280. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  281. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  282. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  283. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  284. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  285. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  286. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  287. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  288. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  289. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  290. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  291. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  292. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  293. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  294. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  295. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  296. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  297. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  298. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  299. package/build/dist/Types/Permission.js +33 -0
  300. package/build/dist/Types/Permission.js.map +1 -1
  301. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  302. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  303. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  304. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  305. package/build/dist/Types/Text.js +82 -17
  306. package/build/dist/Types/Text.js.map +1 -1
  307. package/build/dist/UI/Components/Button/Button.js +22 -14
  308. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  309. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  310. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  311. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  312. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  313. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  314. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  315. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  316. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  317. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  318. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  319. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  320. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  321. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  322. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  323. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  324. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  325. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  326. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  327. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  328. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  329. package/build/dist/UI/Components/List/List.js +19 -5
  330. package/build/dist/UI/Components/List/List.js.map +1 -1
  331. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  332. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  333. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  334. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  335. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  338. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  340. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  343. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  344. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  345. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  346. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  347. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  348. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  349. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  350. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  351. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  352. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  353. package/build/dist/UI/Components/Table/Table.js +35 -24
  354. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  355. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  356. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  357. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  358. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  359. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  360. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  361. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  362. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  363. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  364. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  365. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  366. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  367. package/build/dist/UI/Utils/Project.js +24 -1
  368. package/build/dist/UI/Utils/Project.js.map +1 -1
  369. package/build/dist/Utils/API.js +6 -1
  370. package/build/dist/Utils/API.js.map +1 -1
  371. package/jest.config.json +1 -1
  372. package/package.json +2 -3
  373. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  374. package/Tests/__mocks__/otpauth.js +0 -30
  375. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  376. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -0,0 +1,196 @@
1
+ import { PaletteCommand } from "./Types";
2
+
3
+ /*
4
+ * Pure, React-free filtering and ranking for the command palette. Kept out of
5
+ * the component so the ranking contract can be unit-tested directly and reused
6
+ * by anything that needs "which commands match this text, best first".
7
+ */
8
+
9
+ /**
10
+ * How a command matched the query, best match first. The palette sorts by
11
+ * this tier and keeps the caller's original order within a tier, so two
12
+ * commands whose titles both start with the query stay in catalog order.
13
+ */
14
+ export enum PaletteMatchRank {
15
+ TitlePrefix = 0,
16
+ TitleSubstring = 1,
17
+ Keyword = 2,
18
+ Category = 3,
19
+ /** Query characters appear in order in the title, but not adjacently. */
20
+ TitleSubsequence = 4,
21
+ }
22
+
23
+ export interface PaletteCommandMatch {
24
+ command: PaletteCommand;
25
+ rank: PaletteMatchRank;
26
+ }
27
+
28
+ /** One run of text in a title/description, flagged when it matched the query. */
29
+ export interface PaletteHighlightSegment {
30
+ text: string;
31
+ isMatch: boolean;
32
+ }
33
+
34
+ export const normalizePaletteQuery: (query: string) => string = (
35
+ query: string,
36
+ ): string => {
37
+ return query.trim().toLowerCase();
38
+ };
39
+
40
+ /**
41
+ * True when every character of `needle` appears in `haystack` in order (not
42
+ * necessarily adjacent) — the "mntr" finds "Monitors" fallback. Both inputs
43
+ * are expected to be lowercased already.
44
+ */
45
+ export const isSubsequenceMatch: (
46
+ needle: string,
47
+ haystack: string,
48
+ ) => boolean = (needle: string, haystack: string): boolean => {
49
+ if (needle.length === 0) {
50
+ return true;
51
+ }
52
+ let needleIndex: number = 0;
53
+ for (
54
+ let haystackIndex: number = 0;
55
+ haystackIndex < haystack.length && needleIndex < needle.length;
56
+ haystackIndex++
57
+ ) {
58
+ if (haystack[haystackIndex] === needle[needleIndex]) {
59
+ needleIndex++;
60
+ }
61
+ }
62
+ return needleIndex === needle.length;
63
+ };
64
+
65
+ /**
66
+ * Rank one command against an already-normalized query, or null when it does
67
+ * not match at all.
68
+ */
69
+ export const rankPaletteCommand: (
70
+ command: PaletteCommand,
71
+ normalizedQuery: string,
72
+ ) => PaletteMatchRank | null = (
73
+ command: PaletteCommand,
74
+ normalizedQuery: string,
75
+ ): PaletteMatchRank | null => {
76
+ const title: string = command.title.toLowerCase();
77
+
78
+ if (title.startsWith(normalizedQuery)) {
79
+ return PaletteMatchRank.TitlePrefix;
80
+ }
81
+ if (title.includes(normalizedQuery)) {
82
+ return PaletteMatchRank.TitleSubstring;
83
+ }
84
+ const keywordMatches: boolean = (command.keywords || []).some(
85
+ (keyword: string) => {
86
+ return keyword.toLowerCase().includes(normalizedQuery);
87
+ },
88
+ );
89
+ if (keywordMatches) {
90
+ return PaletteMatchRank.Keyword;
91
+ }
92
+ if (command.category.toLowerCase().includes(normalizedQuery)) {
93
+ return PaletteMatchRank.Category;
94
+ }
95
+ if (isSubsequenceMatch(normalizedQuery, title)) {
96
+ return PaletteMatchRank.TitleSubsequence;
97
+ }
98
+ return null;
99
+ };
100
+
101
+ /**
102
+ * Filter commands against free text and return them best-match-first:
103
+ * title-prefix > title-substring > keyword > category > title-subsequence.
104
+ * The sort is stable — within a tier the caller's order is preserved. An
105
+ * empty/whitespace query matches everything in the original order.
106
+ */
107
+ export const filterPaletteCommands: (
108
+ commands: Array<PaletteCommand>,
109
+ query: string,
110
+ ) => Array<PaletteCommandMatch> = (
111
+ commands: Array<PaletteCommand>,
112
+ query: string,
113
+ ): Array<PaletteCommandMatch> => {
114
+ const normalizedQuery: string = normalizePaletteQuery(query);
115
+
116
+ if (!normalizedQuery) {
117
+ return commands.map((command: PaletteCommand) => {
118
+ return { command, rank: PaletteMatchRank.TitlePrefix };
119
+ });
120
+ }
121
+
122
+ const matches: Array<PaletteCommandMatch> = [];
123
+ commands.forEach((command: PaletteCommand) => {
124
+ const rank: PaletteMatchRank | null = rankPaletteCommand(
125
+ command,
126
+ normalizedQuery,
127
+ );
128
+ if (rank !== null) {
129
+ matches.push({ command, rank });
130
+ }
131
+ });
132
+
133
+ // Array.prototype.sort is stable, so equal ranks keep catalog order.
134
+ return matches.sort((a: PaletteCommandMatch, b: PaletteCommandMatch) => {
135
+ return a.rank - b.rank;
136
+ });
137
+ };
138
+
139
+ /**
140
+ * Split text into segments for <mark> highlighting: every case-insensitive
141
+ * occurrence of the query becomes a match segment. Subsequence-only matches
142
+ * produce no highlight (per-character marks read as noise).
143
+ */
144
+ export const getHighlightSegments: (
145
+ text: string,
146
+ query: string,
147
+ ) => Array<PaletteHighlightSegment> = (
148
+ text: string,
149
+ query: string,
150
+ ): Array<PaletteHighlightSegment> => {
151
+ const needle: string = normalizePaletteQuery(query);
152
+ if (!needle) {
153
+ return [{ text, isMatch: false }];
154
+ }
155
+
156
+ const haystack: string = text.toLowerCase();
157
+ const segments: Array<PaletteHighlightSegment> = [];
158
+ let cursor: number = 0;
159
+ let matchAt: number = haystack.indexOf(needle);
160
+
161
+ while (matchAt !== -1) {
162
+ if (matchAt > cursor) {
163
+ segments.push({ text: text.slice(cursor, matchAt), isMatch: false });
164
+ }
165
+ segments.push({
166
+ text: text.slice(matchAt, matchAt + needle.length),
167
+ isMatch: true,
168
+ });
169
+ cursor = matchAt + needle.length;
170
+ matchAt = haystack.indexOf(needle, cursor);
171
+ }
172
+
173
+ if (cursor < text.length) {
174
+ segments.push({ text: text.slice(cursor), isMatch: false });
175
+ }
176
+
177
+ if (segments.length === 0) {
178
+ return [{ text, isMatch: false }];
179
+ }
180
+
181
+ return segments;
182
+ };
183
+
184
+ /**
185
+ * Stable slug for section test ids: "Analytics & Automation" →
186
+ * "analytics-automation". Search-provider sections use the provider's own id
187
+ * instead of this.
188
+ */
189
+ export const getPaletteSectionId: (title: string) => string = (
190
+ title: string,
191
+ ): string => {
192
+ return title
193
+ .toLowerCase()
194
+ .replace(/[^a-z0-9]+/g, "-")
195
+ .replace(/^-+|-+$/g, "");
196
+ };
@@ -0,0 +1,173 @@
1
+ import Icon from "../Icon/Icon";
2
+ import KeyboardShortcut, {
3
+ KeyboardShortcutSize,
4
+ KeyboardShortcutVariant,
5
+ } from "../KeyboardShortcut/KeyboardShortcut";
6
+ import { KeyboardShortcutKey } from "../KeyboardShortcut/KeyboardKey";
7
+ import IconProp from "../../../Types/Icon/IconProp";
8
+ import { getHighlightSegments, PaletteHighlightSegment } from "./PaletteFilter";
9
+ import React, { FunctionComponent, ReactElement, ReactNode } from "react";
10
+
11
+ interface IconColorClasses {
12
+ bg: string;
13
+ ring: string;
14
+ text: string;
15
+ }
16
+
17
+ /*
18
+ * Same Tailwind color-name convention (and palette) as the NavBar items /
19
+ * NavBarMenuModal: a soft tinted square with a ring and a colored glyph.
20
+ * Unknown colors fall back to indigo. Every class here already has an
21
+ * html.dark remap in Theme.css (the navbar menu ships the identical set).
22
+ */
23
+ const ICON_COLOR_CLASSES: Record<string, IconColorClasses> = {
24
+ purple: {
25
+ bg: "bg-purple-50",
26
+ ring: "ring-purple-200",
27
+ text: "text-purple-600",
28
+ },
29
+ blue: { bg: "bg-blue-50", ring: "ring-blue-200", text: "text-blue-600" },
30
+ gray: { bg: "bg-gray-100", ring: "ring-gray-300", text: "text-gray-600" },
31
+ amber: { bg: "bg-amber-50", ring: "ring-amber-200", text: "text-amber-600" },
32
+ green: { bg: "bg-green-50", ring: "ring-green-200", text: "text-green-600" },
33
+ cyan: { bg: "bg-cyan-50", ring: "ring-cyan-200", text: "text-cyan-600" },
34
+ slate: { bg: "bg-slate-100", ring: "ring-slate-300", text: "text-slate-600" },
35
+ indigo: {
36
+ bg: "bg-indigo-50",
37
+ ring: "ring-indigo-200",
38
+ text: "text-indigo-600",
39
+ },
40
+ rose: { bg: "bg-rose-50", ring: "ring-rose-200", text: "text-rose-600" },
41
+ violet: {
42
+ bg: "bg-violet-50",
43
+ ring: "ring-violet-200",
44
+ text: "text-violet-600",
45
+ },
46
+ orange: {
47
+ bg: "bg-orange-50",
48
+ ring: "ring-orange-200",
49
+ text: "text-orange-600",
50
+ },
51
+ stone: { bg: "bg-stone-100", ring: "ring-stone-300", text: "text-stone-600" },
52
+ sky: { bg: "bg-sky-50", ring: "ring-sky-200", text: "text-sky-600" },
53
+ emerald: {
54
+ bg: "bg-emerald-50",
55
+ ring: "ring-emerald-200",
56
+ text: "text-emerald-600",
57
+ },
58
+ yellow: {
59
+ bg: "bg-yellow-50",
60
+ ring: "ring-yellow-200",
61
+ text: "text-yellow-600",
62
+ },
63
+ red: { bg: "bg-red-50", ring: "ring-red-200", text: "text-red-600" },
64
+ teal: { bg: "bg-teal-50", ring: "ring-teal-200", text: "text-teal-600" },
65
+ };
66
+
67
+ /** What a palette row needs to paint itself — command and provider results both reduce to this. */
68
+ export interface PaletteRowItem {
69
+ /**
70
+ * Unique across everything visible in the palette (recents are prefixed,
71
+ * provider results are namespaced by provider id). Used for the option's
72
+ * DOM id and data-testid.
73
+ */
74
+ id: string;
75
+ title: string;
76
+ description?: string | undefined;
77
+ icon?: IconProp | undefined;
78
+ iconColor?: string | undefined;
79
+ shortcut?: Array<KeyboardShortcutKey> | undefined;
80
+ }
81
+
82
+ export interface ComponentProps {
83
+ item: PaletteRowItem;
84
+ /** Current search text — matching runs of title/description get a <mark>. */
85
+ query: string;
86
+ isActive: boolean;
87
+ /** Selection follows the mouse: fired on mousemove over the row. */
88
+ onActivate: () => void;
89
+ onSelect: () => void;
90
+ setRowElement: (element: HTMLDivElement | null) => void;
91
+ }
92
+
93
+ // Wrap the portions of text that match the query in a highlight.
94
+ const highlightMatch: (text: string, query: string) => ReactNode = (
95
+ text: string,
96
+ query: string,
97
+ ): ReactNode => {
98
+ const segments: Array<PaletteHighlightSegment> = getHighlightSegments(
99
+ text,
100
+ query,
101
+ );
102
+ return segments.map((segment: PaletteHighlightSegment, index: number) => {
103
+ if (!segment.isMatch) {
104
+ return <React.Fragment key={index}>{segment.text}</React.Fragment>;
105
+ }
106
+ return (
107
+ <mark
108
+ key={index}
109
+ className="rounded-sm bg-yellow-100 px-0.5 text-inherit"
110
+ >
111
+ {segment.text}
112
+ </mark>
113
+ );
114
+ });
115
+ };
116
+
117
+ const PaletteRow: FunctionComponent<ComponentProps> = (
118
+ props: ComponentProps,
119
+ ): ReactElement => {
120
+ const item: PaletteRowItem = props.item;
121
+ const colors: IconColorClasses =
122
+ ICON_COLOR_CLASSES[item.iconColor || "indigo"] ||
123
+ ICON_COLOR_CLASSES["indigo"]!;
124
+
125
+ return (
126
+ <div
127
+ id={`command-palette-option-${item.id}`}
128
+ data-testid={`command-palette-option-${item.id}`}
129
+ role="option"
130
+ aria-selected={props.isActive}
131
+ ref={(element: HTMLDivElement | null) => {
132
+ props.setRowElement(element);
133
+ }}
134
+ onMouseMove={() => {
135
+ props.onActivate();
136
+ }}
137
+ onClick={() => {
138
+ props.onSelect();
139
+ }}
140
+ className={`flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 transition-colors duration-100 ${
141
+ props.isActive ? "bg-indigo-50" : ""
142
+ }`}
143
+ >
144
+ {item.icon && (
145
+ <div
146
+ className={`flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg ring-1 ${colors.bg} ${colors.ring}`}
147
+ >
148
+ <Icon icon={item.icon} className={`h-4 w-4 ${colors.text}`} />
149
+ </div>
150
+ )}
151
+ <div className="min-w-0 flex-1">
152
+ <p className="truncate text-sm font-medium text-gray-900">
153
+ {highlightMatch(item.title, props.query)}
154
+ </p>
155
+ {item.description && (
156
+ <p className="truncate text-xs text-gray-500">
157
+ {highlightMatch(item.description, props.query)}
158
+ </p>
159
+ )}
160
+ </div>
161
+ {item.shortcut && item.shortcut.length > 0 && (
162
+ <KeyboardShortcut
163
+ keys={item.shortcut}
164
+ size={KeyboardShortcutSize.ExtraSmall}
165
+ variant={KeyboardShortcutVariant.Ghost}
166
+ className="flex-shrink-0"
167
+ />
168
+ )}
169
+ </div>
170
+ );
171
+ };
172
+
173
+ export default PaletteRow;
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Recently-run palette commands, persisted as an array of command ids in
3
+ * localStorage. Same guarded read/write shape as the navbar's recent products
4
+ * (NavBarMenuModal): storage can be absent (SSR), locked (private mode), or
5
+ * hold garbage — every failure mode degrades to "no recents", never a throw.
6
+ */
7
+
8
+ export const RECENT_COMMANDS_LIMIT: number = 7;
9
+
10
+ export const readRecentCommandIds: (storageKey: string) => Array<string> = (
11
+ storageKey: string,
12
+ ): Array<string> => {
13
+ try {
14
+ if (typeof window === "undefined" || !window.localStorage) {
15
+ return [];
16
+ }
17
+ const raw: string | null = window.localStorage.getItem(storageKey);
18
+ if (!raw) {
19
+ return [];
20
+ }
21
+ const parsed: unknown = JSON.parse(raw);
22
+ if (!Array.isArray(parsed)) {
23
+ return [];
24
+ }
25
+ return parsed
26
+ .filter((value: unknown): value is string => {
27
+ return typeof value === "string";
28
+ })
29
+ .slice(0, RECENT_COMMANDS_LIMIT);
30
+ } catch {
31
+ // Ignore storage access / parse errors (e.g. private mode, bad JSON).
32
+ return [];
33
+ }
34
+ };
35
+
36
+ export const writeRecentCommandId: (
37
+ storageKey: string,
38
+ commandId: string,
39
+ ) => void = (storageKey: string, commandId: string): void => {
40
+ try {
41
+ if (typeof window === "undefined" || !window.localStorage) {
42
+ return;
43
+ }
44
+ const existing: Array<string> = readRecentCommandIds(storageKey).filter(
45
+ (id: string) => {
46
+ return id !== commandId;
47
+ },
48
+ );
49
+ const next: Array<string> = [commandId, ...existing].slice(
50
+ 0,
51
+ RECENT_COMMANDS_LIMIT,
52
+ );
53
+ window.localStorage.setItem(storageKey, JSON.stringify(next));
54
+ } catch {
55
+ // Ignore storage write errors (e.g. private mode, quota exceeded).
56
+ }
57
+ };
@@ -0,0 +1,49 @@
1
+ import IconProp from "../../../Types/Icon/IconProp";
2
+ import { KeyboardShortcutKey } from "../KeyboardShortcut/KeyboardKey";
3
+
4
+ /**
5
+ * One executable entry in the command palette: a page to jump to, or an
6
+ * action to run. Commands are static (known before the user types) — async
7
+ * results from the backend go through PaletteSearchProvider instead.
8
+ */
9
+ export interface PaletteCommand {
10
+ /** Stable unique id — recents are persisted as a list of these ids. */
11
+ id: string;
12
+ title: string;
13
+ description?: string | undefined;
14
+ icon?: IconProp | undefined;
15
+ /** Tailwind color name like "blue" | "purple" — same convention as NavBar items. */
16
+ iconColor?: string | undefined;
17
+ /** Section the command is grouped under when browsing (e.g. "Essentials"). */
18
+ category: string;
19
+ /** Extra match terms the user might type that are not in the title. */
20
+ keywords?: Array<string> | undefined;
21
+ /** Optional shortcut hint rendered as keycaps on the row. */
22
+ shortcut?: Array<KeyboardShortcutKey> | undefined;
23
+ onSelect: () => void;
24
+ }
25
+
26
+ /** One row returned by an async search provider. */
27
+ export interface PaletteSearchResult {
28
+ /** Unique within the provider's result set. */
29
+ id: string;
30
+ title: string;
31
+ description?: string | undefined;
32
+ icon?: IconProp | undefined;
33
+ /** Tailwind color name like "blue" | "purple" — same convention as NavBar items. */
34
+ iconColor?: string | undefined;
35
+ onSelect: () => void;
36
+ }
37
+
38
+ /**
39
+ * Async source of palette rows (e.g. "search monitors by name"). Providers
40
+ * run in parallel once the query is long enough; a provider that rejects
41
+ * shows nothing — it must never take the palette down with it.
42
+ */
43
+ export interface PaletteSearchProvider {
44
+ /** Stable unique id, also used in section test ids. */
45
+ id: string;
46
+ /** Section heading shown above this provider's results. */
47
+ title: string;
48
+ search: (searchText: string) => Promise<Array<PaletteSearchResult>>;
49
+ }
@@ -0,0 +1,129 @@
1
+ import { PaletteSearchProvider, PaletteSearchResult } from "./Types";
2
+ import React, { useEffect, useMemo, useRef, useState } from "react";
3
+
4
+ /*
5
+ * Async search-provider plumbing for the command palette, isolated from the
6
+ * rendering so the timing contract (debounce, stale discard, per-provider
7
+ * error isolation) is directly unit-testable with fake timers.
8
+ */
9
+
10
+ export const PROVIDER_SEARCH_DEBOUNCE_MS: number = 200;
11
+ export const PROVIDER_SEARCH_MIN_QUERY_LENGTH: number = 2;
12
+
13
+ export interface ProviderSearchSection {
14
+ provider: PaletteSearchProvider;
15
+ results: Array<PaletteSearchResult>;
16
+ /** True from the moment the debounced search fires until this provider settles. */
17
+ isPending: boolean;
18
+ }
19
+
20
+ /**
21
+ * Runs every provider in parallel against the query, 200ms after the user
22
+ * stops typing and only once the query is at least 2 characters.
23
+ *
24
+ * - Stale responses are discarded: each debounced dispatch bumps a sequence
25
+ * token, and a response only lands if its token is still current.
26
+ * - Providers are isolated: one rejecting shows an empty section for that
27
+ * provider and never disturbs the others (and never throws).
28
+ *
29
+ * NOTE for callers: memoize the `providers` array (useMemo) — a fresh array
30
+ * identity on every render restarts the debounce timer each keystroke's
31
+ * re-render, effectively working but wastefully re-arming.
32
+ */
33
+ const useProviderSearch: (
34
+ providers: Array<PaletteSearchProvider>,
35
+ query: string,
36
+ ) => Array<ProviderSearchSection> = (
37
+ providers: Array<PaletteSearchProvider>,
38
+ query: string,
39
+ ): Array<ProviderSearchSection> => {
40
+ const [resultsByProviderId, setResultsByProviderId] = useState<
41
+ Record<string, Array<PaletteSearchResult>>
42
+ >({});
43
+ const [pendingByProviderId, setPendingByProviderId] = useState<
44
+ Record<string, boolean>
45
+ >({});
46
+ const sequenceRef: React.MutableRefObject<number> = useRef<number>(0);
47
+
48
+ const trimmedQuery: string = query.trim();
49
+
50
+ useEffect(() => {
51
+ if (providers.length === 0) {
52
+ return undefined;
53
+ }
54
+
55
+ if (trimmedQuery.length < PROVIDER_SEARCH_MIN_QUERY_LENGTH) {
56
+ // Invalidate anything still in flight and clear old results.
57
+ sequenceRef.current++;
58
+ setResultsByProviderId({});
59
+ setPendingByProviderId({});
60
+ return undefined;
61
+ }
62
+
63
+ const debounceTimer: ReturnType<typeof setTimeout> = setTimeout(() => {
64
+ const sequence: number = ++sequenceRef.current;
65
+
66
+ const allPending: Record<string, boolean> = {};
67
+ providers.forEach((provider: PaletteSearchProvider) => {
68
+ allPending[provider.id] = true;
69
+ });
70
+ setPendingByProviderId(allPending);
71
+
72
+ providers.forEach((provider: PaletteSearchProvider) => {
73
+ provider
74
+ .search(trimmedQuery)
75
+ .then((results: Array<PaletteSearchResult>) => {
76
+ if (sequence !== sequenceRef.current) {
77
+ return; // A newer query has been dispatched since.
78
+ }
79
+ setResultsByProviderId(
80
+ (
81
+ previous: Record<string, Array<PaletteSearchResult>>,
82
+ ): Record<string, Array<PaletteSearchResult>> => {
83
+ return { ...previous, [provider.id]: results };
84
+ },
85
+ );
86
+ setPendingByProviderId(
87
+ (previous: Record<string, boolean>): Record<string, boolean> => {
88
+ return { ...previous, [provider.id]: false };
89
+ },
90
+ );
91
+ })
92
+ .catch(() => {
93
+ // A failed provider simply shows nothing — never an error state.
94
+ if (sequence !== sequenceRef.current) {
95
+ return;
96
+ }
97
+ setResultsByProviderId(
98
+ (
99
+ previous: Record<string, Array<PaletteSearchResult>>,
100
+ ): Record<string, Array<PaletteSearchResult>> => {
101
+ return { ...previous, [provider.id]: [] };
102
+ },
103
+ );
104
+ setPendingByProviderId(
105
+ (previous: Record<string, boolean>): Record<string, boolean> => {
106
+ return { ...previous, [provider.id]: false };
107
+ },
108
+ );
109
+ });
110
+ });
111
+ }, PROVIDER_SEARCH_DEBOUNCE_MS);
112
+
113
+ return () => {
114
+ clearTimeout(debounceTimer);
115
+ };
116
+ }, [trimmedQuery, providers]);
117
+
118
+ return useMemo(() => {
119
+ return providers.map((provider: PaletteSearchProvider) => {
120
+ return {
121
+ provider,
122
+ results: resultsByProviderId[provider.id] || [],
123
+ isPending: Boolean(pendingByProviderId[provider.id]),
124
+ };
125
+ });
126
+ }, [providers, resultsByProviderId, pendingByProviderId]);
127
+ };
128
+
129
+ export default useProviderSearch;
@@ -589,7 +589,12 @@ const Dropdown: FunctionComponent<ComponentProps> = (
589
589
  return "text-gray-400 transition-colors duration-150 hover:text-red-500";
590
590
  },
591
591
  menu: () => {
592
- return "!mt-2 !rounded-xl !border !border-gray-100 !bg-white !shadow-xl";
592
+ /*
593
+ * ou-animate-pop-in (Theme.css) is a CSS animation, not a
594
+ * transition: react-select mounts the portalled menu already open,
595
+ * so there is no closed state to transition from.
596
+ */
597
+ return "!mt-2 !rounded-xl !border !border-gray-100 !bg-white !shadow-xl ou-animate-pop-in";
593
598
  },
594
599
  menuList: () => {
595
600
  return "!py-2";
@@ -8,7 +8,7 @@ import UserUtil from "../../Utils/User";
8
8
  import ObjectID from "../../../Types/ObjectID";
9
9
  import OneUptimeDate from "../../../Types/Date";
10
10
  import Tooltip from "../Tooltip/Tooltip";
11
- import MarkdownViewer from "../Markdown.tsx/MarkdownViewer";
11
+ import MarkdownViewer from "../Markdown.tsx/LazyMarkdownViewer";
12
12
  import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
13
13
  import ConfirmModal from "../Modal/ConfirmModal";
14
14
  import Icon from "../Icon/Icon";