@oneuptime/common 13.0.8 → 14.0.1

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 (514) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Index.ts +4 -0
  4. package/Models/DatabaseModels/Monitor.ts +3 -2
  5. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  6. package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
  7. package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
  8. package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
  9. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
  10. package/Scripts/generate-service-worker.js +65 -4
  11. package/Server/API/BaseAPI.ts +26 -5
  12. package/Server/API/GlobalConfigAPI.ts +228 -462
  13. package/Server/API/ProjectOIDC.ts +25 -16
  14. package/Server/API/ProjectSSO.ts +24 -16
  15. package/Server/API/StatusPageAPI.ts +100 -42
  16. package/Server/API/TeamMemberAPI.ts +10 -1
  17. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  18. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  19. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  20. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  21. package/Server/EnvironmentConfig.ts +147 -2
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  26. package/Server/Middleware/UserAuthorization.ts +38 -16
  27. package/Server/Services/AuditLogService.ts +84 -1079
  28. package/Server/Services/DatabaseService.ts +10 -0
  29. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  30. package/Server/Services/GlobalConfigService.ts +80 -1
  31. package/Server/Services/Index.ts +4 -0
  32. package/Server/Services/MonitorProbeService.ts +116 -20
  33. package/Server/Services/MonitorService.ts +59 -6
  34. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  35. package/Server/Services/MonitorTemplateService.ts +26 -0
  36. package/Server/Services/ProjectService.ts +284 -0
  37. package/Server/Services/RumSessionPinService.ts +25 -6
  38. package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
  39. package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
  40. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
  41. package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
  42. package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
  43. package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
  44. package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
  45. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  46. package/Server/Services/StatusPageService.ts +78 -1
  47. package/Server/Services/TeamMemberService.ts +11 -0
  48. package/Server/Services/TeamService.ts +10 -0
  49. package/Server/Services/UserService.ts +10 -17
  50. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  51. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  52. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  53. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  54. package/Server/Types/Markdown.ts +41 -0
  55. package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
  56. package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
  57. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  58. package/Server/Utils/EditionEnforcement.ts +233 -0
  59. package/Server/Utils/FrontendEnvironment.ts +41 -2
  60. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  61. package/Server/Utils/Monitor/AffectedResourceList.ts +148 -0
  62. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  63. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  64. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
  65. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +720 -465
  66. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  67. package/Server/Utils/Monitor/RootCauseList.ts +139 -0
  68. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
  69. package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
  70. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  71. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  72. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  73. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  74. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  75. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  76. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  77. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  78. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  79. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  80. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  81. package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
  82. package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
  83. package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
  84. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  85. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
  86. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
  87. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  88. package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
  89. package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
  90. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
  91. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
  92. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
  93. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  94. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  95. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  96. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  97. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  98. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  99. package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
  100. package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
  101. package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
  102. package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
  103. package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
  104. package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
  105. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  106. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  107. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  108. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  109. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
  110. package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
  111. package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
  112. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  113. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  114. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  115. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  116. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  117. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  118. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  119. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  120. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  121. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  122. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  123. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  124. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  125. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  126. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  127. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  128. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  129. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  130. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  131. package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
  132. package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
  133. package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
  134. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  135. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  136. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  137. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  138. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
  139. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  140. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  141. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  142. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  143. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  144. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  145. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  146. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
  147. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
  148. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  149. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
  150. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
  151. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
  152. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
  153. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
  154. package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
  155. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
  156. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  157. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  158. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
  159. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  160. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  161. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  162. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
  163. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  164. package/Tests/Server/Types/Markdown.test.ts +183 -1
  165. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
  166. package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
  167. package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
  168. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  169. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  170. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  171. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  172. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  173. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  174. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
  175. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +1339 -96
  176. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
  177. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
  178. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  179. package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
  180. package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
  181. package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
  182. package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
  183. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
  184. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  185. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  186. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  187. package/Tests/Types/API/Route.test.ts +56 -0
  188. package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
  189. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  190. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  191. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  192. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  193. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  194. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  195. package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
  196. package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
  197. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  198. package/Tests/UI/Components/Icon.test.tsx +26 -0
  199. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  200. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  201. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  202. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  203. package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
  204. package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
  205. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  206. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  207. package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
  208. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  209. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  210. package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
  211. package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
  212. package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
  213. package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
  214. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  215. package/Types/API/Hostname.ts +63 -16
  216. package/Types/API/Route.ts +19 -2
  217. package/Types/API/URL.ts +26 -2
  218. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  219. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  220. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  221. package/Types/Database/CanonicalModelInstance.ts +102 -0
  222. package/Types/Database/TableColumn.ts +20 -6
  223. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  224. package/Types/IP/IPv6.ts +6 -1
  225. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
  226. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
  227. package/Types/Monitor/MonitorStep.ts +6 -1
  228. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  229. package/Types/Permission.ts +90 -0
  230. package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
  231. package/Types/Rules/RuleRun.ts +4 -0
  232. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  233. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  234. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  235. package/UI/Components/Feed/FeedOptions.ts +427 -0
  236. package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
  237. package/UI/Components/Feed/useFeedItems.ts +28 -9
  238. package/UI/Components/Feed/useFeedOptions.ts +225 -0
  239. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  240. package/UI/Components/Icon/Icon.tsx +1 -1
  241. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  242. package/UI/Components/ModelList/ModelList.tsx +11 -1
  243. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  244. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  245. package/UI/esbuild-enterprise.js +263 -0
  246. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  247. package/Utils/HostAddressUtil.ts +94 -0
  248. package/Utils/LabelRuleImportExport.ts +27 -9
  249. package/Utils/Monitor/MetricValueFormatter.ts +2 -2
  250. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  251. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  252. package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
  253. package/Utils/Slo/SloDuration.ts +22 -6
  254. package/Utils/Slo/SloHealth.ts +8 -7
  255. package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
  256. package/Utils/Slo/SloOverviewText.ts +33 -0
  257. package/Utils/Slo/SloProjection.ts +4 -3
  258. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  259. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  260. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  261. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  262. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  263. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  264. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  265. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  266. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  267. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  268. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  269. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
  270. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
  271. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
  272. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
  273. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
  274. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
  275. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
  276. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
  277. package/build/dist/Server/API/BaseAPI.js +26 -5
  278. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  279. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  280. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  281. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  282. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  283. package/build/dist/Server/API/ProjectSSO.js +24 -16
  284. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  285. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  286. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  287. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  288. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  289. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  290. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  291. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  292. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  293. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  294. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  295. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  296. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  297. package/build/dist/Server/EnvironmentConfig.js +84 -2
  298. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  299. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  300. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  301. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
  302. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
  303. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
  304. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
  305. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  306. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  307. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  308. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  309. package/build/dist/Server/Services/AuditLogService.js +71 -740
  310. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  311. package/build/dist/Server/Services/DatabaseService.js +7 -0
  312. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  313. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  314. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  315. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  316. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  317. package/build/dist/Server/Services/Index.js +4 -0
  318. package/build/dist/Server/Services/Index.js.map +1 -1
  319. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  320. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  321. package/build/dist/Server/Services/MonitorService.js +48 -6
  322. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  323. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  324. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  325. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  326. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  327. package/build/dist/Server/Services/ProjectService.js +214 -0
  328. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  329. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  330. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  331. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
  332. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
  333. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
  334. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
  335. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
  336. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
  337. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
  338. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
  339. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
  340. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
  341. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
  342. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
  343. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
  344. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  345. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  346. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  347. package/build/dist/Server/Services/StatusPageService.js +71 -0
  348. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  349. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  350. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  351. package/build/dist/Server/Services/TeamService.js +9 -0
  352. package/build/dist/Server/Services/TeamService.js.map +1 -1
  353. package/build/dist/Server/Services/UserService.js +10 -16
  354. package/build/dist/Server/Services/UserService.js.map +1 -1
  355. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  356. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  357. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  358. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  359. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  360. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  361. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  362. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  363. package/build/dist/Server/Types/Markdown.js +31 -0
  364. package/build/dist/Server/Types/Markdown.js.map +1 -1
  365. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
  366. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
  367. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
  368. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  369. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  370. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  371. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  372. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  373. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  374. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  375. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  376. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  377. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +67 -0
  378. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  379. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  380. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  381. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  382. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +585 -367
  384. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  385. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  386. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  387. package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
  388. package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
  389. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
  390. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  391. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
  392. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
  393. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  394. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  395. package/build/dist/Types/API/Hostname.js +59 -11
  396. package/build/dist/Types/API/Hostname.js.map +1 -1
  397. package/build/dist/Types/API/Route.js +18 -2
  398. package/build/dist/Types/API/Route.js.map +1 -1
  399. package/build/dist/Types/API/URL.js +23 -2
  400. package/build/dist/Types/API/URL.js.map +1 -1
  401. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  402. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  403. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  404. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  405. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  406. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  407. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  408. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  409. package/build/dist/Types/Database/TableColumn.js +19 -6
  410. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  411. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  412. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  413. package/build/dist/Types/IP/IPv6.js +6 -1
  414. package/build/dist/Types/IP/IPv6.js.map +1 -1
  415. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  416. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  417. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  418. package/build/dist/Types/Permission.js +84 -0
  419. package/build/dist/Types/Permission.js.map +1 -1
  420. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
  421. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  422. package/build/dist/Types/Rules/RuleRun.js +4 -0
  423. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  424. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  425. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  426. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  427. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  428. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  429. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  430. package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
  431. package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
  432. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
  433. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
  434. package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
  435. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
  436. package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
  437. package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
  438. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  439. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  440. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  441. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  442. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  443. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  444. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  445. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  446. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  447. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  448. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  449. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  450. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  451. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  452. package/build/dist/Utils/HostAddressUtil.js +80 -0
  453. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  454. package/build/dist/Utils/LabelRuleImportExport.js +22 -6
  455. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  456. package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
  457. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  458. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  459. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  460. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  461. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
  462. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
  463. package/build/dist/Utils/Slo/SloDuration.js +13 -5
  464. package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
  465. package/build/dist/Utils/Slo/SloHealth.js +8 -7
  466. package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
  467. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
  468. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
  469. package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
  470. package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
  471. package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
  472. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  473. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  474. package/jest.config.json +3 -1
  475. package/package.json +1 -1
  476. package/test-setup.sh +103 -34
  477. package/tsconfig.json +13 -0
  478. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  479. package/Server/API/TeamComplianceAPI.ts +0 -158
  480. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  481. package/Server/Services/TeamComplianceService.ts +0 -733
  482. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  483. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  484. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  485. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  486. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  487. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  488. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  489. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  490. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  491. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  492. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  493. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  494. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  495. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  496. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  497. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  498. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  499. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  500. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  501. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  502. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  503. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  504. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  505. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  506. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  507. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  508. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  509. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  510. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  511. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  512. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  513. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  514. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import FilterCondition from "../../../Types/Filter/FilterCondition";
