@oneuptime/common 13.0.8 → 14.0.0

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 (333) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Scripts/generate-service-worker.js +65 -4
  6. package/Server/API/BaseAPI.ts +26 -5
  7. package/Server/API/GlobalConfigAPI.ts +228 -462
  8. package/Server/API/ProjectOIDC.ts +25 -16
  9. package/Server/API/ProjectSSO.ts +24 -16
  10. package/Server/API/StatusPageAPI.ts +100 -42
  11. package/Server/API/TeamMemberAPI.ts +10 -1
  12. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  13. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  14. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  15. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  16. package/Server/EnvironmentConfig.ts +147 -2
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  19. package/Server/Middleware/UserAuthorization.ts +38 -16
  20. package/Server/Services/AuditLogService.ts +84 -1079
  21. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  22. package/Server/Services/GlobalConfigService.ts +80 -1
  23. package/Server/Services/MonitorProbeService.ts +116 -20
  24. package/Server/Services/MonitorService.ts +56 -4
  25. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  26. package/Server/Services/MonitorTemplateService.ts +26 -0
  27. package/Server/Services/ProjectService.ts +284 -0
  28. package/Server/Services/RumSessionPinService.ts +25 -6
  29. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  30. package/Server/Services/StatusPageService.ts +78 -1
  31. package/Server/Services/TeamMemberService.ts +11 -0
  32. package/Server/Services/TeamService.ts +10 -0
  33. package/Server/Services/UserService.ts +10 -17
  34. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  35. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  36. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  37. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  38. package/Server/Types/Markdown.ts +41 -0
  39. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  40. package/Server/Utils/EditionEnforcement.ts +233 -0
  41. package/Server/Utils/FrontendEnvironment.ts +41 -2
  42. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  43. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  44. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  45. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  46. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  47. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  48. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  49. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  50. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  51. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  52. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  53. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  54. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  55. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  56. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  57. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  58. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  59. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  60. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  61. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  62. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  63. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  64. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  65. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  66. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  67. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  68. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  69. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  70. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  71. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  72. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  73. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  74. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  75. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  76. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  77. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  78. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  79. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  80. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  81. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  82. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  83. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  84. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  85. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  86. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  87. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  88. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  89. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  90. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  91. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  92. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  93. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  95. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  96. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  97. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  98. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  99. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  100. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  101. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  102. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  103. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  104. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  105. package/Tests/Server/Types/Markdown.test.ts +181 -0
  106. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  107. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  108. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  109. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  110. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  111. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  112. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  113. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  114. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  115. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  116. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  117. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  118. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  119. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  120. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  121. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  122. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  123. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  124. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  125. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  126. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  127. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  128. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  129. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  130. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  131. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  132. package/Types/API/Hostname.ts +63 -16
  133. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  134. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  135. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  136. package/Types/Database/CanonicalModelInstance.ts +102 -0
  137. package/Types/Database/TableColumn.ts +20 -6
  138. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  139. package/Types/IP/IPv6.ts +6 -1
  140. package/Types/Monitor/MonitorStep.ts +6 -1
  141. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  142. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  143. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  144. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  145. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  146. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  147. package/UI/Components/ModelList/ModelList.tsx +11 -1
  148. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  149. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  150. package/UI/esbuild-enterprise.js +263 -0
  151. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  152. package/Utils/HostAddressUtil.ts +94 -0
  153. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  154. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  155. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  156. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  157. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  159. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  161. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  163. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  164. package/build/dist/Server/API/BaseAPI.js +26 -5
  165. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  166. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  167. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  168. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  169. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  170. package/build/dist/Server/API/ProjectSSO.js +24 -16
  171. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  172. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  173. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  174. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  175. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  176. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  177. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  178. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  179. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  180. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  181. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  182. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  183. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  184. package/build/dist/Server/EnvironmentConfig.js +84 -2
  185. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  190. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  191. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  192. package/build/dist/Server/Services/AuditLogService.js +71 -740
  193. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  194. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  195. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  196. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  197. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  198. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  199. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  200. package/build/dist/Server/Services/MonitorService.js +45 -4
  201. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  202. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  203. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  204. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  205. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  206. package/build/dist/Server/Services/ProjectService.js +214 -0
  207. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  208. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  209. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  210. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  211. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  212. package/build/dist/Server/Services/StatusPageService.js +71 -0
  213. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  214. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  215. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  216. package/build/dist/Server/Services/TeamService.js +9 -0
  217. package/build/dist/Server/Services/TeamService.js.map +1 -1
  218. package/build/dist/Server/Services/UserService.js +10 -16
  219. package/build/dist/Server/Services/UserService.js.map +1 -1
  220. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  221. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  222. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  223. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  224. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  225. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  226. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  227. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  228. package/build/dist/Server/Types/Markdown.js +31 -0
  229. package/build/dist/Server/Types/Markdown.js.map +1 -1
  230. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  231. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  232. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  233. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  234. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  235. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  236. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  237. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  238. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  239. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  240. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  241. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  242. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  243. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  245. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  246. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  247. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  248. package/build/dist/Types/API/Hostname.js +59 -11
  249. package/build/dist/Types/API/Hostname.js.map +1 -1
  250. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  251. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  252. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  253. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  254. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  255. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  256. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  257. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  258. package/build/dist/Types/Database/TableColumn.js +19 -6
  259. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  260. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  261. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  262. package/build/dist/Types/IP/IPv6.js +6 -1
  263. package/build/dist/Types/IP/IPv6.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  265. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  266. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  267. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  268. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  269. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  270. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  271. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  272. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  273. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  274. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  275. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  276. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  277. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  278. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  279. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  280. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  281. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  282. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  283. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  284. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  285. package/build/dist/Utils/HostAddressUtil.js +80 -0
  286. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  287. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  288. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  289. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  290. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  291. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  292. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  293. package/jest.config.json +3 -1
  294. package/package.json +1 -1
  295. package/test-setup.sh +103 -34
  296. package/tsconfig.json +13 -0
  297. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  298. package/Server/API/TeamComplianceAPI.ts +0 -158
  299. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  300. package/Server/Services/TeamComplianceService.ts +0 -733
  301. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  302. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  303. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  304. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  305. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  306. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  307. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  308. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  309. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  310. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  311. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  312. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  313. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  314. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  315. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  316. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  317. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  318. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  319. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  320. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  321. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  322. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  323. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  324. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  325. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  326. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  327. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  328. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  329. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  330. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  331. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  332. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  333. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -0,0 +1,138 @@
