@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,427 @@
1
+ import UserTotpAuthAPI from "../../../Server/API/UserTotpAuthAPI";
2
+ import UserTotpAuthService from "../../../Server/Services/UserTotpAuthService";
3
+ import UserService from "../../../Server/Services/UserService";
4
+ import TotpAuth from "../../../Server/Utils/TotpAuth";
5
+ import {
6
+ NextFunction,
7
+ OneUptimeRequest,
8
+ OneUptimeResponse,
9
+ } from "../../../Server/Utils/Express";
10
+ import Response from "../../../Server/Utils/Response";
11
+ import { mockRouter } from "./Helpers";
12
+ import Email from "../../../Types/Email";
13
+ import BadDataException from "../../../Types/Exception/BadDataException";
14
+ import Exception from "../../../Types/Exception/Exception";
15
+ import ObjectID from "../../../Types/ObjectID";
16
+ import UserTotpAuth from "../../../Models/DatabaseModels/UserTotpAuth";
17
+ import User from "../../../Models/DatabaseModels/User";
18
+ import * as OTPAuth from "otpauth";
19
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
20
+
21
+ /*
22
+ * POST /user-totp-auth/validate — the endpoint that turns a scanned QR code
23
+ * into a working second factor.
24
+ *
25
+ * TotpAuth itself is deliberately NOT mocked here. The bug this endpoint
26
+ * failed on (issue #3275) lived precisely in the seam between what the QR code
27
+ * told the authenticator app and what the server recomputed, so a test that
28
+ * stubs the verifier proves nothing about it. The secret is generated by the
29
+ * real service path, the code is produced by parsing the stored otpauth:// URI
30
+ * exactly as an authenticator app would, and the endpoint gets the real thing.
31
+ *
32
+ * The second concern is ownership. `id` comes from the request body, so
33
+ * without the userId comparison any signed-in user could mark a stranger's
34
+ * pending TOTP row as verified using a code from their own phone — a second
35
+ * factor the victim does not hold. That check is asserted directly, and the
36
+ * assertion that matters is that isVerified is never written.
37
+ */
38
+
39
+ jest.mock("../../../Server/Utils/Express", () => {
40
+ return {
41
+ getRouter: () => {
42
+ return mockRouter;
43
+ },
44
+ };
45
+ });
46
+
47
+ jest.mock("../../../Server/Utils/Response", () => {
48
+ return {
49
+ sendEntityArrayResponse: jest.fn(),
50
+ sendJsonArrayResponse: jest.fn(),
51
+ sendJsonObjectResponse: jest.fn(),
52
+ sendEmptySuccessResponse: jest.fn(),
53
+ sendEntityResponse: jest.fn(),
54
+ sendErrorResponse: jest.fn(),
55
+ setNoCacheHeaders: jest.fn(),
56
+ };
57
+ });
58
+
59
+ jest.mock("../../../Server/Services/UserTotpAuthService");
60
+ jest.mock("../../../Server/Services/UserService");
61
+
62
+ const VALIDATE_ROUTE: string = "/user-totp-auth/validate";
63
+
64
+ const TOTP_ID: ObjectID = new ObjectID("11111111-1111-4111-8111-111111111111");
65
+ const OWNER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
66
+ const ATTACKER_ID: ObjectID = new ObjectID(
67
+ "33333333-3333-4333-8333-333333333333",
68
+ );
69
+
70
+ const OWNER_EMAIL: Email = new Email("owner@example.com");
71
+
72
+ type CallValidateResult = {
73
+ thrown: unknown;
74
+ nextCallCount: number;
75
+ };
76
+
77
+ type CallValidateFunction = (data: {
78
+ body: Record<string, unknown>;
79
+ signedInUserId?: ObjectID | undefined;
80
+ }) => Promise<CallValidateResult>;
81
+
82
+ const callValidate: CallValidateFunction = async (data: {
83
+ body: Record<string, unknown>;
84
+ signedInUserId?: ObjectID | undefined;
85
+ }): Promise<CallValidateResult> => {
86
+ const req: OneUptimeRequest = {
87
+ body: data.body,
88
+ params: {},
89
+ query: {},
90
+ headers: {},
91
+ userAuthorization: {
92
+ userId: data.signedInUserId || OWNER_ID,
93
+ },
94
+ } as unknown as OneUptimeRequest;
95
+
96
+ const res: OneUptimeResponse = {
97
+ send: jest.fn(),
98
+ json: jest.fn(),
99
+ setHeader: jest.fn(),
100
+ status: jest.fn().mockReturnThis(),
101
+ } as unknown as OneUptimeResponse;
102
+
103
+ const next: NextFunction = jest.fn() as unknown as NextFunction;
104
+
105
+ await mockRouter
106
+ .match("post", VALIDATE_ROUTE)
107
+ .handlerFunction(req, res, next);
108
+
109
+ const nextMock: jest.Mock = next as unknown as jest.Mock;
110
+
111
+ return {
112
+ thrown: nextMock.mock.calls[0]?.[0],
113
+ nextCallCount: nextMock.mock.calls.length,
114
+ };
115
+ };
116
+
117
+ /*
118
+ * A pending enrolment, built the way the service builds one: a real secret and
119
+ * the real otpauth:// URI derived from it.
120
+ */
121
+ type PendingEnrolmentFunction = (options?: { ownerId?: ObjectID }) => {
122
+ row: UserTotpAuth;
123
+ secret: string;
124
+ codeFromTheAuthenticatorApp: () => string;
125
+ };
126
+
127
+ const pendingEnrolment: PendingEnrolmentFunction = (options?: {
128
+ ownerId?: ObjectID;
129
+ }) => {
130
+ const secret: string = TotpAuth.generateSecret();
131
+
132
+ const otpUrl: string = TotpAuth.generateUri({
133
+ secret: secret,
134
+ userEmail: OWNER_EMAIL,
135
+ });
136
+
137
+ const row: UserTotpAuth = new UserTotpAuth();
138
+ row.id = TOTP_ID;
139
+ row.userId = options?.ownerId || OWNER_ID;
140
+ row.twoFactorSecret = secret;
141
+ row.twoFactorOtpUrl = otpUrl;
142
+ row.isVerified = false;
143
+
144
+ return {
145
+ row: row,
146
+ secret: secret,
147
+ codeFromTheAuthenticatorApp: (): string => {
148
+ return (OTPAuth.URI.parse(otpUrl) as OTPAuth.TOTP).generate();
149
+ },
150
+ };
151
+ };
152
+
153
+ let findTotpById: jest.Mock;
154
+ let updateTotpById: jest.Mock;
155
+ let findUserById: jest.Mock;
156
+
157
+ describe("POST /user-totp-auth/validate", () => {
158
+ beforeEach(() => {
159
+ mockRouter.routes.length = 0;
160
+
161
+ findTotpById = jest.fn();
162
+ updateTotpById = jest.fn(async () => {
163
+ return undefined;
164
+ });
165
+ findUserById = jest.fn();
166
+
167
+ UserTotpAuthService.findOneById = findTotpById as never;
168
+ UserTotpAuthService.updateOneById = updateTotpById as never;
169
+ UserTotpAuthService.getModel = jest
170
+ .fn()
171
+ .mockReturnValue({ tableName: "UserTotpAuth" }) as never;
172
+
173
+ UserService.findOneById = findUserById as never;
174
+
175
+ const owner: User = new User();
176
+ owner.id = OWNER_ID;
177
+ owner.email = OWNER_EMAIL;
178
+ findUserById.mockResolvedValue(owner as never);
179
+
180
+ new UserTotpAuthAPI();
181
+ });
182
+
183
+ afterEach(() => {
184
+ jest.restoreAllMocks();
185
+ });
186
+
187
+ describe("the route itself", () => {
188
+ test("is registered as a POST", () => {
189
+ expect(() => {
190
+ return mockRouter.match("post", VALIDATE_ROUTE);
191
+ }).not.toThrow();
192
+ });
193
+
194
+ /*
195
+ * Without an authentication middleware the handler would read
196
+ * `userAuthorization` off an unauthenticated request, the ownership
197
+ * comparison below would compare against `undefined`, and anybody could
198
+ * verify anybody's TOTP row.
199
+ */
200
+ test("is behind an authentication middleware", () => {
201
+ expect(mockRouter.match("post", VALIDATE_ROUTE).middlewares.length).toBe(
202
+ 1,
203
+ );
204
+ });
205
+ });
206
+
207
+ describe("when the code is the one the user's app is showing", () => {
208
+ test("marks the enrolment verified", async () => {
209
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
210
+ pendingEnrolment();
211
+
212
+ findTotpById.mockResolvedValue(enrolment.row as never);
213
+
214
+ const result: CallValidateResult = await callValidate({
215
+ body: {
216
+ id: TOTP_ID.toString(),
217
+ code: enrolment.codeFromTheAuthenticatorApp(),
218
+ },
219
+ });
220
+
221
+ expect(result.thrown).toBeUndefined();
222
+
223
+ expect(updateTotpById).toHaveBeenCalledWith(
224
+ expect.objectContaining({
225
+ id: TOTP_ID,
226
+ data: { isVerified: true },
227
+ }),
228
+ );
229
+
230
+ expect(Response.sendEmptySuccessResponse).toHaveBeenCalled();
231
+ });
232
+
233
+ /*
234
+ * Google Authenticator renders the code as "123 456" and the phone
235
+ * clipboard keeps the space. This is the same enrolment, pasted.
236
+ */
237
+ test("accepts the code as the phone's clipboard hands it over", async () => {
238
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
239
+ pendingEnrolment();
240
+
241
+ findTotpById.mockResolvedValue(enrolment.row as never);
242
+
243
+ const code: string = enrolment.codeFromTheAuthenticatorApp();
244
+
245
+ const result: CallValidateResult = await callValidate({
246
+ body: {
247
+ id: TOTP_ID.toString(),
248
+ code: `${code.slice(0, 3)} ${code.slice(3)}`,
249
+ },
250
+ });
251
+
252
+ expect(result.thrown).toBeUndefined();
253
+ expect(updateTotpById).toHaveBeenCalled();
254
+ });
255
+
256
+ test("reads the secret with root privileges, since the column is unreadable otherwise", async () => {
257
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
258
+ pendingEnrolment();
259
+
260
+ findTotpById.mockResolvedValue(enrolment.row as never);
261
+
262
+ await callValidate({
263
+ body: {
264
+ id: TOTP_ID.toString(),
265
+ code: enrolment.codeFromTheAuthenticatorApp(),
266
+ },
267
+ });
268
+
269
+ expect(findTotpById).toHaveBeenCalledWith(
270
+ expect.objectContaining({
271
+ select: expect.objectContaining({ twoFactorSecret: true }),
272
+ props: { isRoot: true },
273
+ }),
274
+ );
275
+ });
276
+ });
277
+
278
+ describe("when the code is wrong", () => {
279
+ test("fails with Invalid code and leaves the enrolment unverified", async () => {
280
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
281
+ pendingEnrolment();
282
+
283
+ findTotpById.mockResolvedValue(enrolment.row as never);
284
+
285
+ const result: CallValidateResult = await callValidate({
286
+ body: { id: TOTP_ID.toString(), code: "000000" },
287
+ });
288
+
289
+ expect(result.thrown).toBeInstanceOf(BadDataException);
290
+ expect((result.thrown as Exception).message).toBe("Invalid code");
291
+ expect(updateTotpById).not.toHaveBeenCalled();
292
+ });
293
+
294
+ test.each([
295
+ ["a missing code", undefined],
296
+ ["an empty code", ""],
297
+ ["a truncated code", "1234"],
298
+ ])(
299
+ "rejects %s without verifying anything",
300
+ async (_name: string, code: string | undefined) => {
301
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
302
+ pendingEnrolment();
303
+
304
+ findTotpById.mockResolvedValue(enrolment.row as never);
305
+
306
+ const result: CallValidateResult = await callValidate({
307
+ body: { id: TOTP_ID.toString(), code: code },
308
+ });
309
+
310
+ expect(result.thrown).toBeInstanceOf(BadDataException);
311
+ expect(updateTotpById).not.toHaveBeenCalled();
312
+ },
313
+ );
314
+
315
+ /*
316
+ * A code that is valid for a DIFFERENT enrolment. This is what an attacker
317
+ * with their own authenticator would submit.
318
+ */
319
+ test("rejects a code that is valid for somebody else's secret", async () => {
320
+ const victim: ReturnType<PendingEnrolmentFunction> = pendingEnrolment();
321
+ const attacker: ReturnType<PendingEnrolmentFunction> = pendingEnrolment();
322
+
323
+ findTotpById.mockResolvedValue(victim.row as never);
324
+
325
+ const result: CallValidateResult = await callValidate({
326
+ body: {
327
+ id: TOTP_ID.toString(),
328
+ code: attacker.codeFromTheAuthenticatorApp(),
329
+ },
330
+ });
331
+
332
+ expect(result.thrown).toBeInstanceOf(BadDataException);
333
+ expect(updateTotpById).not.toHaveBeenCalled();
334
+ });
335
+ });
336
+
337
+ describe("ownership", () => {
338
+ /*
339
+ * `id` is attacker-controlled. If the handler stopped comparing userId, a
340
+ * signed-in attacker could point this endpoint at any pending enrolment in
341
+ * the database and verify it with a code from their own phone.
342
+ */
343
+ test("refuses to verify an enrolment belonging to another user", async () => {
344
+ const enrolment: ReturnType<PendingEnrolmentFunction> = pendingEnrolment({
345
+ ownerId: OWNER_ID,
346
+ });
347
+
348
+ findTotpById.mockResolvedValue(enrolment.row as never);
349
+
350
+ const result: CallValidateResult = await callValidate({
351
+ body: {
352
+ id: TOTP_ID.toString(),
353
+ code: enrolment.codeFromTheAuthenticatorApp(),
354
+ },
355
+ signedInUserId: ATTACKER_ID,
356
+ });
357
+
358
+ expect(result.thrown).toBeInstanceOf(BadDataException);
359
+ expect(updateTotpById).not.toHaveBeenCalled();
360
+ });
361
+
362
+ test("does not reveal that the enrolment exists", async () => {
363
+ const enrolment: ReturnType<PendingEnrolmentFunction> = pendingEnrolment({
364
+ ownerId: OWNER_ID,
365
+ });
366
+
367
+ findTotpById.mockResolvedValue(enrolment.row as never);
368
+
369
+ const result: CallValidateResult = await callValidate({
370
+ body: {
371
+ id: TOTP_ID.toString(),
372
+ code: enrolment.codeFromTheAuthenticatorApp(),
373
+ },
374
+ signedInUserId: ATTACKER_ID,
375
+ });
376
+
377
+ expect((result.thrown as Exception).message).toBe(
378
+ "Two factor auth not found",
379
+ );
380
+ });
381
+ });
382
+
383
+ describe("when the record is not usable", () => {
384
+ test("fails when no such enrolment exists", async () => {
385
+ findTotpById.mockResolvedValue(null as never);
386
+
387
+ const result: CallValidateResult = await callValidate({
388
+ body: { id: TOTP_ID.toString(), code: "123456" },
389
+ });
390
+
391
+ expect(result.thrown).toBeInstanceOf(BadDataException);
392
+ expect(updateTotpById).not.toHaveBeenCalled();
393
+ });
394
+
395
+ test("fails when the owning user has no email to label the entry with", async () => {
396
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
397
+ pendingEnrolment();
398
+
399
+ findTotpById.mockResolvedValue(enrolment.row as never);
400
+
401
+ const emailless: User = new User();
402
+ emailless.id = OWNER_ID;
403
+ findUserById.mockResolvedValue(emailless as never);
404
+
405
+ const result: CallValidateResult = await callValidate({
406
+ body: {
407
+ id: TOTP_ID.toString(),
408
+ code: enrolment.codeFromTheAuthenticatorApp(),
409
+ },
410
+ });
411
+
412
+ expect(result.thrown).toBeInstanceOf(BadDataException);
413
+ expect(updateTotpById).not.toHaveBeenCalled();
414
+ });
415
+
416
+ test("surfaces the failure through next() rather than crashing the request", async () => {
417
+ findTotpById.mockRejectedValue(new Error("database is on fire") as never);
418
+
419
+ const result: CallValidateResult = await callValidate({
420
+ body: { id: TOTP_ID.toString(), code: "123456" },
421
+ });
422
+
423
+ expect(result.nextCallCount).toBe(1);
424
+ expect(result.thrown).toBeInstanceOf(Error);
425
+ });
426
+ });
427
+ });