@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,519 @@
1
+ /*
2
+ * PasswordHash carries a pre-existing TS5.9 diagnostic that fails any suite
3
+ * whose runtime require graph reaches it (DatabaseService, the base class of
4
+ * every concrete service, imports it). Nothing password-related is under
5
+ * test here, so the module is replaced WITH A FACTORY — an automock would
6
+ * still require (and type-check) the real file.
7
+ */
8
+ jest.mock("../../../Server/Utils/PasswordHash", () => {
9
+ return {
10
+ __esModule: true,
11
+ default: {
12
+ hash: jest.fn(),
13
+ verify: jest.fn(),
14
+ generateSalt: jest.fn(),
15
+ needsUpgrade: jest.fn(),
16
+ applyPepper: jest.fn(),
17
+ },
18
+ };
19
+ });
20
+
21
+ jest.mock("../../../Server/Utils/Express", () => {
22
+ return {
23
+ getRouter: () => {
24
+ return mockRouter;
25
+ },
26
+ };
27
+ });
28
+
29
+ jest.mock("../../../Server/Utils/Response", () => {
30
+ return {
31
+ sendEntityArrayResponse: jest.fn().mockImplementation((...args: []) => {
32
+ return args;
33
+ }),
34
+ sendJsonObjectResponse: jest.fn().mockImplementation((...args: []) => {
35
+ return args;
36
+ }),
37
+ sendEmptySuccessResponse: jest.fn(),
38
+ sendEntityResponse: jest.fn().mockImplementation((...args: []) => {
39
+ return args;
40
+ }),
41
+ };
42
+ });
43
+
44
+ /*
45
+ * The real DatabaseService pulls in the whole server dependency graph. The
46
+ * suite drives BaseAPI.getList with hand-built service stubs instead, so the
47
+ * class is reduced to a mock constructor. getList's stock-hook check compares
48
+ * service.onBeforeFind against DatabaseService.prototype.onBeforeFind BY
49
+ * REFERENCE, so the mock's prototype must carry a real function — a bare
50
+ * jest.fn() constructor's prototype has no onBeforeFind, and every stub would
51
+ * then pass the compare as undefined === undefined, never exercising the
52
+ * inherited-vs-own mechanics the gate relies on. Stock-path stubs inherit the
53
+ * hook (createServiceStub below); override-path stubs shadow it with their
54
+ * own function. The returned instance still needs the methods that concrete
55
+ * services invoke at module load (the require graph instantiates them all),
56
+ * mirroring BaseAPI.test.ts.
57
+ */
58
+ jest.mock("../../../Server/Services/DatabaseService", () => {
59
+ const DatabaseServiceMock: jest.Mock = jest.fn().mockImplementation(() => {
60
+ return {
61
+ countBy: jest.fn(),
62
+ findBy: jest.fn(),
63
+ findOneById: jest.fn(),
64
+ deleteOneBy: jest.fn(),
65
+ deleteOneById: jest.fn(),
66
+ updateOneById: jest.fn(),
67
+ updateOneBy: jest.fn(),
68
+ create: jest.fn(),
69
+ hardDeleteItemsOlderThanInDays: jest.fn(),
70
+ };
71
+ });
72
+
73
+ /*
74
+ * The shared stock hook: a named function on the prototype, so the gate's
75
+ * reference compare works against the same shared-function-inheritance
76
+ * shape it sees in production.
77
+ */
78
+ function stockOnBeforeFind(findBy: unknown): Promise<unknown> {
79
+ return Promise.resolve(findBy);
80
+ }
81
+
82
+ (
83
+ DatabaseServiceMock as unknown as {
84
+ prototype: { onBeforeFind: unknown };
85
+ }
86
+ ).prototype.onBeforeFind = stockOnBeforeFind;
87
+
88
+ return DatabaseServiceMock;
89
+ });
90
+
91
+ jest.mock(
92
+ "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel",
93
+ () => {
94
+ return jest.fn().mockImplementation((initObject: GenericObject) => {
95
+ return {
96
+ ...initObject,
97
+ getCrudApiPath: jest.fn().mockImplementation(() => {
98
+ return "/mock";
99
+ }),
100
+ getTableColumnMetadata: jest.fn().mockImplementation(() => {
101
+ return null;
102
+ }),
103
+ };
104
+ });
105
+ },
106
+ );
107
+
108
+ jest.mock("../../../Server/Services/ProjectService", () => {
109
+ return {
110
+ getCurrentPlan: () => {
111
+ return {
112
+ currentPlan: "Free",
113
+ isSubscriptionUnpaid: false,
114
+ };
115
+ },
116
+ };
117
+ });
118
+
119
+ jest.mock("../../../Server/EnvironmentConfig", () => {
120
+ return {
121
+ ...jest.requireActual("../../../Server/EnvironmentConfig"),
122
+ IsBillingEnabled: true,
123
+ LogLevel: "INFO",
124
+ DisableTelemetry: true,
125
+ };
126
+ });
127
+
128
+ import BaseAPI from "../../../Server/API/BaseAPI";
129
+ import DatabaseService from "../../../Server/Services/DatabaseService";
130
+ import {
131
+ ExpressResponse,
132
+ OneUptimeRequest,
133
+ } from "../../../Server/Utils/Express";
134
+ import Response from "../../../Server/Utils/Response";
135
+ import { mockRouter } from "./Helpers";
136
+ import { afterEach, describe, expect, it } from "@jest/globals";
137
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
138
+ import GenericObject from "../../../Types/GenericObject";
139
+ import { JSONObject } from "../../../Types/JSON";
140
+ import PositiveNumber from "../../../Types/PositiveNumber";
141
+
142
+ // eslint-disable-next-line @typescript-eslint/typedef
143
+ const res = {
144
+ status: jest.fn().mockReturnThis(),
145
+ json: jest.fn().mockReturnThis(),
146
+ send: jest.fn().mockReturnThis(),
147
+ } as any as ExpressResponse;
148
+
149
+ type Deferred<T> = {
150
+ promise: Promise<T>;
151
+ resolve: (value: T) => void;
152
+ reject: (error: Error) => void;
153
+ };
154
+
155
+ function createDeferred<T>(): Deferred<T> {
156
+ let resolveFn: ((value: T) => void) | null = null;
157
+ let rejectFn: ((error: Error) => void) | null = null;
158
+
159
+ const promise: Promise<T> = new Promise<T>(
160
+ (resolve: (value: T) => void, reject: (error: Error) => void) => {
161
+ resolveFn = resolve;
162
+ rejectFn = reject;
163
+ },
164
+ );
165
+
166
+ return {
167
+ promise,
168
+ resolve: resolveFn!,
169
+ reject: rejectFn!,
170
+ };
171
+ }
172
+
173
+ /*
174
+ * getList awaits a handful of internal steps (onBeforeList, the common-props
175
+ * builder) before dispatching the service calls; draining a generous number
176
+ * of microtask turns lets every already-startable step run without ever
177
+ * resolving the deferred service promises.
178
+ */
179
+ async function flushMicrotasks(): Promise<void> {
180
+ for (let i: number = 0; i < 25; i++) {
181
+ await Promise.resolve();
182
+ }
183
+ }
184
+
185
+ type ServiceStub = {
186
+ findBy: jest.Mock;
187
+ countBy: jest.Mock;
188
+ onBeforeFind?: (...args: Array<unknown>) => Promise<unknown>;
189
+ };
190
+
191
+ /*
192
+ * Stock-path stubs must INHERIT onBeforeFind from the mocked DatabaseService
193
+ * prototype — exactly how a concrete service that never overrides the hook
194
+ * holds it in production — so getList's reference compare is exercised for
195
+ * real. Override-path tests shadow it by assigning an own function.
196
+ */
197
+ function createServiceStub(): ServiceStub {
198
+ const stub: ServiceStub = Object.create(
199
+ (DatabaseService as unknown as { prototype: Record<string, unknown> })
200
+ .prototype,
201
+ ) as ServiceStub;
202
+ stub.findBy = jest.fn();
203
+ stub.countBy = jest.fn();
204
+ return stub;
205
+ }
206
+
207
+ function createApi(
208
+ service: ServiceStub,
209
+ ): BaseAPI<BaseModel, DatabaseService<BaseModel>> {
210
+ return new BaseAPI<BaseModel, DatabaseService<BaseModel>>(
211
+ BaseModel,
212
+ service as unknown as DatabaseService<BaseModel>,
213
+ );
214
+ }
215
+
216
+ function createListRequest(body?: JSONObject): OneUptimeRequest {
217
+ return {
218
+ query: {},
219
+ headers: {},
220
+ ...(body ? { body } : {}),
221
+ } as unknown as OneUptimeRequest;
222
+ }
223
+
224
+ describe("BaseAPI.getList findBy/countBy parallel dispatch", () => {
225
+ afterEach(() => {
226
+ jest.clearAllMocks();
227
+ });
228
+
229
+ describe("harness guard: the stock-hook compare stays non-degenerate", () => {
230
+ it("gives stock stubs the inherited prototype hook and override stubs their own", () => {
231
+ const proto: { onBeforeFind: unknown } = (
232
+ DatabaseService as unknown as {
233
+ prototype: { onBeforeFind: unknown };
234
+ }
235
+ ).prototype;
236
+
237
+ /*
238
+ * The gate under test is a function-reference equality. If the mocked
239
+ * prototype ever loses its hook, both sides collapse to undefined and
240
+ * every stub — overridden or not — would take the parallel path, so
241
+ * pin that the two compares genuinely differ.
242
+ */
243
+ expect(typeof proto.onBeforeFind).toBe("function");
244
+
245
+ const stockStub: ServiceStub = createServiceStub();
246
+ expect(stockStub.onBeforeFind).toBe(proto.onBeforeFind);
247
+
248
+ const overrideStub: ServiceStub = createServiceStub();
249
+ overrideStub.onBeforeFind = jest.fn() as unknown as (
250
+ ...args: Array<unknown>
251
+ ) => Promise<unknown>;
252
+ expect(overrideStub.onBeforeFind).not.toBe(proto.onBeforeFind);
253
+ });
254
+ });
255
+
256
+ describe("service with the stock onBeforeFind hook (parallel path)", () => {
257
+ it("starts countBy before findBy has resolved, and sends the unchanged response envelope", async () => {
258
+ const service: ServiceStub = createServiceStub();
259
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
260
+ service,
261
+ );
262
+
263
+ const findDeferred: Deferred<Array<BaseModel>> =
264
+ createDeferred<Array<BaseModel>>();
265
+ const countDeferred: Deferred<PositiveNumber> =
266
+ createDeferred<PositiveNumber>();
267
+
268
+ service.findBy.mockReturnValue(findDeferred.promise);
269
+ service.countBy.mockReturnValue(countDeferred.promise);
270
+
271
+ const request: OneUptimeRequest = createListRequest();
272
+ const pending: Promise<void> = api.getList(request, res);
273
+
274
+ await flushMicrotasks();
275
+
276
+ /*
277
+ * The core of the change: with findBy still pending, countBy must
278
+ * already have been dispatched. The old sequential code awaited findBy
279
+ * to completion first, so countBy would not have been called here.
280
+ */
281
+ expect(service.findBy).toHaveBeenCalledTimes(1);
282
+ expect(service.countBy).toHaveBeenCalledTimes(1);
283
+
284
+ const list: Array<BaseModel> = [
285
+ { id: "mock" },
286
+ ] as unknown as Array<BaseModel>;
287
+ const count: PositiveNumber = new PositiveNumber(42);
288
+
289
+ findDeferred.resolve(list);
290
+ countDeferred.resolve(count);
291
+ await pending;
292
+
293
+ // Same envelope as before: (req, res, list, count, entityType).
294
+ expect(Response.sendEntityArrayResponse).toHaveBeenCalledTimes(1);
295
+ expect(Response.sendEntityArrayResponse).toHaveBeenCalledWith(
296
+ request,
297
+ res,
298
+ list,
299
+ count,
300
+ BaseModel,
301
+ );
302
+ });
303
+
304
+ it("gives countBy its own copies of query and props so neither call can see the other's in-place edits", async () => {
305
+ const service: ServiceStub = createServiceStub();
306
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
307
+ service,
308
+ );
309
+
310
+ service.findBy.mockResolvedValue([]);
311
+ service.countBy.mockResolvedValue(new PositiveNumber(0));
312
+
313
+ const request: OneUptimeRequest = createListRequest({
314
+ query: { name: "zeta" },
315
+ select: { name: true },
316
+ sort: {},
317
+ });
318
+
319
+ await api.getList(request, res);
320
+
321
+ // eslint-disable-next-line @typescript-eslint/typedef
322
+ const findArgs = service.findBy.mock.calls[0]?.[0] as {
323
+ query: JSONObject;
324
+ props: JSONObject;
325
+ };
326
+ // eslint-disable-next-line @typescript-eslint/typedef
327
+ const countArgs = service.countBy.mock.calls[0]?.[0] as {
328
+ query: JSONObject;
329
+ props: JSONObject;
330
+ };
331
+
332
+ // Same effective query and props for both calls...
333
+ expect(countArgs.query).toEqual(findArgs.query);
334
+ expect(countArgs.query).toEqual({ name: "zeta" });
335
+ expect(countArgs.props).toEqual(findArgs.props);
336
+
337
+ // ...but distinct objects, so concurrent mutation cannot leak across.
338
+ expect(countArgs.query).not.toBe(findArgs.query);
339
+ expect(countArgs.props).not.toBe(findArgs.props);
340
+ });
341
+
342
+ it("rejects with findBy's error when findBy fails", async () => {
343
+ const service: ServiceStub = createServiceStub();
344
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
345
+ service,
346
+ );
347
+
348
+ const findDeferred: Deferred<Array<BaseModel>> =
349
+ createDeferred<Array<BaseModel>>();
350
+ const countDeferred: Deferred<PositiveNumber> =
351
+ createDeferred<PositiveNumber>();
352
+
353
+ service.findBy.mockReturnValue(findDeferred.promise);
354
+ service.countBy.mockReturnValue(countDeferred.promise);
355
+
356
+ const pending: Promise<void> = api.getList(createListRequest(), res);
357
+ const findError: Error = new Error("findBy failed");
358
+ const rejection: Promise<void> = expect(pending).rejects.toBe(findError);
359
+
360
+ await flushMicrotasks();
361
+ findDeferred.reject(findError);
362
+ countDeferred.resolve(new PositiveNumber(0));
363
+
364
+ await rejection;
365
+ expect(Response.sendEntityArrayResponse).not.toHaveBeenCalled();
366
+ });
367
+
368
+ it("rejects with countBy's error when countBy fails", async () => {
369
+ const service: ServiceStub = createServiceStub();
370
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
371
+ service,
372
+ );
373
+
374
+ const findDeferred: Deferred<Array<BaseModel>> =
375
+ createDeferred<Array<BaseModel>>();
376
+ const countDeferred: Deferred<PositiveNumber> =
377
+ createDeferred<PositiveNumber>();
378
+
379
+ service.findBy.mockReturnValue(findDeferred.promise);
380
+ service.countBy.mockReturnValue(countDeferred.promise);
381
+
382
+ const pending: Promise<void> = api.getList(createListRequest(), res);
383
+ const countError: Error = new Error("countBy failed");
384
+ const rejection: Promise<void> = expect(pending).rejects.toBe(countError);
385
+
386
+ await flushMicrotasks();
387
+ countDeferred.reject(countError);
388
+ findDeferred.resolve([]);
389
+
390
+ await rejection;
391
+ expect(Response.sendEntityArrayResponse).not.toHaveBeenCalled();
392
+ });
393
+
394
+ it("keeps findBy's error priority when both calls fail, even if countBy fails first", async () => {
395
+ const service: ServiceStub = createServiceStub();
396
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
397
+ service,
398
+ );
399
+
400
+ const findDeferred: Deferred<Array<BaseModel>> =
401
+ createDeferred<Array<BaseModel>>();
402
+ const countDeferred: Deferred<PositiveNumber> =
403
+ createDeferred<PositiveNumber>();
404
+
405
+ service.findBy.mockReturnValue(findDeferred.promise);
406
+ service.countBy.mockReturnValue(countDeferred.promise);
407
+
408
+ const pending: Promise<void> = api.getList(createListRequest(), res);
409
+ const findError: Error = new Error("findBy failed");
410
+ const countError: Error = new Error("countBy failed");
411
+
412
+ /*
413
+ * Sequentially, a double failure always surfaced findBy's error
414
+ * (countBy never even ran). The parallel path must keep that outcome
415
+ * regardless of which call happens to fail first.
416
+ */
417
+ const rejection: Promise<void> = expect(pending).rejects.toBe(findError);
418
+
419
+ await flushMicrotasks();
420
+ countDeferred.reject(countError);
421
+ await flushMicrotasks();
422
+ findDeferred.reject(findError);
423
+
424
+ await rejection;
425
+ });
426
+ });
427
+
428
+ describe("service with an overridden onBeforeFind hook (sequential fallback)", () => {
429
+ /*
430
+ * Some services (ProjectService, the incident/alert privacy filters)
431
+ * mutate the shared query/props objects in place inside onBeforeFind, and
432
+ * the count has always inherited those mutations by running afterwards.
433
+ * For such services the pre-change sequential order — and the shared
434
+ * object identity that carries the mutations — must be preserved.
435
+ */
436
+ it("does not start countBy until findBy resolves, and shares the query/props objects between the calls", async () => {
437
+ const service: ServiceStub = createServiceStub();
438
+ service.onBeforeFind = jest.fn() as unknown as (
439
+ ...args: Array<unknown>
440
+ ) => Promise<unknown>;
441
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
442
+ service,
443
+ );
444
+
445
+ const findDeferred: Deferred<Array<BaseModel>> =
446
+ createDeferred<Array<BaseModel>>();
447
+ const countDeferred: Deferred<PositiveNumber> =
448
+ createDeferred<PositiveNumber>();
449
+
450
+ service.findBy.mockReturnValue(findDeferred.promise);
451
+ service.countBy.mockReturnValue(countDeferred.promise);
452
+
453
+ const request: OneUptimeRequest = createListRequest({
454
+ query: { name: "zeta" },
455
+ select: { name: true },
456
+ sort: {},
457
+ });
458
+ const pending: Promise<void> = api.getList(request, res);
459
+
460
+ await flushMicrotasks();
461
+
462
+ expect(service.findBy).toHaveBeenCalledTimes(1);
463
+ expect(service.countBy).not.toHaveBeenCalled();
464
+
465
+ const list: Array<BaseModel> = [] as Array<BaseModel>;
466
+ findDeferred.resolve(list);
467
+ await flushMicrotasks();
468
+
469
+ expect(service.countBy).toHaveBeenCalledTimes(1);
470
+
471
+ // eslint-disable-next-line @typescript-eslint/typedef
472
+ const findArgs = service.findBy.mock.calls[0]?.[0] as {
473
+ query: JSONObject;
474
+ props: JSONObject;
475
+ };
476
+ // eslint-disable-next-line @typescript-eslint/typedef
477
+ const countArgs = service.countBy.mock.calls[0]?.[0] as {
478
+ query: JSONObject;
479
+ props: JSONObject;
480
+ };
481
+
482
+ // Identity, not just equality: in-place hook mutations must carry over.
483
+ expect(countArgs.query).toBe(findArgs.query);
484
+ expect(countArgs.props).toBe(findArgs.props);
485
+
486
+ const count: PositiveNumber = new PositiveNumber(0);
487
+ countDeferred.resolve(count);
488
+ await pending;
489
+
490
+ expect(Response.sendEntityArrayResponse).toHaveBeenCalledWith(
491
+ request,
492
+ res,
493
+ list,
494
+ count,
495
+ BaseModel,
496
+ );
497
+ });
498
+
499
+ it("never starts countBy when findBy fails", async () => {
500
+ const service: ServiceStub = createServiceStub();
501
+ service.onBeforeFind = jest.fn() as unknown as (
502
+ ...args: Array<unknown>
503
+ ) => Promise<unknown>;
504
+ const api: BaseAPI<BaseModel, DatabaseService<BaseModel>> = createApi(
505
+ service,
506
+ );
507
+
508
+ const findError: Error = new Error("findBy failed");
509
+ service.findBy.mockRejectedValue(findError);
510
+ service.countBy.mockResolvedValue(new PositiveNumber(0));
511
+
512
+ await expect(api.getList(createListRequest(), res)).rejects.toBe(
513
+ findError,
514
+ );
515
+ expect(service.countBy).not.toHaveBeenCalled();
516
+ expect(Response.sendEntityArrayResponse).not.toHaveBeenCalled();
517
+ });
518
+ });
519
+ });