1
+ import HostAddressUtil from "../../Utils/HostAddressUtil";
2
+ import { describe, expect, test } from "@jest/globals";
3
+ import net from "net";
4
+
5
+ /*
6
+ * The two IPv6 text problems that kept being got wrong one call site at a
7
+ * time: brackets are URL syntax rather than part of the address, and
8
+ * "host:port" is ambiguous for IPv6 in a way that does not look wrong.
9
+ */
10
+
11
+ const CUSTOMER_ADDRESS: string = "2001:518:2800:9::2";
12
+
13
+ describe("HostAddressUtil.isIPv6", () => {
14
+ test.each([
15
+ CUSTOMER_ADDRESS,
16
+ `[${CUSTOMER_ADDRESS}]`,
17
+ "::1",
18
+ "[::1]",
19
+ "::",
20
+ "2001:db8::",
21
+ "::ffff:192.0.2.1",
22
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
23
+ ])("%s is IPv6", (value: string) => {
24
+ expect(HostAddressUtil.isIPv6(value)).toBe(true);
25
+ });
26
+
27
+ test.each([
28
+ "192.0.2.1",
29
+ "example.com",
30
+ "localhost",
31
+ "",
32
+ " ",
33
+ "[not-an-address]",
34
+ "2001:db8::1:8443:extra",
35
+ "example.com:8443",
36
+ ])("%s is not IPv6", (value: string) => {
37
+ expect(HostAddressUtil.isIPv6(value)).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe("HostAddressUtil.stripBrackets", () => {
42
+ test("removes the URL brackets from an IPv6 literal", () => {
43
+ expect(HostAddressUtil.stripBrackets(`[${CUSTOMER_ADDRESS}]`)).toBe(
44
+ CUSTOMER_ADDRESS,
45
+ );
46
+ });
47
+
48
+ test("the result is something a socket will accept, which the bracketed form is not", () => {
49
+ /*
50
+ * The point of the whole helper. Node does not strip these itself:
51
+ * net.connect({host: "[::1]"}) goes to the resolver and fails ENOTFOUND.
52
+ */
53
+ expect(net.isIP(`[${CUSTOMER_ADDRESS}]`)).toBe(0);
54
+ expect(
55
+ net.isIP(HostAddressUtil.stripBrackets(`[${CUSTOMER_ADDRESS}]`)),
56
+ ).toBe(6);
57
+ });
58
+
59
+ test("leaves a bare address, an IPv4 address and a DNS name alone", () => {
60
+ expect(HostAddressUtil.stripBrackets(CUSTOMER_ADDRESS)).toBe(
61
+ CUSTOMER_ADDRESS,
62
+ );
63
+ expect(HostAddressUtil.stripBrackets("192.0.2.1")).toBe("192.0.2.1");
64
+ expect(HostAddressUtil.stripBrackets("example.com")).toBe("example.com");
65
+ });
66
+
67
+ test("trims, because a pasted address usually arrives with whitespace", () => {
68
+ expect(HostAddressUtil.stripBrackets(` ${CUSTOMER_ADDRESS}\n`)).toBe(
69
+ CUSTOMER_ADDRESS,
70
+ );
71
+ });
72
+
73
+ test("does NOT launder a bracketed non-address into a bare hostname", () => {
74
+ expect(HostAddressUtil.stripBrackets("[not-an-address]")).toBe(
75
+ "[not-an-address]",
76
+ );
77
+ expect(HostAddressUtil.stripBrackets("[192.0.2.1]")).toBe("[192.0.2.1]");
78
+ });
79
+
80
+ test("handles empty input", () => {
81
+ expect(HostAddressUtil.stripBrackets("")).toBe("");
82
+ });
83
+ });
84
+
85
+ describe("HostAddressUtil.formatHostAndPort", () => {
86
+ test("brackets an IPv6 host so the port separator cannot be read as the address", () => {
87
+ expect(
88
+ HostAddressUtil.formatHostAndPort({ host: CUSTOMER_ADDRESS, port: 179 }),
89
+ ).toBe(`[${CUSTOMER_ADDRESS}]:179`);
90
+ });
91
+
92
+ test("the unbracketed form really is a DIFFERENT valid address, which is why this matters", () => {
93
+ /*
94
+ * Not a cosmetic issue: "2001:518:2800:9::2" + ":179" parses as a real
95
+ * IPv6 address, so an alert naming it sends somebody to the wrong host
96
+ * rather than to something obviously malformed.
97
+ */
98
+ expect(net.isIP(`${CUSTOMER_ADDRESS}:179`)).toBe(6);
99
+ expect(`${CUSTOMER_ADDRESS}:179`).not.toBe(CUSTOMER_ADDRESS);
100
+
101
+ expect(
102
+ HostAddressUtil.formatHostAndPort({ host: CUSTOMER_ADDRESS, port: 179 }),
103
+ ).not.toBe(`${CUSTOMER_ADDRESS}:179`);
104
+ });
105
+
106
+ test("IPv4 and DNS hosts keep the conventional spelling", () => {
107
+ expect(
108
+ HostAddressUtil.formatHostAndPort({ host: "192.0.2.1", port: 179 }),
109
+ ).toBe("192.0.2.1:179");
110
+ expect(
111
+ HostAddressUtil.formatHostAndPort({ host: "example.com", port: "8443" }),
112
+ ).toBe("example.com:8443");
113
+ });
114
+
115
+ test("does not bracket twice", () => {
116
+ expect(
117
+ HostAddressUtil.formatHostAndPort({
118
+ host: `[${CUSTOMER_ADDRESS}]`,
119
+ port: 179,
120
+ }),
121
+ ).toBe(`[${CUSTOMER_ADDRESS}]:179`);
122
+ });
123
+
124
+ test("no port means the bare host, so a stored destination is not rewritten", () => {
125
+ expect(HostAddressUtil.formatHostAndPort({ host: CUSTOMER_ADDRESS })).toBe(
126
+ CUSTOMER_ADDRESS,
127
+ );
128
+ expect(
129
+ HostAddressUtil.formatHostAndPort({ host: CUSTOMER_ADDRESS, port: "" }),
130
+ ).toBe(CUSTOMER_ADDRESS);
131
+ expect(
132
+ HostAddressUtil.formatHostAndPort({
133
+ host: `[${CUSTOMER_ADDRESS}]`,
134
+ port: undefined,
135
+ }),
136
+ ).toBe(CUSTOMER_ADDRESS);
137
+ });
138
+ });
@@ -0,0 +1,313 @@
1
+ import Hostname from "../../../Types/API/Hostname";
2
+ import URL from "../../../Types/API/URL";
3
+ import IP from "../../../Types/IP/IP";
4
+ import MonitorType from "../../../Types/Monitor/MonitorType";
5
+ import MonitorDestinationUtil, {
6
+ ParsedMonitorDestination,
7
+ } from "../../../Utils/Monitor/MonitorDestinationUtil";
8
+ import { describe, expect, test } from "@jest/globals";
9
+
10
+ /*
11
+ * What the dashboard's destination field does with what somebody typed.
12
+ *
13
+ * This exists because of a customer who had working monitors on their IPv4
14
+ * BGP endpoints, added the IPv6 ones, and could not get them to work
15
+ * Their test address, used verbatim throughout this file, was
16
+ * 2001:518:2800:9::2.
17
+ *
18
+ * The failure was not that IPv6 was rejected. It was that an IPv6 address
19
+ * with a trailing space — which is what copying one out of a looking glass,
20
+ * a router config or a terminal gives you — was SILENTLY TRUNCATED to
21
+ * "2001:518" and saved, with no error shown. The identical IPv4 paste worked,
22
+ * because "1.1.1.1 " has no colon to split on.
23
+ *
24
+ * Every "the customer's address" case below failed before the fix.
25
+ */
26
+
27
+ const CUSTOMER_ADDRESS: string = "2001:518:2800:9::2";
28
+
29
+ function parse(
30
+ value: string,
31
+ monitorType: MonitorType,
32
+ ): ParsedMonitorDestination {
33
+ return MonitorDestinationUtil.parse({
34
+ value: value,
35
+ monitorType: monitorType,
36
+ });
37
+ }
38
+
39
+ const HOST_MONITOR_TYPES: Array<MonitorType> = [
40
+ MonitorType.IP,
41
+ MonitorType.Ping,
42
+ MonitorType.Port,
43
+ ];
44
+
45
+ describe("MonitorDestinationUtil.parse — the customer's IPv6 BGP endpoint", () => {
46
+ for (const monitorType of HOST_MONITOR_TYPES) {
47
+ describe(`${monitorType} monitor`, () => {
48
+ test("keeps the address exactly as typed", () => {
49
+ const parsed: ParsedMonitorDestination = parse(
50
+ CUSTOMER_ADDRESS,
51
+ monitorType,
52
+ );
53
+
54
+ expect(parsed.error).toBeNull();
55
+ expect(parsed.destination).toBeInstanceOf(IP);
56
+ expect(parsed.destination?.toString()).toBe(CUSTOMER_ADDRESS);
57
+ });
58
+
59
+ test("a trailing space does not truncate it to its first two groups", () => {
60
+ /*
61
+ * THE BUG. "2001:518:2800:9::2 " missed IP.isIP (which does not
62
+ * trim), fell through to Hostname.fromString, and came back as host
63
+ * "2001" with port 518. 518 is a legal port, so nothing threw and
64
+ * the operator was shown no error at all.
65
+ */
66
+ const parsed: ParsedMonitorDestination = parse(
67
+ `${CUSTOMER_ADDRESS} `,
68
+ monitorType,
69
+ );
70
+
71
+ expect(parsed.error).toBeNull();
72
+ expect(parsed.destination?.toString()).toBe(CUSTOMER_ADDRESS);
73
+ expect(parsed.destination?.toString()).not.toBe("2001:518");
74
+ });
75
+
76
+ test("a leading space or a trailing newline is tolerated the same way", () => {
77
+ expect(
78
+ parse(` ${CUSTOMER_ADDRESS}`, monitorType).destination?.toString(),
79
+ ).toBe(CUSTOMER_ADDRESS);
80
+ expect(
81
+ parse(`${CUSTOMER_ADDRESS}\n`, monitorType).destination?.toString(),
82
+ ).toBe(CUSTOMER_ADDRESS);
83
+ });
84
+
85
+ test("the bracketed spelling is accepted and stored bare", () => {
86
+ /*
87
+ * "[2001:518:2800:9::2]" is how the address is written inside a URL,
88
+ * so it is a form people have to hand. It used to produce the
89
+ * baffling "Hostname [2001 is not in valid format." on Ping/Port.
90
+ */
91
+ const parsed: ParsedMonitorDestination = parse(
92
+ `[${CUSTOMER_ADDRESS}]`,
93
+ monitorType,
94
+ );
95
+
96
+ expect(parsed.error).toBeNull();
97
+ expect(parsed.destination?.toString()).toBe(CUSTOMER_ADDRESS);
98
+ });
99
+
100
+ test("the equivalent IPv4 paste keeps behaving exactly as it always did", () => {
101
+ expect(parse("192.0.2.1", monitorType).destination?.toString()).toBe(
102
+ "192.0.2.1",
103
+ );
104
+ expect(parse("192.0.2.1 ", monitorType).destination?.toString()).toBe(
105
+ "192.0.2.1",
106
+ );
107
+ });
108
+
109
+ test("an address that is genuinely malformed is reported, not stored", () => {
110
+ const parsed: ParsedMonitorDestination = parse(
111
+ "2001:518:2800:9:::2",
112
+ monitorType,
113
+ );
114
+
115
+ expect(parsed.destination).toBeUndefined();
116
+ expect(parsed.error).toBeTruthy();
117
+ });
118
+
119
+ test("an empty field is a missing destination, not a parse failure", () => {
120
+ expect(parse(" ", monitorType)).toEqual({
121
+ destination: undefined,
122
+ error: "Destination is required",
123
+ });
124
+ });
125
+ });
126
+ }
127
+
128
+ describe("every prefix of the address as it is typed", () => {
129
+ /*
130
+ * Typing left to right passes through "2001:518:2800:9::", which IS a
131
+ * real address. The form used to write each parse into the step and
132
+ * never clear it, so a prefix that parsed could outlive the keystrokes
133
+ * after it. What matters here is that no prefix EVER parses to something
134
+ * that is not what was typed.
135
+ */
136
+ test("a prefix either fails to parse or parses to itself — never to something else", () => {
137
+ for (let i: number = 1; i <= CUSTOMER_ADDRESS.length; i++) {
138
+ const prefix: string = CUSTOMER_ADDRESS.substring(0, i);
139
+
140
+ for (const monitorType of HOST_MONITOR_TYPES) {
141
+ const parsed: ParsedMonitorDestination = parse(prefix, monitorType);
142
+
143
+ if (parsed.destination) {
144
+ expect(parsed.destination.toString()).toBe(prefix);
145
+ }
146
+ }
147
+ }
148
+ });
149
+
150
+ test("the COMPLETE address never parses to 2001:518, however it is spelled", () => {
151
+ /*
152
+ * "2001:518" on its own is a legitimate parse of the prefix "2001:518"
153
+ * — the operator really had typed only that. What must never happen is
154
+ * the complete address arriving and 2001:518 being what gets stored.
155
+ */
156
+ const spellings: Array<string> = [
157
+ CUSTOMER_ADDRESS,
158
+ `${CUSTOMER_ADDRESS} `,
159
+ ` ${CUSTOMER_ADDRESS}`,
160
+ `${CUSTOMER_ADDRESS}\n`,
161
+ `\t${CUSTOMER_ADDRESS}\t`,
162
+ `[${CUSTOMER_ADDRESS}]`,
163
+ ` [${CUSTOMER_ADDRESS}] `,
164
+ ];
165
+
166
+ for (const spelling of spellings) {
167
+ for (const monitorType of HOST_MONITOR_TYPES) {
168
+ expect(parse(spelling, monitorType).destination?.toString()).toBe(
169
+ CUSTOMER_ADDRESS,
170
+ );
171
+ }
172
+ }
173
+ });
174
+ });
175
+ });
176
+
177
+ describe("MonitorDestinationUtil.parse — hostnames still work", () => {
178
+ test("a DNS name is a Hostname", () => {
179
+ const parsed: ParsedMonitorDestination = parse(
180
+ "rs1.example.net",
181
+ MonitorType.Ping,
182
+ );
183
+
184
+ expect(parsed.error).toBeNull();
185
+ expect(parsed.destination).toBeInstanceOf(Hostname);
186
+ expect(parsed.destination?.toString()).toBe("rs1.example.net");
187
+ });
188
+
189
+ test("a pasted 'host:port' is split, not stored as a host with a colon in its name", () => {
190
+ /*
191
+ * `new Hostname("rs1.example.net:179")` would happily store the whole
192
+ * authority AS THE HOST, and the socket would then go looking for a DNS
193
+ * name with a colon in it.
194
+ */
195
+ const parsed: ParsedMonitorDestination = parse(
196
+ "rs1.example.net:179",
197
+ MonitorType.Port,
198
+ );
199
+
200
+ expect(parsed.error).toBeNull();
201
+ expect((parsed.destination as Hostname).hostname).toBe("rs1.example.net");
202
+ expect((parsed.destination as Hostname).port?.toNumber()).toBe(179);
203
+ });
204
+
205
+ test("a bracketed IPv6 host WITH a port is split the same way", () => {
206
+ const parsed: ParsedMonitorDestination = parse(
207
+ `[${CUSTOMER_ADDRESS}]:179`,
208
+ MonitorType.Port,
209
+ );
210
+
211
+ expect(parsed.error).toBeNull();
212
+ expect((parsed.destination as Hostname).port?.toNumber()).toBe(179);
213
+ });
214
+
215
+ test("an IP monitor rejects a DNS name, as it always has", () => {
216
+ expect(
217
+ parse("rs1.example.net", MonitorType.IP).destination,
218
+ ).toBeUndefined();
219
+ });
220
+ });
221
+
222
+ describe("MonitorDestinationUtil.parse — URL monitor types", () => {
223
+ const URL_MONITOR_TYPES: Array<MonitorType> = [
224
+ MonitorType.Website,
225
+ MonitorType.API,
226
+ MonitorType.SSLCertificate,
227
+ ];
228
+
229
+ for (const monitorType of URL_MONITOR_TYPES) {
230
+ test(`${monitorType}: a bracketed IPv6 URL round-trips`, () => {
231
+ const parsed: ParsedMonitorDestination = parse(
232
+ `https://[${CUSTOMER_ADDRESS}]/health`,
233
+ monitorType,
234
+ );
235
+
236
+ expect(parsed.error).toBeNull();
237
+ expect(parsed.destination).toBeInstanceOf(URL);
238
+ expect(parsed.destination?.toString()).toBe(
239
+ `https://[${CUSTOMER_ADDRESS}]/health`,
240
+ );
241
+ });
242
+
243
+ test(`${monitorType}: a BARE IPv6 URL is bracketed rather than stored unusable`, () => {
244
+ /*
245
+ * "https://2001:518:2800:9::2/" is not a URL — the first colon reads
246
+ * as the port separator, and every HTTP client rejects it. The field
247
+ * used to accept it verbatim and the monitor then failed forever.
248
+ */
249
+ const parsed: ParsedMonitorDestination = parse(
250
+ `https://${CUSTOMER_ADDRESS}/`,
251
+ monitorType,
252
+ );
253
+
254
+ expect(parsed.error).toBeNull();
255
+ expect(parsed.destination?.toString()).toBe(
256
+ `https://[${CUSTOMER_ADDRESS}]/`,
257
+ );
258
+ });
259
+
260
+ test(`${monitorType}: an IPv4 or DNS URL is left byte for byte alone`, () => {
261
+ expect(
262
+ parse(
263
+ "https://example.com:8443/x?y=1",
264
+ monitorType,
265
+ ).destination?.toString(),
266
+ ).toBe("https://example.com:8443/x?y=1");
267
+ expect(
268
+ parse("https://192.0.2.1/health", monitorType).destination?.toString(),
269
+ ).toBe("https://192.0.2.1/health");
270
+ });
271
+ }
272
+ });
273
+
274
+ describe("MonitorDestinationUtil.toUrlWithBracketedIpv6Host", () => {
275
+ const bracket: (value: string) => string = (value: string): string => {
276
+ return MonitorDestinationUtil.toUrlWithBracketedIpv6Host(value);
277
+ };
278
+
279
+ test("brackets a bare IPv6 host", () => {
280
+ expect(bracket(`https://${CUSTOMER_ADDRESS}`)).toBe(
281
+ `https://[${CUSTOMER_ADDRESS}]`,
282
+ );
283
+ });
284
+
285
+ test("keeps the path, query and fragment where they were", () => {
286
+ expect(bracket(`https://${CUSTOMER_ADDRESS}/a/b?c=1#d`)).toBe(
287
+ `https://[${CUSTOMER_ADDRESS}]/a/b?c=1#d`,
288
+ );
289
+ });
290
+
291
+ test("does not bracket twice", () => {
292
+ expect(bracket(`https://[${CUSTOMER_ADDRESS}]/a`)).toBe(
293
+ `https://[${CUSTOMER_ADDRESS}]/a`,
294
+ );
295
+ });
296
+
297
+ test("keeps userinfo outside the brackets", () => {
298
+ expect(bracket(`https://user:token@${CUSTOMER_ADDRESS}/a`)).toBe(
299
+ `https://user:token@[${CUSTOMER_ADDRESS}]/a`,
300
+ );
301
+ });
302
+
303
+ test("leaves a DNS or IPv4 URL untouched", () => {
304
+ expect(bracket("https://example.com:8443/x")).toBe(
305
+ "https://example.com:8443/x",
306
+ );
307
+ expect(bracket("http://192.0.2.1/x")).toBe("http://192.0.2.1/x");
308
+ });
309
+
310
+ test("leaves a host:port that only looks address-ish untouched", () => {
311
+ expect(bracket("https://localhost:3000")).toBe("https://localhost:3000");
312
+ });
313
+ });