@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
@@ -1085,6 +1085,24 @@ enum Permission {
1085
1085
  ReadProjectUserNotificationRule = "ReadProjectUserNotificationRule",
1086
1086
  EditProjectUserNotificationRule = "EditProjectUserNotificationRule",
1087
1087
 
1088
+ /*
1089
+ * Adding and removing the notification METHODS a member's rules point at:
1090
+ * the email address, phone number or device a page is actually delivered to.
1091
+ *
1092
+ * Separate from EditProjectUserNotificationRule, and deliberately not implied
1093
+ * by it. Repairing somebody's rules re-points their pages between devices
1094
+ * they have already proved they hold; adding a method introduces a device
1095
+ * nobody has proved anything about, so the two have different blast radii and
1096
+ * an installation must be able to hand out the first without the second.
1097
+ *
1098
+ * What it does NOT grant, and cannot: making a method live. A method added
1099
+ * this way lands unverified and the verification code goes to the address
1100
+ * itself, and every verify endpoint refuses a caller who is not the row's
1101
+ * owner. So this permission lets an administrator do the typing for a
1102
+ * colleague; only that colleague can finish it.
1103
+ */
1104
+ ManageProjectUserNotificationMethod = "ManageProjectUserNotificationMethod",
1105
+
1088
1106
  // Resource Permissions (Team Permission)
1089
1107
  CreateProjectOnCallDutyPolicySchedule = "CreateProjectOnCallDutyPolicySchedule",
1090
1108
  EditProjectOnCallDutyPolicySchedule = "EditProjectOnCallDutyPolicySchedule",
@@ -6068,6 +6086,23 @@ export class PermissionHelper {
6068
6086
  isRolePermission: false,
6069
6087
  group: PermissionGroup.OnCallDutyPolicy,
6070
6088
  },
6089
+ /*
6090
+ * isRolePermission false for the same reason as the two above: a true
6091
+ * value would file it under getRolePermissionProps(), the list every
6092
+ * default project role is built from, and the ability to put a new phone
6093
+ * number on a colleague's account would then arrive silently with an
6094
+ * existing role rather than being handed out on purpose.
6095
+ */
6096
+ {
6097
+ permission: Permission.ManageProjectUserNotificationMethod,
6098
+ title: "Manage User Notification Methods",
6099
+ description:
6100
+ "This permission can add and remove the notification methods (email, SMS, call, WhatsApp) of any user in this project, so an administrator can set up a member who cannot yet be paged at all. Addresses are only ever shown masked, an added method stays unverified until the person who owns the device verifies it, and that person is emailed about every change.",
6101
+ isAssignableToTenant: true,
6102
+ isAccessControlPermission: false,
6103
+ isRolePermission: false,
6104
+ group: PermissionGroup.OnCallDutyPolicy,
6105
+ },
6071
6106
 
6072
6107
  {
6073
6108
  permission: Permission.CreateProjectOnCallDutyPolicySchedule,
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Android delivers a notification with the settings of the channel it names,
3
+ * not the settings in the payload - importance, sound and Do Not Disturb
4
+ * bypass are all fixed when the app creates the channel and cannot be raised
5
+ * afterwards. So "ring through silent mode" is expressed by picking a channel,
6
+ * and these ids are the contract for that choice.
7
+ *
8
+ * The app creates channels with exactly these ids in
9
+ * MobileApp/src/notifications/channels.ts. An id sent from here that the app
10
+ * never created does not fail loudly: Android falls back to a default channel
11
+ * and the page arrives with default settings, which for an on-call page means
12
+ * silently. Keep the two lists in step.
13
+ */
14
+ enum AndroidNotificationChannel {
15
+ /*
16
+ * Do Not Disturb bypass, alarm audio stream, MAX importance. Reserved for
17
+ * on-call pages from a device whose owner opted in and granted the app Do
18
+ * Not Disturb access.
19
+ */
20
+ Critical = "oncall_critical",
21
+ High = "oncall_high",
22
+ Normal = "oncall_normal",
23
+ Low = "oncall_low",
24
+ }
25
+
26
+ export default AndroidNotificationChannel;
@@ -13,6 +13,21 @@ interface PushNotificationMessage {
13
13
  }>;
14
14
  clickAction?: string;
15
15
  url?: string;
16
+ /*
17
+ * Ask the handset to ring even when it is silenced or in Do Not Disturb.
18
+ * Reserved for on-call pages: a responder who has opted their device in has
19
+ * decided that being woken is the point. Everything else - owner
20
+ * subscriptions, note-posted, test pings - leaves this unset and respects
21
+ * the ringer switch like a normal notification.
22
+ *
23
+ * Honouring it is a three-way handshake, and all three have to agree or the
24
+ * page arrives silently:
25
+ * - the responder opts THIS device in (UserPush.isCriticalAlertEnabled),
26
+ * - the server stamps the flag onto the payload (PushNotificationService),
27
+ * - the OS lets it through (iOS critical-alert entitlement, or an Android
28
+ * channel granted Do Not Disturb access).
29
+ */
30
+ isCriticalAlert?: boolean;
16
31
  }
17
32
 
18
33
  export default PushNotificationMessage;
@@ -1,26 +1,18 @@
1
1
  import PushDeviceType from "./PushDeviceType";
2
+ import PushNotificationMessage from "./PushNotificationMessage";
2
3
 
3
4
  interface PushNotificationRequest {
4
5
  devices: Array<{
5
6
  token: string;
6
7
  name?: string;
7
8
  }>;
8
- message: {
9
- title: string;
10
- body: string;
11
- icon?: string;
12
- badge?: string;
13
- data?: { [key: string]: any };
14
- tag?: string;
15
- requireInteraction?: boolean;
16
- actions?: Array<{
17
- action: string;
18
- title: string;
19
- icon?: string;
20
- }>;
21
- clickAction?: string;
22
- url?: string;
23
- };
9
+ /*
10
+ * The same shape PushNotificationUtil builds, referenced rather than
11
+ * restated: this used to be a copy of every field in PushNotificationMessage,
12
+ * and a copy is a place for a newly added field to go missing on the way to
13
+ * the sender.
14
+ */
15
+ message: PushNotificationMessage;
24
16
  deviceType: PushDeviceType;
25
17
  }
26
18
 
@@ -0,0 +1,144 @@
1
+ import TechStack from "./TechStack";
2
+
3
+ /*
4
+ * Which runtime a telemetry service is written in, and how to work that out
5
+ * from what the service actually reported.
6
+ *
7
+ * This started life inside the dashboard's service-overview charts
8
+ * (`serviceGoldenMetrics.ts`), where it picked which runtime charts to draw.
9
+ * It lives in Common now because a second consumer needs the same answer from
10
+ * a place the dashboard cannot reach: the monitor recommendation catalog. The
11
+ * recommendations a Java service should get (JVM heap pressure, GC pause
12
+ * time, thread exhaustion) have nothing in common with the ones a Go service
13
+ * should get (goroutine leaks, GC cycles), and the catalog runs in Common.
14
+ *
15
+ * `serviceGoldenMetrics.ts` re-exports everything here, so the dashboard's
16
+ * existing import paths are unchanged.
17
+ */
18
+
19
+ // Canonical values of the telemetry.sdk.language resource attribute.
20
+ export type ServiceLanguage =
21
+ | "java"
22
+ | "dotnet"
23
+ | "nodejs"
24
+ | "python"
25
+ | "go"
26
+ | "ruby"
27
+ | "php"
28
+ | "rust"
29
+ | "erlang"
30
+ | "swift"
31
+ | "cpp"
32
+ | "webjs";
33
+
34
+ export const SERVICE_LANGUAGE_DISPLAY_NAMES: Record<ServiceLanguage, string> = {
35
+ java: "Java",
36
+ dotnet: ".NET",
37
+ nodejs: "Node.js",
38
+ python: "Python",
39
+ go: "Go",
40
+ ruby: "Ruby",
41
+ php: "PHP",
42
+ rust: "Rust",
43
+ erlang: "Erlang / Elixir",
44
+ swift: "Swift",
45
+ cpp: "C++",
46
+ webjs: "Browser JS",
47
+ };
48
+
49
+ const KNOWN_LANGUAGES: Array<ServiceLanguage> = Object.keys(
50
+ SERVICE_LANGUAGE_DISPLAY_NAMES,
51
+ ) as Array<ServiceLanguage>;
52
+
53
+ /*
54
+ * process.runtime.name values seen in the wild → language. Checked as
55
+ * case-insensitive substrings, so "OpenJDK Runtime Environment" → java.
56
+ * Ordered: more specific markers first ("graalvm" before "go").
57
+ */
58
+ const RUNTIME_NAME_MARKERS: Array<{ marker: string; lang: ServiceLanguage }> = [
59
+ { marker: "openjdk", lang: "java" },
60
+ { marker: "graalvm", lang: "java" },
61
+ { marker: "java", lang: "java" },
62
+ { marker: "dotnet", lang: "dotnet" },
63
+ { marker: ".net", lang: "dotnet" },
64
+ { marker: "node", lang: "nodejs" },
65
+ { marker: "deno", lang: "nodejs" },
66
+ { marker: "bun", lang: "nodejs" },
67
+ { marker: "cpython", lang: "python" },
68
+ { marker: "pypy", lang: "python" },
69
+ { marker: "python", lang: "python" },
70
+ { marker: "beam", lang: "erlang" },
71
+ { marker: "erlang", lang: "erlang" },
72
+ { marker: "ruby", lang: "ruby" },
73
+ { marker: "php", lang: "php" },
74
+ { marker: "rust", lang: "rust" },
75
+ { marker: "swift", lang: "swift" },
76
+ { marker: "go", lang: "go" },
77
+ ];
78
+
79
+ // Manual tech-stack selections → language (JVM languages map to java).
80
+ const TECH_STACK_LANGUAGES: Partial<Record<TechStack, ServiceLanguage>> = {
81
+ [TechStack.Java]: "java",
82
+ [TechStack.Kotlin]: "java",
83
+ [TechStack.CSharp]: "dotnet",
84
+ [TechStack.NodeJS]: "nodejs",
85
+ [TechStack.TypeScript]: "nodejs",
86
+ [TechStack.JavaScript]: "nodejs",
87
+ [TechStack.React]: "webjs",
88
+ [TechStack.Python]: "python",
89
+ [TechStack.Go]: "go",
90
+ [TechStack.Ruby]: "ruby",
91
+ [TechStack.PHP]: "php",
92
+ [TechStack.Rust]: "rust",
93
+ [TechStack.Swift]: "swift",
94
+ [TechStack.CPlusPlus]: "cpp",
95
+ };
96
+
97
+ /*
98
+ * Best-effort language for a service, in descending order of trust:
99
+ *
100
+ * 1. `telemetry.sdk.language` — stamped by the SDK itself, so it is what the
101
+ * process actually runs on rather than what anyone believes it runs on.
102
+ * 2. `process.runtime.name` — present when runtime instrumentation is on but
103
+ * the SDK attribute did not survive a collector hop.
104
+ * 3. The manually-chosen tech stack — a human's answer, used only when the
105
+ * telemetry has not answered.
106
+ *
107
+ * Returns null when nothing identifies the runtime. Callers must treat that as
108
+ * "unknown", never as a default language: recommending JVM heap monitors to a
109
+ * service that turns out to be Go is worse than recommending nothing.
110
+ */
111
+ export const detectServiceLanguage: (data: {
112
+ telemetrySdkLanguage?: string | undefined;
113
+ runtimeName?: string | undefined;
114
+ techStack?: Array<TechStack> | undefined;
115
+ }) => ServiceLanguage | null = (data: {
116
+ telemetrySdkLanguage?: string | undefined;
117
+ runtimeName?: string | undefined;
118
+ techStack?: Array<TechStack> | undefined;
119
+ }): ServiceLanguage | null => {
120
+ const sdkLanguage: string = (data.telemetrySdkLanguage || "")
121
+ .trim()
122
+ .toLowerCase();
123
+ if (sdkLanguage && KNOWN_LANGUAGES.includes(sdkLanguage as ServiceLanguage)) {
124
+ return sdkLanguage as ServiceLanguage;
125
+ }
126
+
127
+ const runtimeName: string = (data.runtimeName || "").trim().toLowerCase();
128
+ if (runtimeName) {
129
+ for (const entry of RUNTIME_NAME_MARKERS) {
130
+ if (runtimeName.includes(entry.marker)) {
131
+ return entry.lang;
132
+ }
133
+ }
134
+ }
135
+
136
+ for (const stack of data.techStack || []) {
137
+ const lang: ServiceLanguage | undefined = TECH_STACK_LANGUAGES[stack];
138
+ if (lang) {
139
+ return lang;
140
+ }
141
+ }
142
+
143
+ return null;
144
+ };
package/Types/Text.ts CHANGED
@@ -236,21 +236,117 @@ export default class Text {
236
236
  return null;
237
237
  }
238
238
 
239
- public static generateRandomText(length?: number): string {
240
- if (!length) {
241
- length = 10;
239
+ /*
240
+ * Cryptographically secure random bytes, or null if this runtime has no
241
+ * CSPRNG at all.
242
+ *
243
+ * `globalThis.crypto` is the Web Crypto API: present in every browser, and a
244
+ * global in Node since 19. It is used here rather than `require("crypto")`
245
+ * because this file is bundled into the dashboard as well as the server, so
246
+ * it cannot reach for a Node built-in.
247
+ */
248
+ private static getSecureRandomBytes(byteCount: number): Uint8Array | null {
249
+ const webCrypto: { getRandomValues?: (array: Uint8Array) => Uint8Array } =
250
+ (
251
+ globalThis as unknown as {
252
+ crypto?: { getRandomValues?: (array: Uint8Array) => Uint8Array };
253
+ }
254
+ ).crypto || {};
255
+
256
+ if (typeof webCrypto.getRandomValues !== "function") {
257
+ return null;
242
258
  }
243
259
 
244
- let result: string = "";
245
- const characters: string =
246
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
260
+ return webCrypto.getRandomValues(new Uint8Array(byteCount));
261
+ }
262
+
263
+ /*
264
+ * Draw `length` characters uniformly at random from `characters`.
265
+ *
266
+ * TWO properties here are load-bearing, and this used to have neither:
267
+ *
268
+ * 1. The source is a CSPRNG. Math.random is a fast non-cryptographic PRNG
269
+ * (xorshift128+ in V8) whose entire internal state can be recovered from
270
+ * a handful of observed outputs and then run forwards AND backwards. For
271
+ * a value used as a secret that is worse than a short code: an attacker
272
+ * who sees one generated value predicts the next one exactly, with no
273
+ * guessing at all.
274
+ *
275
+ * 2. The draw is unbiased. `byte % n` is only uniform when n divides 256.
276
+ * Rejection sampling discards the short tail of the byte range instead of
277
+ * folding it, which is what keeps a k-character code at its full
278
+ * k * log2(n) bits of entropy.
279
+ *
280
+ * The Math.random fallback survives ONLY for the case where no CSPRNG exists
281
+ * at all, which is unreachable on any supported runtime. It is there so the
282
+ * non-secret callers - SQL alias suffixes in QueryHelper and friends, which
283
+ * run on every query - cannot be broken by an exotic environment. Secrets
284
+ * must NOT come through here: notification-channel codes go through
285
+ * Common/Server/Utils/VerificationCode.ts, which throws rather than
286
+ * downgrading.
287
+ */
288
+ private static generateRandomString(
289
+ length: number,
290
+ characters: string,
291
+ ): string {
247
292
  const charactersLength: number = characters.length;
248
- for (let i: number = 0; i < length; i++) {
249
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
293
+
294
+ /*
295
+ * The largest multiple of charactersLength that fits in a byte. Bytes at
296
+ * or above it are thrown away rather than folded - that is the whole of
297
+ * the rejection sampling, and skipping it is what biases the result.
298
+ */
299
+ const unbiasedCeiling: number = 256 - (256 % charactersLength);
300
+
301
+ let result: string = "";
302
+
303
+ while (result.length < length) {
304
+ const remaining: number = length - result.length;
305
+
306
+ /*
307
+ * Over-draw a little so a rejected byte usually does not cost a second
308
+ * trip into the CSPRNG. With a 10-character alphabet fewer than 3% of
309
+ * bytes are rejected, so one pass almost always finishes the job.
310
+ */
311
+ const bytes: Uint8Array | null = Text.getSecureRandomBytes(
312
+ remaining + Math.ceil(remaining / 4) + 4,
313
+ );
314
+
315
+ if (!bytes) {
316
+ while (result.length < length) {
317
+ result += characters.charAt(
318
+ Math.floor(Math.random() * charactersLength),
319
+ );
320
+ }
321
+
322
+ return result;
323
+ }
324
+
325
+ for (let i: number = 0; i < bytes.length; i++) {
326
+ if (result.length >= length) {
327
+ break;
328
+ }
329
+
330
+ const byte: number | undefined = bytes[i];
331
+
332
+ if (byte === undefined || byte >= unbiasedCeiling) {
333
+ continue;
334
+ }
335
+
336
+ result += characters.charAt(byte % charactersLength);
337
+ }
250
338
  }
339
+
251
340
  return result;
252
341
  }
253
342
 
343
+ public static generateRandomText(length?: number): string {
344
+ return Text.generateRandomString(
345
+ length || 10,
346
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
347
+ );
348
+ }
349
+
254
350
  public static trimLines(text: string): string {
255
351
  return text
256
352
  .split("\n")
@@ -260,18 +356,14 @@ export default class Text {
260
356
  .join("\n");
261
357
  }
262
358
 
359
+ /*
360
+ * The alphabet used to be the string "12134567890" - eleven characters, with
361
+ * "1" written twice and every digit therefore NOT equally likely. Combined
362
+ * with Math.random that made a six-digit code both biased and predictable.
363
+ * Both halves are fixed in generateRandomString above.
364
+ */
263
365
  public static generateRandomNumber(length?: number): string {
264
- if (!length) {
265
- length = 10;
266
- }
267
-
268
- let result: string = "";
269
- const characters: string = "12134567890";
270
- const charactersLength: number = characters.length;
271
- for (let i: number = 0; i < length; i++) {
272
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
273
- }
274
- return result;
366
+ return Text.generateRandomString(length || 10, "0123456789");
275
367
  }
276
368
 
277
369
  public static convertNumberToWords(num: number): string {
@@ -91,22 +91,28 @@ const Button: FunctionComponent<ComponentProps> = ({
91
91
  const translatedTitle: string | undefined = translateString(title);
92
92
  const translatedTooltip: string | undefined = translateString(tooltip);
93
93
  useEffect(() => {
94
- // componentDidMount
95
- if (shortcutKey) {
96
- window.addEventListener(`keydown`, (e: KeyboardEventProp) => {
97
- return handleKeyboard(e);
98
- });
94
+ if (!shortcutKey) {
95
+ return undefined;
99
96
  }
100
97
 
101
- // componentDidUnmount
98
+ /*
99
+ * One stable function for the whole subscription. The previous version
100
+ * passed a fresh arrow function to removeEventListener, which never
101
+ * matches the one that was added — every render leaked a live keydown
102
+ * listener for the lifetime of the page.
103
+ */
104
+ const onKeyDown: (event: KeyboardEventProp) => void = (
105
+ event: KeyboardEventProp,
106
+ ): void => {
107
+ return handleKeyboard(event);
108
+ };
109
+
110
+ window.addEventListener(`keydown`, onKeyDown);
111
+
102
112
  return () => {
103
- if (shortcutKey) {
104
- window.removeEventListener(`keydown`, (e: KeyboardEventProp) => {
105
- return handleKeyboard(e);
106
- });
107
- }
113
+ window.removeEventListener(`keydown`, onKeyDown);
108
114
  };
109
- });
115
+ }, [shortcutKey, onClick]);
110
116
 
111
117
  type HandleKeyboardFunction = (event: KeyboardEventProp) => void;
112
118
 
@@ -141,7 +147,7 @@ const Button: FunctionComponent<ComponentProps> = ({
141
147
  }
142
148
 
143
149
  if (buttonStyle === ButtonStyleType.LINK) {
144
- buttonStyleCssClass = `text-indigo-600 hover:text-indigo-900 space-x-2`;
150
+ buttonStyleCssClass = `text-indigo-600 hover:text-indigo-900 space-x-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2`;
145
151
 
146
152
  if (icon) {
147
153
  buttonStyleCssClass += ` flex`;
@@ -149,7 +155,7 @@ const Button: FunctionComponent<ComponentProps> = ({
149
155
  }
150
156
 
151
157
  if (buttonStyle === ButtonStyleType.SECONDARY_LINK) {
152
- buttonStyleCssClass = `text-sm text-gray-400 hover:text-gray-500 space-x-2`;
158
+ buttonStyleCssClass = `text-sm text-gray-400 hover:text-gray-500 space-x-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2`;
153
159
 
154
160
  if (icon) {
155
161
  buttonStyleCssClass += ` flex`;
@@ -239,6 +245,13 @@ const Button: FunctionComponent<ComponentProps> = ({
239
245
  } focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2 md:mt-0 md:ml-3 md:w-auto md:text-sm`;
240
246
  }
241
247
 
248
+ /*
249
+ * The one shared point every variant funnels through: hover/focus colour
250
+ * changes ease in instead of snapping, at the same 150ms the other
251
+ * primitives use.
252
+ */
253
+ buttonStyleCssClass += ` transition-colors duration-150 ease-out`;
254
+
242
255
  buttonStyleCssClass += ` ` + buttonSize;
243
256
 
244
257
  if (className) {
@@ -1,6 +1,16 @@
1
1
  import "highlight.js/styles/a11y-dark.css";
2
- import React, { FunctionComponent, ReactElement, useState } from "react";
3
- import Highlight from "react-highlight";
2
+ import React, {
3
+ FunctionComponent,
4
+ ReactElement,
5
+ useMemo,
6
+ useState,
7
+ } from "react";
8
+ /*
9
+ * Trimmed highlight.js instance — lib/core plus only the languages CodeBlock
10
+ * callers actually pass (see the audit in LanguageRegistry.ts). Replaces
11
+ * react-highlight, which imported all 194 grammars (~1.5MB) eagerly.
12
+ */
13
+ import hljs, { resolveRegisteredLanguage } from "./LanguageRegistry";
4
14
  import Icon from "../Icon/Icon";
5
15
  import IconProp from "../../../Types/Icon/IconProp";
6
16
 
@@ -31,6 +41,38 @@ const CodeBlock: FunctionComponent<ComponentProps> = (
31
41
  const maxHeight: string = props.maxHeight || "500px";
32
42
  const showCopyButton: boolean = props.showCopyButton !== false;
33
43
 
44
+ /*
45
+ * react-highlight rendered <pre><code className>…</code></pre> and then ran
46
+ * hljs.highlightBlock over the DOM after mount (reading the language from
47
+ * the `language-*` class and adding the theme's `hljs` class). We keep that
48
+ * exact DOM shape but highlight synchronously: registered language → hljs
49
+ * token markup (hljs.highlight escapes the source before wrapping it in
50
+ * spans, so the HTML is safe to inject); unregistered language or a
51
+ * ReactElement child → plain React-escaped rendering. hljs is never called
52
+ * with an unknown language, so it never logs lookup warnings.
53
+ */
54
+ const registeredLanguage: string | null =
55
+ typeof props.code === "string"
56
+ ? resolveRegisteredLanguage(props.language)
57
+ : null;
58
+
59
+ const highlightedHtml: string | null = useMemo((): string | null => {
60
+ if (typeof props.code !== "string" || !registeredLanguage) {
61
+ return null;
62
+ }
63
+
64
+ return hljs.highlight(props.code, {
65
+ language: registeredLanguage,
66
+ ignoreIllegals: true,
67
+ }).value;
68
+ }, [props.code, registeredLanguage]);
69
+
70
+ /*
71
+ * `hljs` carries the a11y-dark theme background; react-highlight added it
72
+ * post-mount, we add it up front so there is no unstyled first paint.
73
+ */
74
+ const codeClassName: string = `hljs p-4 language-${props.language} text-sm leading-relaxed`;
75
+
34
76
  return (
35
77
  <div className="relative group">
36
78
  {showCopyButton && typeof props.code === "string" && (
@@ -51,11 +93,16 @@ const CodeBlock: FunctionComponent<ComponentProps> = (
51
93
  className="overflow-auto rounded-lg border border-gray-700"
52
94
  style={{ maxHeight: maxHeight }}
53
95
  >
54
- <Highlight
55
- className={`p-4 language-${props.language} text-sm leading-relaxed`}
56
- >
57
- {props.code}
58
- </Highlight>
96
+ <pre>
97
+ {highlightedHtml !== null ? (
98
+ <code
99
+ className={codeClassName}
100
+ dangerouslySetInnerHTML={{ __html: highlightedHtml }}
101
+ />
102
+ ) : (
103
+ <code className={codeClassName}>{props.code}</code>
104
+ )}
105
+ </pre>
59
106
  </div>
60
107
  </div>
61
108
  );