2
2
  import ObjectID from "../../../Types/ObjectID";
3
+ import MonitorType from "../../../Types/Monitor/MonitorType";
3
4
  import RuleCriteria, {
4
5
  RULE_CRITERIA_LEGACY_NEVER_MATCH_PATTERN,
5
6
  RULE_CRITERIA_SCHEMA_VERSION,
@@ -385,3 +386,111 @@ describe("getSloMonitorRuleCriteriaKey", () => {
385
386
  );
386
387
  });
387
388
  });
389
+
390
+ describe("SLO monitor type criteria descriptions", () => {
391
+ test.each(["", "Invalid monitor type"])(
392
+ "describes an invalid legacy monitor type as matching no monitors: %p",
393
+ (monitorType: string) => {
394
+ expect(
395
+ describeSloMonitorRuleCriteria({
396
+ rule: {
397
+ monitorType: monitorType as MonitorType,
398
+ monitorNamePattern: ".*",
399
+ },
400
+ }),
401
+ ).toBe(SLO_MONITOR_RULE_INVALID_CRITERIA_DESCRIPTION);
402
+ },
403
+ );
404
+
405
+ test.each([
406
+ { operator: RuleCriteriaOperator.Equals, value: "Invalid monitor type" },
407
+ { operator: RuleCriteriaOperator.NotEquals, value: "" },
408
+ { operator: RuleCriteriaOperator.Contains, value: MonitorType.API },
409
+ { operator: RuleCriteriaOperator.HasAnyOf, value: [MonitorType.API] },
410
+ ])(
411
+ "describes invalid type criteria as matching no monitors: %p",
412
+ (filter: Pick<RuleCriteriaFilter, "operator" | "value">) => {
413
+ expect(
414
+ describeSloMonitorRuleCriteria({
415
+ rule: {
416
+ criteria: criteria(FilterCondition.Any, [
417
+ { field: "monitorType", ...filter },
418
+ ]),
419
+ },
420
+ }),
421
+ ).toBe(SLO_MONITOR_RULE_INVALID_CRITERIA_DESCRIPTION);
422
+ },
423
+ );
424
+
425
+ test.each([RuleCriteriaOperator.Equals, RuleCriteriaOperator.NotEquals])(
426
+ "describes configured monitor type %s without looking up labels",
427
+ (operator: RuleCriteriaOperator) => {
428
+ const rule: SloMonitorRuleCriteriaCarrier = {
429
+ criteria: criteria(FilterCondition.All, [
430
+ { field: "monitorType", operator, value: MonitorType.API },
431
+ {
432
+ field: "monitorNamePattern",
433
+ operator: RuleCriteriaOperator.DoesNotMatchPattern,
434
+ value: "^staging-",
435
+ },
436
+ ]),
437
+ monitorType: MonitorType.Website,
438
+ };
439
+ expect(describeSloMonitorRuleCriteria({ rule })).toBe(
440
+ `Type ${SLO_MONITOR_RULE_OPERATOR_PHRASES[operator]} "API" AND Name does not match pattern "^staging-"`,
441
+ );
442
+ expect(getSloMonitorRuleLabelIds(rule)).toEqual([]);
443
+ },
444
+ );
445
+
446
+ test("describes a legacy monitor type combined with other attributes", () => {
447
+ expect(
448
+ describeSloMonitorRuleCriteria({
449
+ rule: {
450
+ monitorType: MonitorType.Website,
451
+ monitorLabels: [{ _id: PRODUCTION_LABEL_ID, name: "Production" }],
452
+ monitorNamePattern: "^checkout-",
453
+ },
454
+ }),
455
+ ).toBe(
456
+ 'Labels has any of "Production" AND Type is "Website" AND Name matches pattern "^checkout-"',
457
+ );
458
+ });
459
+
460
+ test("describes a type-only legacy rule as a configured condition", () => {
461
+ expect(
462
+ describeSloMonitorRuleCriteria({
463
+ rule: { monitorType: MonitorType.API },
464
+ }),
465
+ ).toBe('Type is "API"');
466
+ });
467
+
468
+ test("detects changes to legacy monitor types for audit and feed updates", () => {
469
+ const apiKey: string = getSloMonitorRuleCriteriaKey({
470
+ monitorType: MonitorType.API,
471
+ });
472
+ expect(apiKey).not.toBe(
473
+ getSloMonitorRuleCriteriaKey({ monitorType: MonitorType.Website }),
474
+ );
475
+ expect(apiKey).not.toBe(getSloMonitorRuleCriteriaKey({}));
476
+ expect(getSloMonitorRuleCriteriaKey({ monitorType: null })).toBe(
477
+ getSloMonitorRuleCriteriaKey({}),
478
+ );
479
+ });
480
+
481
+ test("ignores legacy monitor type shadows for configured rule identity", () => {
482
+ const configured: RuleCriteria = criteria(FilterCondition.All, [
483
+ {
484
+ field: "monitorType",
485
+ operator: RuleCriteriaOperator.Equals,
486
+ value: MonitorType.API,
487
+ },
488
+ ]);
489
+ expect(
490
+ getSloMonitorRuleCriteriaKey({
491
+ criteria: configured,
492
+ monitorType: MonitorType.Website,
493
+ }),
494
+ ).toBe(getSloMonitorRuleCriteriaKey({ criteria: configured }));
495
+ });
496
+ });
@@ -5,6 +5,7 @@ import SloWindowType from "../../../Types/ServiceLevelObjective/SloWindowType";
5
5
  import {
6
6
  getSliTypeText,
7
7
  getSloAtRiskText,
8
+ getSloBudgetRemainingText,
8
9
  getSloDowntimeStatusesText,
9
10
  getSloHeadline,
10
11
  getSloMonitorCountText,
@@ -79,6 +80,69 @@ describe("getSloTargetText", () => {
79
80
  });
80
81
  });
