@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,239 @@
1
+ import CommandPalette from "../../../../UI/Components/CommandPalette/CommandPalette";
2
+ import { PaletteCommand } from "../../../../UI/Components/CommandPalette/Types";
3
+ import { resetPageScrollLockForTesting } from "../../../../UI/Utils/PageScrollLock";
4
+ /*
5
+ * The main entry, not "/extend-expect": the latter no longer ships type
6
+ * declarations, so every jest-dom matcher in this file fails to typecheck and
7
+ * the whole suite is skipped before a single assertion runs.
8
+ */
9
+ import "@testing-library/jest-dom";
10
+ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
11
+ import React from "react";
12
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
13
+
14
+ /*
15
+ * The palette is portalled to document.body and handles its keys on the
16
+ * dialog root. This suite pins the flat-list keyboard contract AND what the
17
+ * rest of the document observes: handled keys bubble flagged
18
+ * defaultPrevented, so page-level shortcut listeners that respect the flag
19
+ * stay quiet — except Escape, which the topmost dialog consumes outright
20
+ * (preventDefault + stopPropagation), so document/window Escape listeners
21
+ * underneath never see it at all and cannot dismiss their own layer on the
22
+ * same keypress. Same document-listener technique as
23
+ * OperatorSelectorKeyboard.test.tsx.
24
+ */
25
+
26
+ interface ObservedKey {
27
+ key: string;
28
+ defaultPrevented: boolean;
29
+ }
30
+
31
+ // Keys that made it all the way up to a document-level listener, in order.
32
+ const documentKeys: Array<ObservedKey> = [];
33
+
34
+ const documentKeyDownListener: (event: KeyboardEvent) => void = (
35
+ event: KeyboardEvent,
36
+ ): void => {
37
+ documentKeys.push({
38
+ key: event.key,
39
+ defaultPrevented: event.defaultPrevented,
40
+ });
41
+ };
42
+
43
+ type MakeCommandFunction = (
44
+ id: string,
45
+ title: string,
46
+ onSelect?: () => void,
47
+ ) => PaletteCommand;
48
+
49
+ const makeCommand: MakeCommandFunction = (
50
+ id: string,
51
+ title: string,
52
+ onSelect?: () => void,
53
+ ): PaletteCommand => {
54
+ return {
55
+ id,
56
+ title,
57
+ category: "Pages",
58
+ onSelect: onSelect || ((): void => {}),
59
+ };
60
+ };
61
+
62
+ interface PaletteHarness {
63
+ input: HTMLElement;
64
+ onClose: jest.Mock;
65
+ }
66
+
67
+ type RenderPaletteFunction = (
68
+ commands: Array<PaletteCommand>,
69
+ ) => PaletteHarness;
70
+
71
+ const renderPalette: RenderPaletteFunction = (
72
+ commands: Array<PaletteCommand>,
73
+ ): PaletteHarness => {
74
+ const onClose: jest.Mock = jest.fn();
75
+ render(
76
+ <CommandPalette commands={commands} isOpen={true} onClose={onClose} />,
77
+ );
78
+ return { input: screen.getByTestId("command-palette-input"), onClose };
79
+ };
80
+
81
+ type ExpectSelectedFunction = (commandId: string) => void;
82
+
83
+ const expectSelected: ExpectSelectedFunction = (commandId: string): void => {
84
+ expect(
85
+ screen.getByTestId(`command-palette-option-${commandId}`),
86
+ ).toHaveAttribute("aria-selected", "true");
87
+ expect(screen.getByTestId("command-palette-input")).toHaveAttribute(
88
+ "aria-activedescendant",
89
+ `command-palette-option-${commandId}`,
90
+ );
91
+ };
92
+
93
+ const THREE_COMMANDS: Array<PaletteCommand> = [
94
+ makeCommand("one", "First"),
95
+ makeCommand("two", "Second"),
96
+ makeCommand("three", "Third"),
97
+ ];
98
+
99
+ describe("CommandPalette keyboard contract", () => {
100
+ beforeEach(() => {
101
+ resetPageScrollLockForTesting();
102
+ documentKeys.length = 0;
103
+ document.addEventListener("keydown", documentKeyDownListener);
104
+ });
105
+
106
+ afterEach(() => {
107
+ document.removeEventListener("keydown", documentKeyDownListener);
108
+ cleanup();
109
+ resetPageScrollLockForTesting();
110
+ });
111
+
112
+ test("ArrowDown walks the flat list and wraps from the last row to the first", () => {
113
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
114
+
115
+ expectSelected("one");
116
+
117
+ fireEvent.keyDown(harness.input, { key: "ArrowDown" });
118
+ expectSelected("two");
119
+
120
+ fireEvent.keyDown(harness.input, { key: "ArrowDown" });
121
+ expectSelected("three");
122
+
123
+ fireEvent.keyDown(harness.input, { key: "ArrowDown" });
124
+ expectSelected("one");
125
+ });
126
+
127
+ test("ArrowUp from the first row wraps to the last", () => {
128
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
129
+
130
+ fireEvent.keyDown(harness.input, { key: "ArrowUp" });
131
+
132
+ expectSelected("three");
133
+ });
134
+
135
+ test("Home and End jump to the first and last row", () => {
136
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
137
+
138
+ fireEvent.keyDown(harness.input, { key: "End" });
139
+ expectSelected("three");
140
+
141
+ fireEvent.keyDown(harness.input, { key: "Home" });
142
+ expectSelected("one");
143
+ });
144
+
145
+ test("Enter runs the active command and closes the palette", () => {
146
+ const onSelect: jest.Mock = jest.fn();
147
+ const harness: PaletteHarness = renderPalette([
148
+ makeCommand("one", "First"),
149
+ makeCommand("two", "Second", onSelect),
150
+ makeCommand("three", "Third"),
151
+ ]);
152
+
153
+ fireEvent.keyDown(harness.input, { key: "ArrowDown" });
154
+ fireEvent.keyDown(harness.input, { key: "Enter" });
155
+
156
+ expect(onSelect).toHaveBeenCalledTimes(1);
157
+ expect(harness.onClose).toHaveBeenCalledTimes(1);
158
+ });
159
+
160
+ test("Escape closes without running anything", () => {
161
+ const onSelect: jest.Mock = jest.fn();
162
+ const harness: PaletteHarness = renderPalette([
163
+ makeCommand("one", "First", onSelect),
164
+ ]);
165
+
166
+ fireEvent.keyDown(harness.input, { key: "Escape" });
167
+
168
+ expect(onSelect).not.toHaveBeenCalled();
169
+ expect(harness.onClose).toHaveBeenCalledTimes(1);
170
+ });
171
+
172
+ test("handled keys are defaultPrevented; plain typing is not", () => {
173
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
174
+
175
+ // fireEvent returns false when a handler called preventDefault().
176
+ expect(fireEvent.keyDown(harness.input, { key: "ArrowDown" })).toBe(false);
177
+ expect(fireEvent.keyDown(harness.input, { key: "ArrowUp" })).toBe(false);
178
+ expect(fireEvent.keyDown(harness.input, { key: "Home" })).toBe(false);
179
+ expect(fireEvent.keyDown(harness.input, { key: "End" })).toBe(false);
180
+ expect(fireEvent.keyDown(harness.input, { key: "Enter" })).toBe(false);
181
+ expect(fireEvent.keyDown(harness.input, { key: "Escape" })).toBe(false);
182
+
183
+ // Ordinary characters belong to the input — the palette must not eat them.
184
+ expect(fireEvent.keyDown(harness.input, { key: "a" })).toBe(true);
185
+ });
186
+
187
+ test("handled keys bubble flagged as consumed — except Escape, which never reaches the document", () => {
188
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
189
+
190
+ fireEvent.keyDown(harness.input, { key: "ArrowDown" });
191
+ fireEvent.keyDown(harness.input, { key: "Escape" });
192
+
193
+ /*
194
+ * Escape is absent: the palette stopPropagation's its dismiss key so a
195
+ * dialog layered underneath (Ask AI's window listener) cannot also close
196
+ * itself on the same keypress.
197
+ */
198
+ expect(documentKeys).toEqual([
199
+ { key: "ArrowDown", defaultPrevented: true },
200
+ ]);
201
+ expect(harness.onClose).toHaveBeenCalledTimes(1);
202
+ });
203
+
204
+ test("navigation keys do nothing (and stay unprevented) on an empty list", () => {
205
+ const harness: PaletteHarness = renderPalette([]);
206
+
207
+ expect(fireEvent.keyDown(harness.input, { key: "ArrowDown" })).toBe(true);
208
+ expect(fireEvent.keyDown(harness.input, { key: "Enter" })).toBe(true);
209
+ expect(harness.onClose).not.toHaveBeenCalled();
210
+
211
+ // Escape still closes an empty palette.
212
+ expect(fireEvent.keyDown(harness.input, { key: "Escape" })).toBe(false);
213
+ expect(harness.onClose).toHaveBeenCalledTimes(1);
214
+ });
215
+
216
+ test("selection follows the mouse, and the keyboard continues from there", () => {
217
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
218
+
219
+ fireEvent.mouseMove(screen.getByTestId("command-palette-option-two"));
220
+ expectSelected("two");
221
+
222
+ fireEvent.keyDown(harness.input, { key: "ArrowDown" });
223
+ expectSelected("three");
224
+ });
225
+
226
+ test("the selection resets to the top when the query changes", () => {
227
+ const harness: PaletteHarness = renderPalette(THREE_COMMANDS);
228
+
229
+ fireEvent.keyDown(harness.input, { key: "End" });
230
+ expectSelected("three");
231
+
232
+ fireEvent.change(harness.input, { target: { value: "t" } });
233
+
234
+ // "t" matches "Third" by prefix — it ranks first and is selected.
235
+ expectSelected("three");
236
+ fireEvent.change(harness.input, { target: { value: "" } });
237
+ expectSelected("one");
238
+ });
239
+ });
@@ -0,0 +1,400 @@
1
+ import CommandPalette from "../../../../UI/Components/CommandPalette/CommandPalette";
2
+ import {
3
+ PaletteCommand,
4
+ PaletteSearchProvider,
5
+ PaletteSearchResult,
6
+ } from "../../../../UI/Components/CommandPalette/Types";
7
+ import {
8
+ PROVIDER_SEARCH_DEBOUNCE_MS,
9
+ PROVIDER_SEARCH_MIN_QUERY_LENGTH,
10
+ } from "../../../../UI/Components/CommandPalette/UseProviderSearch";
11
+ import { resetPageScrollLockForTesting } from "../../../../UI/Utils/PageScrollLock";
12
+ /*
13
+ * The main entry, not "/extend-expect": the latter no longer ships type
14
+ * declarations, so every jest-dom matcher in this file fails to typecheck and
15
+ * the whole suite is skipped before a single assertion runs.
16
+ */
17
+ import "@testing-library/jest-dom";
18
+ import {
19
+ act,
20
+ cleanup,
21
+ fireEvent,
22
+ render,
23
+ screen,
24
+ } from "@testing-library/react";
25
+ import React from "react";
26
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
27
+
28
+ /*
29
+ * Async search-provider contract: debounce, minimum query length, parallel
30
+ * dispatch, stale-response discard, per-provider error isolation, and the
31
+ * pending indicator. Fake timers drive the debounce; deferred promises let
32
+ * each test settle providers in whatever order it needs.
33
+ */
34
+
35
+ interface Deferred<T> {
36
+ promise: Promise<T>;
37
+ resolve: (value: T) => void;
38
+ reject: (error: Error) => void;
39
+ }
40
+
41
+ type CreateDeferredFunction = <T>() => Deferred<T>;
42
+
43
+ const createDeferred: CreateDeferredFunction = <T,>(): Deferred<T> => {
44
+ let resolveFunction: (value: T) => void = (): void => {};
45
+ let rejectFunction: (error: Error) => void = (): void => {};
46
+ const promise: Promise<T> = new Promise<T>(
47
+ (resolve: (value: T) => void, reject: (error: Error) => void) => {
48
+ resolveFunction = resolve;
49
+ rejectFunction = reject;
50
+ },
51
+ );
52
+ return { promise, resolve: resolveFunction, reject: rejectFunction };
53
+ };
54
+
55
+ type MakeResultFunction = (
56
+ id: string,
57
+ title: string,
58
+ onSelect?: () => void,
59
+ ) => PaletteSearchResult;
60
+
61
+ const makeResult: MakeResultFunction = (
62
+ id: string,
63
+ title: string,
64
+ onSelect?: () => void,
65
+ ): PaletteSearchResult => {
66
+ return { id, title, onSelect: onSelect || ((): void => {}) };
67
+ };
68
+
69
+ const BASE_COMMANDS: Array<PaletteCommand> = [
70
+ {
71
+ id: "go-monitors",
72
+ title: "Monitors",
73
+ category: "Pages",
74
+ onSelect: (): void => {},
75
+ },
76
+ ];
77
+
78
+ /*
79
+ * Async on purpose: a provider that returns an already-resolved promise
80
+ * settles on the microtask queue right after the timers fire, and awaiting
81
+ * the act() keeps that state update wrapped.
82
+ */
83
+ async function advance(milliseconds: number): Promise<void> {
84
+ await act(async () => {
85
+ jest.advanceTimersByTime(milliseconds);
86
+ });
87
+ }
88
+
89
+ type TypeQueryFunction = (value: string) => void;
90
+
91
+ const typeQuery: TypeQueryFunction = (value: string): void => {
92
+ fireEvent.change(screen.getByTestId("command-palette-input"), {
93
+ target: { value },
94
+ });
95
+ };
96
+
97
+ describe("CommandPalette search providers", () => {
98
+ beforeEach(() => {
99
+ jest.useFakeTimers();
100
+ resetPageScrollLockForTesting();
101
+ });
102
+
103
+ afterEach(() => {
104
+ cleanup();
105
+ jest.useRealTimers();
106
+ resetPageScrollLockForTesting();
107
+ });
108
+
109
+ test("does not search until the query reaches the minimum length", async () => {
110
+ const search: jest.Mock = jest.fn(() => {
111
+ return Promise.resolve([]);
112
+ });
113
+ const provider: PaletteSearchProvider = {
114
+ id: "monitors",
115
+ title: "Monitors",
116
+ search,
117
+ };
118
+ render(
119
+ <CommandPalette
120
+ commands={BASE_COMMANDS}
121
+ searchProviders={[provider]}
122
+ isOpen={true}
123
+ onClose={(): void => {}}
124
+ />,
125
+ );
126
+
127
+ typeQuery("m");
128
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS * 3);
129
+
130
+ expect(search).not.toHaveBeenCalled();
131
+ expect(PROVIDER_SEARCH_MIN_QUERY_LENGTH).toBeGreaterThan(1);
132
+ expect(screen.queryByTestId("command-palette-section-monitors")).toBeNull();
133
+ });
134
+
135
+ test("debounces: a burst of keystrokes produces one search with the final text", async () => {
136
+ const search: jest.Mock = jest.fn(() => {
137
+ return Promise.resolve([]);
138
+ });
139
+ render(
140
+ <CommandPalette
141
+ commands={BASE_COMMANDS}
142
+ searchProviders={[{ id: "monitors", title: "Monitors", search }]}
143
+ isOpen={true}
144
+ onClose={(): void => {}}
145
+ />,
146
+ );
147
+
148
+ typeQuery("mo");
149
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS - 100);
150
+ typeQuery("mon");
151
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS - 1);
152
+
153
+ expect(search).not.toHaveBeenCalled();
154
+
155
+ await advance(1);
156
+
157
+ expect(search).toHaveBeenCalledTimes(1);
158
+ expect(search).toHaveBeenCalledWith("mon");
159
+ });
160
+
161
+ test("shows a pending indicator per section while a provider is in flight", async () => {
162
+ const deferred: Deferred<Array<PaletteSearchResult>> =
163
+ createDeferred<Array<PaletteSearchResult>>();
164
+ const search: jest.Mock = jest.fn(() => {
165
+ return deferred.promise;
166
+ });
167
+ render(
168
+ <CommandPalette
169
+ commands={BASE_COMMANDS}
170
+ searchProviders={[{ id: "monitors", title: "Monitors", search }]}
171
+ isOpen={true}
172
+ onClose={(): void => {}}
173
+ />,
174
+ );
175
+
176
+ typeQuery("api");
177
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS);
178
+
179
+ expect(
180
+ screen.getByTestId("command-palette-section-pending-monitors"),
181
+ ).toBeInTheDocument();
182
+
183
+ await act(async () => {
184
+ deferred.resolve([makeResult("mon-1", "API Monitor")]);
185
+ });
186
+
187
+ expect(
188
+ screen.queryByTestId("command-palette-section-pending-monitors"),
189
+ ).toBeNull();
190
+ expect(
191
+ screen.getByTestId("command-palette-option-monitors-mon-1"),
192
+ ).toHaveTextContent("API Monitor");
193
+ });
194
+
195
+ test("runs all providers in parallel and renders one section per provider", async () => {
196
+ const monitorsDeferred: Deferred<Array<PaletteSearchResult>> =
197
+ createDeferred<Array<PaletteSearchResult>>();
198
+ const incidentsDeferred: Deferred<Array<PaletteSearchResult>> =
199
+ createDeferred<Array<PaletteSearchResult>>();
200
+ const monitorsSearch: jest.Mock = jest.fn(() => {
201
+ return monitorsDeferred.promise;
202
+ });
203
+ const incidentsSearch: jest.Mock = jest.fn(() => {
204
+ return incidentsDeferred.promise;
205
+ });
206
+ render(
207
+ <CommandPalette
208
+ commands={BASE_COMMANDS}
209
+ searchProviders={[
210
+ { id: "monitors", title: "Monitors", search: monitorsSearch },
211
+ { id: "incidents", title: "Incidents", search: incidentsSearch },
212
+ ]}
213
+ isOpen={true}
214
+ onClose={(): void => {}}
215
+ />,
216
+ );
217
+
218
+ typeQuery("api");
219
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS);
220
+
221
+ // Both dispatched before either resolves — parallel, not sequential.
222
+ expect(monitorsSearch).toHaveBeenCalledTimes(1);
223
+ expect(incidentsSearch).toHaveBeenCalledTimes(1);
224
+
225
+ await act(async () => {
226
+ monitorsDeferred.resolve([makeResult("m1", "API Monitor")]);
227
+ incidentsDeferred.resolve([makeResult("i1", "API outage")]);
228
+ });
229
+
230
+ expect(
231
+ screen.getByTestId("command-palette-section-monitors"),
232
+ ).toBeInTheDocument();
233
+ expect(
234
+ screen.getByTestId("command-palette-section-incidents"),
235
+ ).toBeInTheDocument();
236
+ expect(
237
+ screen.getByTestId("command-palette-option-monitors-m1"),
238
+ ).toBeInTheDocument();
239
+ expect(
240
+ screen.getByTestId("command-palette-option-incidents-i1"),
241
+ ).toBeInTheDocument();
242
+ });
243
+
244
+ test("discards a stale response that lands after a newer query was dispatched", async () => {
245
+ const firstDeferred: Deferred<Array<PaletteSearchResult>> =
246
+ createDeferred<Array<PaletteSearchResult>>();
247
+ const secondDeferred: Deferred<Array<PaletteSearchResult>> =
248
+ createDeferred<Array<PaletteSearchResult>>();
249
+ const search: jest.Mock = jest
250
+ .fn(() => {
251
+ return secondDeferred.promise;
252
+ })
253
+ .mockImplementationOnce(() => {
254
+ return firstDeferred.promise;
255
+ });
256
+ render(
257
+ <CommandPalette
258
+ commands={BASE_COMMANDS}
259
+ searchProviders={[{ id: "monitors", title: "Monitors", search }]}
260
+ isOpen={true}
261
+ onClose={(): void => {}}
262
+ />,
263
+ );
264
+
265
+ typeQuery("ap");
266
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS);
267
+ typeQuery("api");
268
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS);
269
+
270
+ expect(search).toHaveBeenCalledTimes(2);
271
+
272
+ // The FIRST (now stale) response arrives after the second dispatch.
273
+ await act(async () => {
274
+ firstDeferred.resolve([makeResult("stale", "Stale Monitor")]);
275
+ });
276
+
277
+ expect(
278
+ screen.queryByTestId("command-palette-option-monitors-stale"),
279
+ ).toBeNull();
280
+ // Still waiting on the current dispatch.
281
+ expect(
282
+ screen.getByTestId("command-palette-section-pending-monitors"),
283
+ ).toBeInTheDocument();
284
+
285
+ await act(async () => {
286
+ secondDeferred.resolve([makeResult("fresh", "Fresh Monitor")]);
287
+ });
288
+
289
+ expect(
290
+ screen.getByTestId("command-palette-option-monitors-fresh"),
291
+ ).toHaveTextContent("Fresh Monitor");
292
+ });
293
+
294
+ test("a rejecting provider shows nothing while the others still render", async () => {
295
+ const failingSearch: jest.Mock = jest.fn(() => {
296
+ return Promise.reject(new Error("backend down"));
297
+ });
298
+ const workingDeferred: Deferred<Array<PaletteSearchResult>> =
299
+ createDeferred<Array<PaletteSearchResult>>();
300
+ const workingSearch: jest.Mock = jest.fn(() => {
301
+ return workingDeferred.promise;
302
+ });
303
+ render(
304
+ <CommandPalette
305
+ commands={BASE_COMMANDS}
306
+ searchProviders={[
307
+ { id: "incidents", title: "Incidents", search: failingSearch },
308
+ { id: "monitors", title: "Monitors", search: workingSearch },
309
+ ]}
310
+ isOpen={true}
311
+ onClose={(): void => {}}
312
+ />,
313
+ );
314
+
315
+ typeQuery("api");
316
+ await advance(PROVIDER_SEARCH_DEBOUNCE_MS);
317
+
318
+ await act(async () => {
319
+ workingDeferred.resolve([makeResult("m1", "API Monitor")]);
320
+ });
321
+
322
+ // The failed provider's section vanished entirely — no error surface.
323
+ expect(
324
+ screen.queryByTestId("command-palette-section-incidents"),
325
+ ).toBeNull();
326
+ expect(
327
+ screen.queryByTestId("command-palette-section-pending-incidents"),
328
+ ).toBeNull();
329
+ expect(
330
+ screen.getByTestId("command-palette-option-monitors-m1"),
331
+ ).toBeInTheDocument();
332
+ });
333
+
334
+ test("provider results join the flat keyboard order and Enter selects them", async () => {
335
+ const onResultSelect: jest.Mock = jest.fn();
336
+ const onClose: jest.Mock = jest.fn();
337
+ const search: jest.Mock = jest.fn(() => {
338
+ return Promise.resolve([makeResult("m1", "API Monitor", onResultSelect)]);
339
+ });
340
+ render(
341
+ <CommandPalette
342
+ commands={BASE_COMMANDS}
343
+ searchProviders={[{ id: "monitors", title: "Monitors", search }]}
344
+ isOpen={true}
345
+ onClose={onClose}
346
+ recentStorageKey="provider-recents-key"
347
+ />,
348
+ );
349
+
350
+ typeQuery("monitors");
351
+ await act(async () => {
352
+ jest.advanceTimersByTime(PROVIDER_SEARCH_DEBOUNCE_MS);
353
+ });
354
+
355
+ const input: HTMLElement = screen.getByTestId("command-palette-input");
356
+
357
+ // Flat order: the matching command first, then the provider row.
358
+ fireEvent.keyDown(input, { key: "ArrowDown" });
359
+ expect(
360
+ screen.getByTestId("command-palette-option-monitors-m1"),
361
+ ).toHaveAttribute("aria-selected", "true");
362
+
363
+ fireEvent.keyDown(input, { key: "Enter" });
364
+
365
+ expect(onResultSelect).toHaveBeenCalledTimes(1);
366
+ expect(onClose).toHaveBeenCalledTimes(1);
367
+ // Provider results are not commands: nothing is recorded into recents.
368
+ expect(window.localStorage.getItem("provider-recents-key")).toBeNull();
369
+ });
370
+
371
+ test("clearing the query below the minimum drops provider results", async () => {
372
+ const search: jest.Mock = jest.fn(() => {
373
+ return Promise.resolve([makeResult("m1", "API Monitor")]);
374
+ });
375
+ render(
376
+ <CommandPalette
377
+ commands={BASE_COMMANDS}
378
+ searchProviders={[{ id: "monitors", title: "Monitors", search }]}
379
+ isOpen={true}
380
+ onClose={(): void => {}}
381
+ />,
382
+ );
383
+
384
+ typeQuery("api");
385
+ await act(async () => {
386
+ jest.advanceTimersByTime(PROVIDER_SEARCH_DEBOUNCE_MS);
387
+ });
388
+
389
+ expect(
390
+ screen.getByTestId("command-palette-option-monitors-m1"),
391
+ ).toBeInTheDocument();
392
+
393
+ typeQuery("");
394
+
395
+ expect(
396
+ screen.queryByTestId("command-palette-option-monitors-m1"),
397
+ ).toBeNull();
398
+ expect(screen.queryByTestId("command-palette-section-monitors")).toBeNull();
399
+ });
400
+ });