81
82
 
83
+ describe("getSloBudgetRemainingText", () => {
84
+ test.each([-99882.8, -120, -0.001, -Number.MIN_VALUE, -Number.MAX_VALUE])(
85
+ "explains a %p%% remaining budget as exceeded, even if it rounds to zero",
86
+ (percentage: number) => {
87
+ expect(getSloBudgetRemainingText(percentage)).toBe("Budget exceeded");
88
+ },
89
+ );
90
+
91
+ test.each([0, -0])(
92
+ "distinguishes an exactly exhausted %p%% budget from an overage",
93
+ (percentage: number) => {
94
+ expect(getSloBudgetRemainingText(percentage)).toBe("No budget left");
95
+ },
96
+ );
97
+
98
+ test.each([Number.MIN_VALUE, 0.001, 0.049, 0.05, 0.0999])(
99
+ "keeps a small positive %p%% budget distinct from no budget left",
100
+ (percentage: number) => {
101
+ expect(getSloBudgetRemainingText(percentage)).toBe("<0.1%");
102
+ },
103
+ );
104
+
105
+ test.each([
106
+ [0.1, "0.1%"],
107
+ [0.14, "0.1%"],
108
+ [0.15, "0.2%"],
109
+ [15, "15%"],
110
+ [20, "20%"],
111
+ [62.5, "62.5%"],
112
+ [62.56, "62.6%"],
113
+ [100, "100%"],
114
+ ])(
115
+ "shows a positive %p%% remainder as %s without unnecessary decimal places",
116
+ (percentage: number, expected: string) => {
117
+ expect(getSloBudgetRemainingText(percentage)).toBe(expected);
118
+ },
119
+ );
120
+
121
+ test.each([100.01, 150, Number.MAX_VALUE])(
122
+ "never claims more than a full budget when the input is %p%%",
123
+ (percentage: number) => {
124
+ expect(getSloBudgetRemainingText(percentage)).toBe("100%");
125
+ },
126
+ );
127
+
128
+ test.each([
129
+ ["missing", undefined],
130
+ ["null", null],
131
+ ["NaN", Number.NaN],
132
+ ["positive infinity", Number.POSITIVE_INFINITY],
133
+ ["negative infinity", Number.NEGATIVE_INFINITY],
134
+ ["numeric string", "62.5" as unknown as number],
135
+ ["negative numeric string", "-99882.8" as unknown as number],
136
+ ["true", true as unknown as number],
137
+ ["false", false as unknown as number],
138
+ ])(
139
+ "makes no budget claim for %s data",
140
+ (_label: string, percentage: number | null | undefined) => {
141
+ expect(getSloBudgetRemainingText(percentage)).toBeNull();
142
+ },
143
+ );
144
+ });
145
+
82
146
  describe("getSloMonitorCountText", () => {
83
147
  test.each([
84
148
  [0, "No monitors"],
@@ -0,0 +1,248 @@
1
+ import ObjectID from "../../../Types/ObjectID";
2
+ import { JSONObject } from "../../../Types/JSON";
3
+ import {
4
+ UptimeDailyAggregate,
5
+ UptimeDayBucket,
6
+ } from "../../../Types/StatusPage/UptimeDailyAggregate";
7
+ import UptimeDailyAggregateUtil from "../../../Utils/StatusPage/UptimeDailyAggregateUtil";
8
+ import { describe, expect, test } from "@jest/globals";
9
+
10
+ /*
11
+ * WHAT THIS FILE IS DEFENDING
12
+ *
13
+ * The wire format for the uptime aggregate, which replaced shipping raw
14
+ * MonitorStatusTimeline rows to the status page.
15
+ *
16
+ * Two properties matter more than the round trip itself:
17
+ *
18
+ * 1. It must be SMALL. The rows it replaces serialize to ~431 bytes each,
19
+ * almost entirely typed-JSON envelopes, so one real status page's 254,550
20
+ * matching rows came to ~105 MB. If this format ever grows envelopes it
21
+ * gives that back.
22
+ *
23
+ * 2. Parsing must be TOTAL. A status page that cannot read its aggregate has
24
+ * to fall back to "no data", which is honest. What it must never do is
25
+ * fall back to something that looks like uptime.
26
+ */
27
+
28
+ const MONITOR_A: string = "11111111-1111-4111-8111-111111111111";
29
+ const STATUS_UP: string = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
30
+
31
+ function sample(): UptimeDailyAggregate {
32
+ return {
33
+ monitors: [
34
+ {
35
+ monitorId: new ObjectID(MONITOR_A),
36
+ buckets: [
37
+ {
38
+ bucketStart: new Date("2026-07-01T00:00:00.000Z"),
39
+ bucketEnd: new Date("2026-07-02T00:00:00.000Z"),
40
+ daySeconds: 86400,
41
+ coveredSeconds: 86400,
42
+ statusDurations: [
43
+ {
44
+ monitorStatusId: new ObjectID(STATUS_UP),
45
+ seconds: 86400,
46
+ },
47
+ ],
48
+ },
49
+ {
50
+ bucketStart: new Date("2026-07-02T00:00:00.000Z"),
51
+ bucketEnd: new Date("2026-07-03T00:00:00.000Z"),
52
+ daySeconds: 86400,
53
+ coveredSeconds: 0,
54
+ statusDurations: [],
55
+ },
56
+ ],
57
+ },
58
+ ],
59
+ isComplete: true,
60
+ completeFrom: null,
61
+ };
62
+ }
63
+
64
+ describe("UptimeDailyAggregateUtil", () => {
65
+ describe("round trip", () => {
66
+ test("survives toJSON -> fromJSON unchanged", () => {
67
+ const parsed: UptimeDailyAggregate = UptimeDailyAggregateUtil.fromJSON(
68
+ UptimeDailyAggregateUtil.toJSON(sample()),
69
+ );
70
+
71
+ expect(parsed.monitors).toHaveLength(1);
72
+ expect(parsed.monitors[0]!.monitorId.toString()).toBe(MONITOR_A);
73
+ expect(parsed.monitors[0]!.buckets).toHaveLength(2);
74
+
75
+ const first: UptimeDayBucket = parsed.monitors[0]!.buckets[0]!;
76
+
77
+ expect(first.coveredSeconds).toBe(86400);
78
+ expect(first.daySeconds).toBe(86400);
79
+ expect(first.bucketStart.toISOString()).toBe("2026-07-01T00:00:00.000Z");
80
+ expect(first.statusDurations[0]!.monitorStatusId.toString()).toBe(
81
+ STATUS_UP,
82
+ );
83
+ });
84
+
85
+ test("a no-data day survives as a no-data day", () => {
86
+ /*
87
+ * The single most important thing to preserve. If coverage were lost in
88
+ * transit the client would treat the day as ordinary and paint it with
89
+ * the operator's colour - which is the bug this whole change removes.
90
+ */
91
+ const parsed: UptimeDailyAggregate = UptimeDailyAggregateUtil.fromJSON(
92
+ UptimeDailyAggregateUtil.toJSON(sample()),
93
+ );
94
+
95
+ expect(parsed.monitors[0]!.buckets[1]!.coveredSeconds).toBe(0);
96
+ expect(parsed.monitors[0]!.buckets[1]!.statusDurations).toHaveLength(0);
97
+ });
98
+ });
99
+
100
+ describe("the wire format stays small", () => {
101
+ test("dates and ids are plain strings, not typed-JSON envelopes", () => {
102
+ const json: JSONObject = UptimeDailyAggregateUtil.toJSON(sample());
103
+ const encoded: string = JSON.stringify(json);
104
+
105
+ expect(encoded).not.toContain("_type");
106
+ expect(encoded).toContain("2026-07-01T00:00:00.000Z");
107
+ expect(encoded).toContain(MONITOR_A);
108
+ });
109
+
110
+ test("a 90-day, 50-monitor page stays far below the row payload it replaces", () => {
111
+ /*
112
+ * The rows this replaces were ~431 bytes each and a real page matched
113
+ * 254,550 of them (~105 MB). This asserts the replacement is bounded by
114
+ * structure rather than by how much a monitor flaps.
115
+ */
116
+ const aggregate: UptimeDailyAggregate = {
117
+ monitors: [],
118
+ isComplete: true,
119
+ completeFrom: null,
120
+ };
121
+
122
+ for (let m: number = 0; m < 50; m++) {
123
+ const buckets: Array<UptimeDayBucket> = [];
124
+
125
+ for (let d: number = 0; d < 90; d++) {
126
+ const start: Date = new Date(
127
+ Date.UTC(2026, 3, 1) + d * 24 * 60 * 60 * 1000,
128
+ );
129
+
130
+ buckets.push({
131
+ bucketStart: start,
132
+ bucketEnd: new Date(start.getTime() + 86400000),
133
+ daySeconds: 86400,
134
+ coveredSeconds: 86400,
135
+ statusDurations: [
136
+ { monitorStatusId: new ObjectID(STATUS_UP), seconds: 86000 },
137
+ { monitorStatusId: new ObjectID(MONITOR_A), seconds: 400 },
138
+ ],
139
+ });
140
+ }
141
+
142
+ aggregate.monitors.push({
143
+ monitorId: new ObjectID(
144
+ `${MONITOR_A.slice(0, -2)}${m % 10}${m % 10}`,
145
+ ),
146
+ buckets,
147
+ });
148
+ }
149
+
150
+ const bytes: number = JSON.stringify(
151
+ UptimeDailyAggregateUtil.toJSON(aggregate),
152
+ ).length;
153
+
154
+ // Comfortably under a megabyte for the largest page the product allows.
155
+ expect(bytes).toBeLessThan(1_500_000);
156
+ });
157
+ });
158
+
159
+ describe("parsing is total", () => {
160
+ test.each([
161
+ ["null", null],
162
+ ["undefined", undefined],
163
+ ["an empty object", {}],
164
+ ["monitors not an array", { monitors: "nope" }],
165
+ ])(
166
+ "%s yields an empty aggregate rather than throwing",
167
+ (_name: string, input: unknown) => {
168
+ expect(() => {
169
+ return UptimeDailyAggregateUtil.fromJSON(input as JSONObject);
170
+ }).not.toThrow();
171
+
172
+ const parsed: UptimeDailyAggregate = UptimeDailyAggregateUtil.fromJSON(
173
+ input as JSONObject,
174
+ );
175
+
176
+ expect(parsed.monitors).toHaveLength(0);
177
+ },
178
+ );
179
+
180
+ test("a bucket with an unparseable date is dropped, not turned into coverage", () => {
181
+ const parsed: UptimeDailyAggregate = UptimeDailyAggregateUtil.fromJSON({
182
+ monitors: [
183
+ {
184
+ monitorId: MONITOR_A,
185
+ buckets: [
186
+ {
187
+ bucketStart: "not-a-date",
188
+ bucketEnd: "also-not",
189
+ daySeconds: 1,
190
+ },
191
+ ],
192
+ },
193
+ ],
194
+ weird: true,
195
+ } as unknown as JSONObject);
196
+
197
+ expect(parsed.monitors[0]!.buckets).toHaveLength(0);
198
+ });
199
+
200
+ test("a missing isComplete means complete, not incomplete", () => {
201
+ /*
202
+ * An older server has no cap in this path either. Defaulting to
203
+ * incomplete would grey out every bar on a perfectly healthy page.
204
+ */
205
+ const parsed: UptimeDailyAggregate = UptimeDailyAggregateUtil.fromJSON({
206
+ monitors: [],
207
+ } as unknown as JSONObject);
208
+
209
+ expect(parsed.isComplete).toBe(true);
210
+ });
211
+
212
+ test("an explicit isComplete false is honoured", () => {
213
+ const parsed: UptimeDailyAggregate = UptimeDailyAggregateUtil.fromJSON({
214
+ monitors: [],
215
+ isComplete: false,
216
+ completeFrom: "2026-07-05T00:00:00.000Z",
217
+ } as unknown as JSONObject);
218
+
219
+ expect(parsed.isComplete).toBe(false);
220
+ expect(parsed.completeFrom?.toISOString()).toBe(
221
+ "2026-07-05T00:00:00.000Z",
222
+ );
223
+ });
224
+ });
225
+
226
+ describe("getBucketsForMonitor", () => {
227
+ test("returns that monitor's buckets", () => {
228
+ expect(
229
+ UptimeDailyAggregateUtil.getBucketsForMonitor(sample(), MONITOR_A),
230
+ ).toHaveLength(2);
231
+ });
232
+
233
+ test("returns empty for a monitor that is not in the aggregate", () => {
234
+ expect(
235
+ UptimeDailyAggregateUtil.getBucketsForMonitor(
236
+ sample(),
237
+ "99999999-9999-4999-8999-999999999999",
238
+ ),
239
+ ).toHaveLength(0);
240
+ });
241
+
242
+ test("returns empty rather than throwing when there is no aggregate", () => {
243
+ expect(
244
+ UptimeDailyAggregateUtil.getBucketsForMonitor(null, MONITOR_A),
245
+ ).toHaveLength(0);
246
+ });
247
+ });
248
+ });
@@ -3,6 +3,8 @@ import BadDataException from "../Exception/BadDataException";
3
3
  import { JSONObject, ObjectType } from "../JSON";
4
4
  import Port from "../Port";
5
5
  import Typeof from "../Typeof";
6
+ import HostAddressUtil from "../../Utils/HostAddressUtil";
7
+ import IP from "../IP/IP";
6
8
  import { FindOperator } from "typeorm";
7
9
 
8
10
  /*
@@ -22,10 +24,26 @@ const HOST_REGEX: RegExp =
22
24
  */
23
25
  const USER_INFO_REGEX: RegExp = /^[^@/?#\s[\]]*$/;
24
26
 
25
- const IPV6_CHARS_REGEX: RegExp = /^[0-9a-fA-F:.]+$/;
26
-
27
27
  const PORT_REGEX: RegExp = /^\d{1,5}$/;
28
28
 
29
+ /*
30
+ * The authority spellings of an IPv6 host: bracketed ("[::1]", "[::1]:8080")
31
+ * or bare. Two or more colons cannot be a "host:port", so every colon in a
32
+ * bare value belongs to the address itself.
33
+ *
34
+ * This only decides which SHAPE a value has. Whether what it holds is really
35
+ * an address is IP.isIP's job, and isValid below asks it -- the old code
36
+ * settled for a "[0-9a-fA-F:.]+" charset, which called "::::" and
37
+ * "1.2.3.4.5.6" valid hosts and then handed them to a socket.
38
+ */
39
+ function isIPv6Authority(authority: string): boolean {
40
+ return authority.startsWith("[") || (authority.match(/:/g) || []).length > 1;
41
+ }
42
+
43
+ function isIPv6Address(value: string): boolean {
44
+ return Boolean(value) && IP.isIP(value) && new IP(value).isIPv6();
45
+ }
46
+
29
47
  export default class Hostname extends DatabaseProperty {
30
48
  private _route: string = "";
31
49
  public get hostname(): string {
@@ -104,15 +122,15 @@ export default class Hostname extends DatabaseProperty {
104
122
  /^\[([0-9a-fA-F:.]+)\](?::(\d{1,5}))?$/,
105
123
  );
106
124
  if (bracketedIpv6Match) {
107
- return true;
125
+ return isIPv6Address(bracketedIpv6Match[1] as string);
108
126
  }
109
127
 
110
128
  /*
111
129
  * Unbracketed IPv6 literal. Two or more colons cannot be a "host:port",
112
130
  * so the colons must be part of the address itself.
113
131
  */
114
- if ((authority.match(/:/g) || []).length > 1) {
115
- return IPV6_CHARS_REGEX.test(authority);
132
+ if (isIPv6Authority(authority)) {
133
+ return isIPv6Address(authority);
116
134
  }
117
135
 
118
136
  const colonIndex: number = authority.indexOf(":");
@@ -158,28 +176,57 @@ export default class Hostname extends DatabaseProperty {
158
176
  throw new BadDataException("Invalid JSON: " + JSON.stringify(json));
159
177
  }
160
178
 
179
+ /*
180
+ * The authority form. An IPv6 host is BRACKETED once a port is appended,
181
+ * because "2001:db8::1" + ":179" is not an ambiguous-looking string — it is
182
+ * "2001:db8::1:179", which net.isIP() accepts as a different, perfectly
183
+ * valid address. Anything that read this back (or showed it to an operator)
184
+ * was therefore pointed at a host nobody configured.
185
+ *
186
+ * Without a port the host is returned exactly as it is held, brackets and
187
+ * all. A bracketed value came from a URL authority and has to go back into
188
+ * one — dropping the brackets there would make URL.toString() emit
189
+ * "https://2001:db8::1/", which no URL parser accepts.
190
+ */
161
191
  public override toString(): string {
162
- let hostname: string = this.hostname;
163
-
164
- if (this.port) {
165
- hostname += ":" + this.port.toString();
192
+ if (!this.port) {
193
+ return this.hostname;
166
194
  }
167
195
 
168
- return hostname;
196
+ return HostAddressUtil.formatHostAndPort({
197
+ host: this.hostname,
198
+ port: this.port.toString(),
199
+ });
169
200
  }
170
201
 
202
+ /*
203
+ * Reads a stored host, which may carry a ":port".
204
+ *
205
+ * IPv6 literals are handed to fromAuthority instead of being split, because
206
+ * splitting on the first colon is meaningless for an address that is made
207
+ * of colons: "2001:518:2800:9::2" used to come back as host "2001" with
208
+ * port 518 — and because 518 is a legal port number NOTHING threw. A
209
+ * monitor saved from the dashboard therefore silently pointed at a host
210
+ * called "2001", and the address the operator typed was simply gone.
211
+ *
212
+ * DNS names and IPv4 literals keep the original behaviour exactly: neither
213
+ * can contain two colons, so neither can reach the new branch.
214
+ */
171
215
  public static fromString(hostname: string | Hostname): Hostname {
172
216
  if (hostname instanceof Hostname) {
173
217
  hostname = hostname.toString();
174
218
  }
175
219
 
176
- if (hostname.includes(":")) {
177
- return new Hostname(
178
- hostname.split(":")[0] as string,
179
- hostname.split(":")[1],
180
- );
220
+ const value: string = (hostname || "").trim();
221
+
222
+ if (isIPv6Authority(value)) {
223
+ return Hostname.fromAuthority(value);
224
+ }
225
+
226
+ if (value.includes(":")) {
227
+ return new Hostname(value.split(":")[0] as string, value.split(":")[1]);
181
228
  }
182
- return new Hostname(hostname);
229
+ return new Hostname(value);
183
230
  }
184
231
 
185
232
  /*
@@ -7,13 +7,30 @@ import { FindOperator } from "typeorm";
7
7
  export default class Route extends DatabaseProperty {
8
8
  private static readonly SCHEME_PREFIX: RegExp = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
9
9
 
10
+ /*
11
+ * True when the value, read on its own as a relative reference, would start
12
+ * with a scheme (RFC 3986 4.2): "javascript:alert(1)", but equally
13
+ * "bot123:ABC/sendMessage". Route refuses such values so a scheme can never
14
+ * reach a navigation sink; URL uses this to know when a path it parsed has
15
+ * to keep its leading "/".
16
+ */
17
+ public static hasSchemePrefix(route: string): boolean {
18
+ return Route.SCHEME_PREFIX.test(route);
19
+ }
20
+
10
21
  private static validateRoute(route: string): void {
11
- if (Route.SCHEME_PREFIX.test(route)) {
22
+ if (Route.hasSchemePrefix(route)) {
12
23
  throw new BadDataException(`Invalid route: ${route}`);
13
24
  }
14
25
 
26
+ /*
27
+ * RFC 3986 path characters: unreserved (which includes "~"), sub-delims,
28
+ * ":" and "@", plus "/", "?", "#", "[", "]" and "%" for the rest of a
29
+ * path-and-query. "~" was missing, so "https://example.com/~user" could
30
+ * not be parsed at all.
31
+ */
15
32
  const matchRouteCharacters: RegExp =
16
- /^[a-zA-Z_\d\-!#$%&'()*+,./:;=?@[\]]*$/;
33
+ /^[a-zA-Z_\d\-!#$%&'()*+,./:;=?@[\]~]*$/;
17
34
 
18
35
  if (route && !matchRouteCharacters.test(route)) {
19
36
  throw new BadDataException(`Invalid route: ${route}`);
package/Types/API/URL.ts CHANGED
@@ -459,7 +459,26 @@ export default class URL extends DatabaseProperty {
459
459
  const path: string = pathSegments.join("/");
460
460
 
461
461
  if (path || fragment) {
462
- route = new Route(path + fragment);
462
+ /*
463
+ * The route is held without the "/" that separates it from the
464
+ * authority ("api/v1/items"), and toString puts it back. Dropping it is
465
+ * only harmless while the first segment has no ":" — once it does, the
466
+ * bare path reads as a scheme ("bot123:ABC/sendMessage" is scheme
467
+ * "bot123", RFC 3986 4.2) and Route rejects it. Telegram puts the bot
468
+ * token, which always contains a ":", in exactly that segment, so every
469
+ * https://api.telegram.org/bot<token>/... URL failed to parse — which
470
+ * broke the Telegram workflow component, the API components, and the
471
+ * SSRF check on every sandboxed axios call to Telegram.
472
+ *
473
+ * Keep the "/" for those paths only. It is the path as written, it
474
+ * cannot be read as a scheme, and toString produces the same string
475
+ * either way; every path that parsed before parses to the same Route.
476
+ */
477
+ const routeValue: string = path + fragment;
478
+
479
+ route = new Route(
480
+ Route.hasSchemePrefix(routeValue) ? "/" + routeValue : routeValue,
481
+ );
463
482
  }
464
483
 
465
484
  const queryString: string = URL.queryStringOf(url);
@@ -535,8 +554,13 @@ export default class URL extends DatabaseProperty {
535
554
  }
536
555
 
537
556
  public addRoute(route: Route | string): URL {
557
+ /*
558
+ * A string goes to Route.addRoute as-is, which prefixes the "/" before
559
+ * validating. Wrapping it in a Route first validated the bare form, so
560
+ * addRoute("bot123:ABC/sendMessage") threw for the same reason as above.
561
+ */
538
562
  if (typeof route === Typeof.String) {
539
- this.route.addRoute(new Route(route.toString()));
563
+ this.route.addRoute(route.toString());
540
564
  }
541
565
 
542
566
  if (route instanceof Route) {