@oneuptime/common 12.0.32 → 12.0.33

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 (597) hide show
  1. package/Models/AnalyticsModels/Span.ts +14 -0
  2. package/Models/DatabaseModels/EnterpriseLicense.ts +56 -0
  3. package/Models/DatabaseModels/Index.ts +10 -0
  4. package/Models/DatabaseModels/Monitor.ts +76 -0
  5. package/Models/DatabaseModels/NetworkAlertPolicy.ts +690 -0
  6. package/Models/DatabaseModels/NetworkDevice.ts +176 -1
  7. package/Models/DatabaseModels/NetworkSite.ts +201 -2
  8. package/Models/DatabaseModels/NetworkSiteType.ts +102 -3
  9. package/Models/DatabaseModels/NetworkSnmpCredentialProfile.ts +841 -0
  10. package/Models/DatabaseModels/TelemetryException.ts +119 -0
  11. package/Models/DatabaseModels/UserNotificationEmailRollupBatch.ts +334 -0
  12. package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +370 -0
  13. package/Models/DatabaseModels/UserNotificationEmailRollupSetting.ts +282 -0
  14. package/Server/API/EnterpriseLicenseAPI.ts +391 -64
  15. package/Server/API/LlmProviderAPI.ts +137 -22
  16. package/Server/EnvironmentConfig.ts +14 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.ts +55 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.ts +31 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.ts +124 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.ts +132 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.ts +79 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.ts +191 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  24. package/Server/Infrastructure/QueueWorker.ts +38 -8
  25. package/Server/Middleware/BearerTokenAuthorization.ts +12 -0
  26. package/Server/Middleware/ProjectAuthorization.ts +11 -0
  27. package/Server/Middleware/SCIMAuthorization.ts +12 -0
  28. package/Server/Middleware/TelemetryIngest.ts +11 -0
  29. package/Server/Services/AIBillingService.ts +101 -27
  30. package/Server/Services/EnterpriseLicenseInstanceService.ts +197 -0
  31. package/Server/Services/EnterpriseLicenseService.ts +55 -0
  32. package/Server/Services/Index.ts +10 -0
  33. package/Server/Services/MonitorService.ts +143 -21
  34. package/Server/Services/MonitorStatusTimelineService.ts +150 -8
  35. package/Server/Services/MonitorTemplateService.ts +217 -1
  36. package/Server/Services/NetworkAlertPolicyEngineService.ts +2159 -0
  37. package/Server/Services/NetworkAlertPolicyService.ts +944 -0
  38. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +9 -10
  39. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +60 -15
  40. package/Server/Services/NetworkDeviceService.ts +1032 -73
  41. package/Server/Services/NetworkSiteService.ts +1502 -129
  42. package/Server/Services/NetworkSiteTypeService.ts +1216 -0
  43. package/Server/Services/NetworkSnmpCredentialProfileService.ts +238 -0
  44. package/Server/Services/NotificationService.ts +118 -28
  45. package/Server/Services/ProjectService.ts +105 -48
  46. package/Server/Services/StatusPagePrivateUserService.ts +103 -3
  47. package/Server/Services/UserNotificationEmailRollupBatchService.ts +78 -0
  48. package/Server/Services/UserNotificationEmailRollupItemService.ts +79 -0
  49. package/Server/Services/UserNotificationEmailRollupSettingService.ts +113 -0
  50. package/Server/Services/UserNotificationSettingService.ts +62 -25
  51. package/Server/Services/UserService.ts +95 -0
  52. package/Server/Types/Database/QueryHelper.ts +2 -2
  53. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +14 -3
  54. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +10 -0
  55. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +10 -0
  56. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +62 -1
  57. package/Server/Utils/AI/Toolbox/AlertTools.ts +15 -1
  58. package/Server/Utils/Billing/BillingFailureNoticeThrottle.ts +80 -0
  59. package/Server/Utils/DataSource/HttpFetch.ts +9 -2
  60. package/Server/Utils/EmailRollup/EmailRollupConstants.ts +98 -0
  61. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +872 -0
  62. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +781 -0
  63. package/Server/Utils/EmailRollup/EmailRollupWriter.ts +368 -0
  64. package/Server/Utils/LLM/LLMService.ts +78 -0
  65. package/Server/Utils/LogRedaction.ts +8 -0
  66. package/Server/Utils/Logger.ts +79 -1
  67. package/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.ts +190 -0
  68. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +9 -2
  69. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +61 -1
  70. package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +14 -0
  71. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +49 -0
  72. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +255 -4
  73. package/Server/Utils/Monitor/MonitorMetricUtil.ts +41 -0
  74. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +43 -0
  75. package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +499 -19
  76. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +66 -10
  77. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +238 -44
  78. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +146 -39
  79. package/Server/Utils/NetworkDevice/DeviceHealthAggregation.ts +31 -18
  80. package/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.ts +213 -0
  81. package/Server/Utils/OutboundUserAgent.ts +152 -0
  82. package/Server/Utils/StartServer.ts +51 -19
  83. package/Server/Utils/Telemetry/CaptureSpan.ts +32 -8
  84. package/Server/Utils/Telemetry/ErrorClassResolver.ts +120 -0
  85. package/Server/Utils/Telemetry/SpanUtil.ts +33 -0
  86. package/Server/Utils/Telemetry.ts +198 -51
  87. package/Server/Views/Partials/SensitiveUrlToken.ejs +140 -0
  88. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +134 -33
  89. package/Tests/App/Dashboard/AskAiSuggestedPrompts.test.ts +279 -0
  90. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +517 -0
  91. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +26 -0
  92. package/Tests/App/Dashboard/DeviceStatusHero.test.tsx +588 -0
  93. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +18 -9
  94. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +632 -0
  95. package/Tests/App/Dashboard/MonitorRecommendationCreateProgress.test.tsx +682 -0
  96. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +4 -2
  97. package/Tests/App/Dashboard/NetworkDeviceCreateFormMonitorBinding.test.tsx +676 -0
  98. package/Tests/App/Dashboard/NetworkDeviceCreateFormPingMonitor.test.tsx +791 -0
  99. package/Tests/App/Dashboard/NetworkDeviceCreateFormProbeAndSnmp.test.tsx +833 -0
  100. package/Tests/App/Dashboard/NetworkDeviceCriteriaFilter.test.ts +161 -0
  101. package/Tests/App/Dashboard/NetworkDeviceSettingsMonitoringSection.test.tsx +458 -0
  102. package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +41 -5
  103. package/Tests/App/Dashboard/PingMonitorProvisioning.test.ts +368 -0
  104. package/Tests/App/Dashboard/RecommendationCard.test.tsx +796 -0
  105. package/Tests/App/Dashboard/RecommendationToolbar.test.tsx +519 -0
  106. package/Tests/App/Dashboard/RecommendationsList.test.tsx +661 -0
  107. package/Tests/App/Dashboard/UseBulkCreatePingMonitors.test.tsx +899 -0
  108. package/Tests/App/Dashboard/UseBulkSnmpCredentialProfileActions.test.tsx +732 -0
  109. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +256 -0
  110. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +11 -1
  111. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +9 -4
  112. package/Tests/Models/NetworkSiteHierarchy.test.ts +41 -4
  113. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +951 -7
  114. package/Tests/Server/API/LlmProviderConnectionTest.test.ts +522 -0
  115. package/Tests/Server/Infrastructure/Postgres/NetworkSnmpCredentialProfilesAndAlertPoliciesMigration.test.ts +854 -0
  116. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +18 -0
  117. package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +535 -0
  118. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +560 -0
  119. package/Tests/Server/Services/BillingRechargeOwnerEmailGuards.test.ts +783 -0
  120. package/Tests/Server/Services/EnterpriseLicenseInstanceServiceDeletionUsage.test.ts +707 -0
  121. package/Tests/Server/Services/EnterpriseLicenseServiceUsageAggregationLock.test.ts +329 -0
  122. package/Tests/Server/Services/MonitorServiceDeleteNetworkDeviceCleanup.test.ts +294 -0
  123. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +802 -0
  124. package/Tests/Server/Services/MonitorStatusTimelineService.test.ts +72 -2
  125. package/Tests/Server/Services/NetworkAlertPolicyEngineHooks.test.ts +752 -0
  126. package/Tests/Server/Services/NetworkAlertPolicyEngineService.test.ts +1849 -0
  127. package/Tests/Server/Services/NetworkAlertPolicyModel.test.ts +1793 -0
  128. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +47 -4
  129. package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +97 -12
  130. package/Tests/Server/Services/NetworkDeviceAutoMonitorLifecycle.test.ts +148 -3
  131. package/Tests/Server/Services/NetworkDeviceMonitorBindingUpdateGuard.test.ts +514 -0
  132. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +447 -58
  133. package/Tests/Server/Services/NetworkDeviceMonitoringMethodTransition.test.ts +739 -0
  134. package/Tests/Server/Services/NetworkDevicePollingTenancy.test.ts +501 -0
  135. package/Tests/Server/Services/NetworkDeviceSiteDefaultProbe.test.ts +432 -0
  136. package/Tests/Server/Services/NetworkSiteMonitoringDefaults.test.ts +498 -0
  137. package/Tests/Server/Services/NetworkSiteRollupPolicyAndMaintenance.test.ts +337 -55
  138. package/Tests/Server/Services/NetworkSiteService.test.ts +1868 -131
  139. package/Tests/Server/Services/NetworkSiteTypeService.test.ts +1109 -0
  140. package/Tests/Server/Services/NetworkSnmpCredentialProfileModel.test.ts +926 -0
  141. package/Tests/Server/Services/NetworkSnmpCredentialProfileService.test.ts +796 -0
  142. package/Tests/Server/Services/ProjectServiceNetworkSiteTypeDefaults.test.ts +214 -0
  143. package/Tests/Server/Services/StatusPagePrivateUserEmailChangePasswordResetExpiry.test.ts +269 -0
  144. package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +472 -0
  145. package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +569 -0
  146. package/Tests/Server/Services/UserNotificationEmailRollupSettingModel.test.ts +314 -0
  147. package/Tests/Server/Services/UserNotificationEmailRollupSettingService.test.ts +409 -0
  148. package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +856 -0
  149. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +102 -1
  150. package/Tests/Server/Types/Database/Permissions/DashboardAccessPermission.test.ts +321 -0
  151. package/Tests/Server/Types/Database/QueryHelperOperators.test.ts +17 -0
  152. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +124 -0
  153. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +208 -0
  154. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +235 -0
  155. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +253 -0
  156. package/Tests/Server/Utils/Captcha.test.ts +422 -0
  157. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +96 -2
  158. package/Tests/Server/Utils/Database/MigrationFailureLog.test.ts +466 -0
  159. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1096 -0
  160. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerClaim.test.ts +466 -0
  161. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +1428 -0
  162. package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +752 -0
  163. package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +864 -0
  164. package/Tests/Server/Utils/LoggerFaultDemotion.test.ts +346 -0
  165. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +633 -0
  166. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +196 -0
  167. package/Tests/Server/Utils/Monitor/DatabaseMetricWrite.test.ts +367 -0
  168. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +100 -0
  169. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +5 -0
  170. package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +512 -31
  171. package/Tests/Server/Utils/Monitor/NetworkDeviceMetricUtil.test.ts +252 -0
  172. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +479 -42
  173. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +351 -5
  174. package/Tests/Server/Utils/NetworkDevice/DeviceHealthAggregation.test.ts +136 -12
  175. package/Tests/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.test.ts +306 -0
  176. package/Tests/Server/Utils/OutboundUserAgent.test.ts +266 -0
  177. package/Tests/Server/Utils/SecurityEvent/ThreatIntel/TaxiiClientUserAgent.test.ts +141 -0
  178. package/Tests/Server/Utils/Telemetry/ErrorClassResolver.test.ts +298 -0
  179. package/Tests/Server/Utils/Telemetry.test.ts +456 -66
  180. package/Tests/Types/Monitor/DatabaseMetricCatalog.test.ts +388 -0
  181. package/Tests/Types/Monitor/MonitorStepDatabaseMonitor.test.ts +243 -0
  182. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
  183. package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
  184. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +16 -7
  185. package/Tests/Types/Monitor/NetworkDeviceAlertPack.test.ts +60 -0
  186. package/Tests/Types/NetworkDevice/NetworkAlertPolicyScope.test.ts +655 -0
  187. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +115 -26
  188. package/Tests/Types/NetworkSite/DefaultNetworkSiteType.test.ts +39 -0
  189. package/Tests/Types/NotificationSetting/NotificationEmailRollupPolicy.test.ts +299 -0
  190. package/Tests/Types/Telemetry/ErrorClass.test.ts +483 -0
  191. package/Tests/UI/Components/BasicRadioButtons.test.tsx +336 -0
  192. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +15 -0
  193. package/Tests/UI/Components/ProgressBar.test.tsx +38 -2
  194. package/Tests/UI/Components/SideMenuItem.test.tsx +226 -4
  195. package/Tests/UI/Components/SideMenuSection.test.tsx +310 -0
  196. package/Tests/UI/Components/SideOverSubmitState.test.tsx +335 -0
  197. package/Tests/UI/Utils/PageScrollLock.test.tsx +377 -0
  198. package/Tests/UI/Utils/PermissionGate.test.ts +78 -0
  199. package/Tests/UI/Utils/SensitiveUrlToken.test.ts +140 -0
  200. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +69 -0
  201. package/Tests/Utils/EnterpriseLicenseSync.test.ts +7 -1
  202. package/Tests/Utils/EnterpriseLicenseUsage.test.ts +433 -2
  203. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +280 -1
  204. package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
  205. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +38 -0
  206. package/Tests/Utils/NetworkDevice/DeviceHealthStateUtil.test.ts +284 -23
  207. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +177 -0
  208. package/Tests/Utils/NetworkDevice/MonitoringMethodThreadedClassifiers.test.ts +992 -0
  209. package/Tests/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.test.ts +427 -0
  210. package/Tests/Utils/NetworkDevice/ReachabilityStampConsistency.test.ts +185 -0
  211. package/Tests/Utils/NetworkDevice/SnmpCredentialUtil.test.ts +268 -0
  212. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +55 -5
  213. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +98 -21
  214. package/Tests/Utils/NetworkDiscovery/DiscoveryImportEligibility.test.ts +98 -20
  215. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +8 -7
  216. package/Tests/Utils/NetworkDiscovery/PingMonitorBuilderForAddress.test.ts +274 -0
  217. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +230 -51
  218. package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +209 -0
  219. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +145 -0
  220. package/Types/AI/ExceptionAIClassification.ts +10 -24
  221. package/Types/Email/EmailTemplateType.ts +2 -0
  222. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +5 -0
  223. package/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.ts +13 -0
  224. package/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.ts +6 -0
  225. package/Types/Exception/ApiException.ts +10 -0
  226. package/Types/Exception/BadDataException.ts +11 -0
  227. package/Types/Exception/BadRequestException.ts +11 -0
  228. package/Types/Exception/Exception.ts +65 -0
  229. package/Types/Exception/ForbiddenException.ts +10 -0
  230. package/Types/Exception/NotAuthenticatedException.ts +10 -0
  231. package/Types/Exception/NotAuthorizedException.ts +10 -0
  232. package/Types/Exception/NotFoundException.ts +11 -0
  233. package/Types/Exception/PayloadTooLargeException.ts +11 -0
  234. package/Types/Exception/PaymentRequiredException.ts +10 -0
  235. package/Types/Exception/ServiceUnavailableException.ts +10 -0
  236. package/Types/Exception/SsoAuthorizationException.ts +10 -0
  237. package/Types/Exception/TenantNotFoundException.ts +11 -0
  238. package/Types/Exception/TimeoutException.ts +10 -0
  239. package/Types/Exception/TooManyRequestsException.ts +10 -0
  240. package/Types/Exception/UnableToReachServer.ts +10 -0
  241. package/Types/Exception/WebsiteRequestException.ts +10 -0
  242. package/Types/Monitor/CriteriaFilter.ts +60 -2
  243. package/Types/Monitor/DatabaseMetricCatalog.ts +693 -0
  244. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +83 -0
  245. package/Types/Monitor/MonitorCriteriaInstance.ts +76 -0
  246. package/Types/Monitor/MonitorMetricType.ts +71 -0
  247. package/Types/Monitor/MonitorStep.ts +76 -0
  248. package/Types/Monitor/MonitorStepDatabaseMonitor.ts +171 -0
  249. package/Types/Monitor/MonitorStepSqlMonitor.ts +2 -20
  250. package/Types/Monitor/MonitorType.ts +47 -1
  251. package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +22 -1
  252. package/Types/Monitor/SqlConnectionConfig.ts +31 -0
  253. package/Types/NetworkDevice/NetworkAlertPolicyScope.ts +343 -0
  254. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +42 -22
  255. package/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.ts +30 -0
  256. package/Types/NotificationSetting/NotificationEmailRollupCategory.ts +255 -0
  257. package/Types/NotificationSetting/NotificationEmailRollupPolicy.ts +104 -0
  258. package/Types/Permission.ts +90 -0
  259. package/Types/Probe/ProbeMonitorResponse.ts +16 -0
  260. package/Types/Telemetry/ErrorClass.ts +274 -0
  261. package/Types/Telemetry/UnitOfWork.ts +59 -0
  262. package/UI/Components/CommandPalette/CommandPalette.tsx +2 -8
  263. package/UI/Components/EditionLabel/EditionLabel.tsx +4 -0
  264. package/UI/Components/Modal/Modal.tsx +2 -8
  265. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +44 -0
  266. package/UI/Components/ProgressBar/ProgressBar.tsx +12 -1
  267. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +37 -7
  268. package/UI/Components/SideMenu/SideMenu.tsx +123 -5
  269. package/UI/Components/SideMenu/SideMenuItem.tsx +76 -84
  270. package/UI/Components/SideMenu/SideMenuSection.tsx +55 -23
  271. package/UI/Components/SideOver/SideOver.tsx +21 -20
  272. package/UI/Utils/PageScrollLock.ts +79 -3
  273. package/UI/Utils/PermissionGate.ts +61 -0
  274. package/UI/Utils/SensitiveUrlToken.ts +112 -0
  275. package/UI/Utils/TestLLMProvider.ts +11 -0
  276. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +16 -0
  277. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +191 -11
  278. package/Utils/Monitor/MonitorMetricType.ts +153 -1
  279. package/Utils/Monitor/NetworkTopologyUtil.ts +23 -3
  280. package/Utils/NetworkDevice/DeviceHealthStateUtil.ts +65 -14
  281. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +43 -14
  282. package/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.ts +247 -0
  283. package/Utils/NetworkDevice/SnmpCredentialUtil.ts +68 -0
  284. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +15 -7
  285. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +52 -17
  286. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +55 -23
  287. package/Utils/NetworkDiscovery/PingMonitorBuilder.ts +115 -13
  288. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +66 -20
  289. package/Utils/NetworkSite/TypeHierarchyUtil.ts +313 -0
  290. package/build/dist/Models/AnalyticsModels/Span.js +14 -0
  291. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  292. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +59 -0
  293. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  294. package/build/dist/Models/DatabaseModels/Index.js +10 -0
  295. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  296. package/build/dist/Models/DatabaseModels/Monitor.js +75 -0
  297. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  298. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js +713 -0
  299. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js.map +1 -0
  300. package/build/dist/Models/DatabaseModels/NetworkDevice.js +180 -1
  301. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  302. package/build/dist/Models/DatabaseModels/NetworkSite.js +200 -2
  303. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  304. package/build/dist/Models/DatabaseModels/NetworkSiteType.js +102 -3
  305. package/build/dist/Models/DatabaseModels/NetworkSiteType.js.map +1 -1
  306. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js +878 -0
  307. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js.map +1 -0
  308. package/build/dist/Models/DatabaseModels/TelemetryException.js +123 -0
  309. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  310. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js +363 -0
  311. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js.map +1 -0
  312. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +402 -0
  313. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -0
  314. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js +296 -0
  315. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js.map +1 -0
  316. package/build/dist/Server/API/EnterpriseLicenseAPI.js +273 -60
  317. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  318. package/build/dist/Server/API/LlmProviderAPI.js +108 -13
  319. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  320. package/build/dist/Server/EnvironmentConfig.js +12 -0
  321. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  322. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js +24 -0
  323. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js.map +1 -0
  324. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js +16 -0
  325. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js.map +1 -0
  326. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js +111 -0
  327. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js.map +1 -0
  328. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js +77 -0
  329. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js.map +1 -0
  330. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js +50 -0
  331. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js.map +1 -0
  332. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js +83 -0
  333. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js.map +1 -0
  334. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  335. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  336. package/build/dist/Server/Infrastructure/QueueWorker.js +33 -5
  337. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  338. package/build/dist/Server/Middleware/BearerTokenAuthorization.js +12 -0
  339. package/build/dist/Server/Middleware/BearerTokenAuthorization.js.map +1 -1
  340. package/build/dist/Server/Middleware/ProjectAuthorization.js +11 -0
  341. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/SCIMAuthorization.js +12 -0
  343. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +1 -1
  344. package/build/dist/Server/Middleware/TelemetryIngest.js +11 -0
  345. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  346. package/build/dist/Server/Services/AIBillingService.js +70 -16
  347. package/build/dist/Server/Services/AIBillingService.js.map +1 -1
  348. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +167 -0
  349. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -1
  350. package/build/dist/Server/Services/EnterpriseLicenseService.js +39 -0
  351. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  352. package/build/dist/Server/Services/Index.js +10 -0
  353. package/build/dist/Server/Services/Index.js.map +1 -1
  354. package/build/dist/Server/Services/MonitorService.js +119 -21
  355. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  356. package/build/dist/Server/Services/MonitorStatusTimelineService.js +127 -8
  357. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  358. package/build/dist/Server/Services/MonitorTemplateService.js +162 -0
  359. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  360. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js +1683 -0
  361. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js.map +1 -0
  362. package/build/dist/Server/Services/NetworkAlertPolicyService.js +732 -0
  363. package/build/dist/Server/Services/NetworkAlertPolicyService.js.map +1 -0
  364. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +10 -9
  365. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  366. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +54 -11
  367. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  368. package/build/dist/Server/Services/NetworkDeviceService.js +841 -70
  369. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  370. package/build/dist/Server/Services/NetworkSiteService.js +1145 -107
  371. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  372. package/build/dist/Server/Services/NetworkSiteTypeService.js +863 -0
  373. package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
  374. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js +217 -0
  375. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js.map +1 -0
  376. package/build/dist/Server/Services/NotificationService.js +80 -17
  377. package/build/dist/Server/Services/NotificationService.js.map +1 -1
  378. package/build/dist/Server/Services/ProjectService.js +66 -30
  379. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  380. package/build/dist/Server/Services/StatusPagePrivateUserService.js +92 -2
  381. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  382. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js +76 -0
  383. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js.map +1 -0
  384. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js +77 -0
  385. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js.map +1 -0
  386. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js +111 -0
  387. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js.map +1 -0
  388. package/build/dist/Server/Services/UserNotificationSettingService.js +46 -20
  389. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  390. package/build/dist/Server/Services/UserService.js +90 -0
  391. package/build/dist/Server/Services/UserService.js.map +1 -1
  392. package/build/dist/Server/Types/Database/QueryHelper.js +1 -1
  393. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  394. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +14 -3
  395. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  396. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +10 -0
  397. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  398. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +10 -0
  399. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  400. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +54 -1
  401. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  402. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +18 -4
  403. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  404. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js +59 -0
  405. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js.map +1 -0
  406. package/build/dist/Server/Utils/DataSource/HttpFetch.js +2 -1
  407. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  408. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +91 -0
  409. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -0
  410. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +691 -0
  411. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -0
  412. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +497 -0
  413. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -0
  414. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +257 -0
  415. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -0
  416. package/build/dist/Server/Utils/LLM/LLMService.js +64 -1
  417. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  418. package/build/dist/Server/Utils/LogRedaction.js +8 -0
  419. package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
  420. package/build/dist/Server/Utils/Logger.js +63 -1
  421. package/build/dist/Server/Utils/Logger.js.map +1 -1
  422. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js +141 -0
  423. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js.map +1 -0
  424. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  425. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  426. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +51 -1
  427. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  428. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +7 -0
  429. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
  430. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +37 -0
  431. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  432. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +158 -6
  433. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  434. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  435. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  436. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +32 -0
  437. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  438. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +330 -21
  439. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
  440. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +55 -10
  441. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  442. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +178 -49
  443. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  444. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +119 -29
  445. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  446. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js +30 -17
  447. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js.map +1 -1
  448. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js +158 -0
  449. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js.map +1 -0
  450. package/build/dist/Server/Utils/OutboundUserAgent.js +123 -0
  451. package/build/dist/Server/Utils/OutboundUserAgent.js.map +1 -0
  452. package/build/dist/Server/Utils/StartServer.js +38 -14
  453. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  454. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +28 -8
  455. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  456. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js +95 -0
  457. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js.map +1 -0
  458. package/build/dist/Server/Utils/Telemetry/SpanUtil.js +29 -0
  459. package/build/dist/Server/Utils/Telemetry/SpanUtil.js.map +1 -1
  460. package/build/dist/Server/Utils/Telemetry.js +162 -46
  461. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  462. package/build/dist/Types/AI/ExceptionAIClassification.js +10 -24
  463. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -1
  464. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  465. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  466. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js +2 -0
  467. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js.map +1 -0
  468. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js +7 -0
  469. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js.map +1 -0
  470. package/build/dist/Types/Exception/ApiException.js +9 -0
  471. package/build/dist/Types/Exception/ApiException.js.map +1 -1
  472. package/build/dist/Types/Exception/BadDataException.js +10 -0
  473. package/build/dist/Types/Exception/BadDataException.js.map +1 -1
  474. package/build/dist/Types/Exception/BadRequestException.js +10 -0
  475. package/build/dist/Types/Exception/BadRequestException.js.map +1 -1
  476. package/build/dist/Types/Exception/Exception.js +56 -0
  477. package/build/dist/Types/Exception/Exception.js.map +1 -1
  478. package/build/dist/Types/Exception/ForbiddenException.js +9 -0
  479. package/build/dist/Types/Exception/ForbiddenException.js.map +1 -1
  480. package/build/dist/Types/Exception/NotAuthenticatedException.js +9 -0
  481. package/build/dist/Types/Exception/NotAuthenticatedException.js.map +1 -1
  482. package/build/dist/Types/Exception/NotAuthorizedException.js +9 -0
  483. package/build/dist/Types/Exception/NotAuthorizedException.js.map +1 -1
  484. package/build/dist/Types/Exception/NotFoundException.js +10 -0
  485. package/build/dist/Types/Exception/NotFoundException.js.map +1 -1
  486. package/build/dist/Types/Exception/PayloadTooLargeException.js +10 -0
  487. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -1
  488. package/build/dist/Types/Exception/PaymentRequiredException.js +9 -0
  489. package/build/dist/Types/Exception/PaymentRequiredException.js.map +1 -1
  490. package/build/dist/Types/Exception/ServiceUnavailableException.js +9 -0
  491. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -1
  492. package/build/dist/Types/Exception/SsoAuthorizationException.js +9 -0
  493. package/build/dist/Types/Exception/SsoAuthorizationException.js.map +1 -1
  494. package/build/dist/Types/Exception/TenantNotFoundException.js +10 -0
  495. package/build/dist/Types/Exception/TenantNotFoundException.js.map +1 -1
  496. package/build/dist/Types/Exception/TimeoutException.js +9 -0
  497. package/build/dist/Types/Exception/TimeoutException.js.map +1 -1
  498. package/build/dist/Types/Exception/TooManyRequestsException.js +9 -0
  499. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -1
  500. package/build/dist/Types/Exception/UnableToReachServer.js +9 -0
  501. package/build/dist/Types/Exception/UnableToReachServer.js.map +1 -1
  502. package/build/dist/Types/Exception/WebsiteRequestException.js +9 -0
  503. package/build/dist/Types/Exception/WebsiteRequestException.js.map +1 -1
  504. package/build/dist/Types/Monitor/CriteriaFilter.js +45 -2
  505. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  506. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js +594 -0
  507. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js.map +1 -0
  508. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js +21 -0
  509. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js.map +1 -0
  510. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +70 -0
  511. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  512. package/build/dist/Types/Monitor/MonitorMetricType.js +61 -0
  513. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  514. package/build/dist/Types/Monitor/MonitorStep.js +54 -0
  515. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  516. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js +122 -0
  517. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js.map +1 -0
  518. package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js.map +1 -1
  519. package/build/dist/Types/Monitor/MonitorType.js +45 -1
  520. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  521. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +21 -1
  522. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -1
  523. package/build/dist/Types/Monitor/SqlConnectionConfig.js +2 -0
  524. package/build/dist/Types/Monitor/SqlConnectionConfig.js.map +1 -0
  525. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js +172 -0
  526. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js.map +1 -0
  527. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +40 -22
  528. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -1
  529. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js +26 -0
  530. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js.map +1 -0
  531. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js +186 -0
  532. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js.map +1 -0
  533. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js +87 -0
  534. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js.map +1 -0
  535. package/build/dist/Types/Permission.js +80 -0
  536. package/build/dist/Types/Permission.js.map +1 -1
  537. package/build/dist/Types/Telemetry/ErrorClass.js +216 -0
  538. package/build/dist/Types/Telemetry/ErrorClass.js.map +1 -0
  539. package/build/dist/Types/Telemetry/UnitOfWork.js +59 -0
  540. package/build/dist/Types/Telemetry/UnitOfWork.js.map +1 -0
  541. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +2 -7
  542. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  543. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +3 -0
  544. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  545. package/build/dist/UI/Components/Modal/Modal.js +2 -7
  546. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  547. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +39 -0
  548. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  549. package/build/dist/UI/Components/ProgressBar/ProgressBar.js +12 -1
  550. package/build/dist/UI/Components/ProgressBar/ProgressBar.js.map +1 -1
  551. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +6 -6
  552. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  553. package/build/dist/UI/Components/SideMenu/SideMenu.js +67 -5
  554. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  555. package/build/dist/UI/Components/SideMenu/SideMenuItem.js +49 -53
  556. package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
  557. package/build/dist/UI/Components/SideMenu/SideMenuSection.js +25 -7
  558. package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
  559. package/build/dist/UI/Components/SideOver/SideOver.js +5 -20
  560. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  561. package/build/dist/UI/Utils/PageScrollLock.js +31 -3
  562. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -1
  563. package/build/dist/UI/Utils/PermissionGate.js +42 -1
  564. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  565. package/build/dist/UI/Utils/SensitiveUrlToken.js +97 -0
  566. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -0
  567. package/build/dist/UI/Utils/TestLLMProvider.js +4 -5
  568. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -1
  569. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +15 -0
  570. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -1
  571. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +111 -9
  572. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  573. package/build/dist/Utils/Monitor/MonitorMetricType.js +109 -1
  574. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  575. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +8 -3
  576. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  577. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js +27 -7
  578. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js.map +1 -1
  579. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +7 -5
  580. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  581. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js +180 -0
  582. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js.map +1 -0
  583. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js +12 -0
  584. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js.map +1 -0
  585. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +15 -5
  586. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  587. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +44 -15
  588. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  589. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +52 -23
  590. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -1
  591. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js +85 -13
  592. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js.map +1 -1
  593. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +30 -15
  594. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  595. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +191 -0
  596. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -0
  597. package/package.json +2 -2
@@ -13,6 +13,10 @@ import { JSONObject } from "../../../Types/JSON";
13
13
  import EnterpriseLicenseSyncUtil, {
14
14
  EnterpriseLicenseSyncResult,
15
15
  } from "../../../Utils/EnterpriseLicense/EnterpriseLicenseSync";
16
+ import EnterpriseLicenseUsageUtil from "../../../Utils/EnterpriseLicense/EnterpriseLicenseUsage";
17
+ import EnterpriseLicenseUserCountSource from "../../../Types/EnterpriseLicense/EnterpriseLicenseUserCountSource";
18
+ import EnterpriseLicenseInstanceSummary from "../../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
19
+ import MasterAdminAuthorization from "../../../Server/Middleware/MasterAdminAuthorization";
16
20
  import {
17
21
  NextFunction,
18
22
  OneUptimeRequest,
@@ -117,6 +121,8 @@ jest.mock("../../../Server/Services/EnterpriseLicenseInstanceService");
117
121
 
118
122
  const REPORT_ROUTE: string = "/enterprise-license/report-user-count";
119
123
  const VALIDATE_ROUTE: string = "/enterprise-license/validate";
124
+ const ACTIVE_USAGE_ROUTE: string =
125
+ "/enterprise-license/:enterpriseLicenseId/active-usage";
120
126
 
121
127
  const LICENSE_ID: ObjectID = ObjectID.generate();
122
128
  const LICENSE_KEY: string = "acme-license-key";
@@ -160,6 +166,12 @@ type MakeInstanceFunction = (
160
166
  overrides?: Record<string, unknown>,
161
167
  ) => EnterpriseLicenseInstance;
162
168
 
169
+ interface LicenseUsageReportResultForTest {
170
+ reportedAt: Date;
171
+ instances: Array<EnterpriseLicenseInstance>;
172
+ currentUserCount: number;
173
+ }
174
+
163
175
  const makeInstance: MakeInstanceFunction = (
164
176
  overrides?: Record<string, unknown>,
165
177
  ): EnterpriseLicenseInstance => {
@@ -217,6 +229,13 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/report-user-count", () =
217
229
  EnterpriseLicenseService.updateOneById = jest
218
230
  .fn()
219
231
  .mockResolvedValue(undefined);
232
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
233
+ .fn()
234
+ .mockImplementation(
235
+ async (data: { fn: () => Promise<unknown> }): Promise<unknown> => {
236
+ return await data.fn();
237
+ },
238
+ );
220
239
 
221
240
  EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([]);
222
241
  EnterpriseLicenseInstanceService.findOneBy = jest
@@ -468,6 +487,149 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/report-user-count", () =
468
487
  }),
469
488
  }),
470
489
  );
490
+ expect(
491
+ EnterpriseLicenseService.runWithUsageAggregationLock,
492
+ ).toHaveBeenCalledWith(
493
+ expect.objectContaining({
494
+ licenseId: LICENSE_ID,
495
+ fn: expect.any(Function),
496
+ }),
497
+ );
498
+ });
499
+
500
+ it("serializes concurrent reports so an older partial snapshot cannot win", async () => {
501
+ const instanceRows: Array<EnterpriseLicenseInstance> = [];
502
+ const events: Array<string> = [];
503
+ let lockTail: Promise<void> = Promise.resolve();
504
+
505
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
506
+ .fn()
507
+ .mockImplementation(
508
+ async (data: {
509
+ fn: () => Promise<LicenseUsageReportResultForTest>;
510
+ }): Promise<LicenseUsageReportResultForTest> => {
511
+ const predecessor: Promise<void> = lockTail;
512
+ let releaseLock: () => void = (): void => {};
513
+ lockTail = new Promise<void>((resolve: () => void) => {
514
+ releaseLock = resolve;
515
+ });
516
+
517
+ await predecessor;
518
+ try {
519
+ return await data.fn();
520
+ } finally {
521
+ releaseLock();
522
+ }
523
+ },
524
+ );
525
+ EnterpriseLicenseInstanceService.findOneBy = jest
526
+ .fn()
527
+ .mockImplementation(
528
+ async (args: {
529
+ query: { instanceId: string };
530
+ }): Promise<EnterpriseLicenseInstance | null> => {
531
+ return (
532
+ instanceRows.find(
533
+ (instance: EnterpriseLicenseInstance): boolean => {
534
+ return instance.instanceId === args.query.instanceId;
535
+ },
536
+ ) || null
537
+ );
538
+ },
539
+ );
540
+ EnterpriseLicenseInstanceService.create = jest
541
+ .fn()
542
+ .mockImplementation(
543
+ async (args: {
544
+ data: EnterpriseLicenseInstance;
545
+ }): Promise<EnterpriseLicenseInstance> => {
546
+ instanceRows.push(args.data);
547
+ events.push(`upsert:${args.data.instanceId}`);
548
+ await Promise.resolve();
549
+ return args.data;
550
+ },
551
+ );
552
+ EnterpriseLicenseInstanceService.findBy = jest
553
+ .fn()
554
+ .mockImplementation(
555
+ async (): Promise<Array<EnterpriseLicenseInstance>> => {
556
+ events.push(
557
+ `read:${instanceRows
558
+ .map((instance: EnterpriseLicenseInstance): string => {
559
+ return instance.instanceId || "";
560
+ })
561
+ .join(",")}`,
562
+ );
563
+ await Promise.resolve();
564
+ return [...instanceRows];
565
+ },
566
+ );
567
+ EnterpriseLicenseService.updateOneById = jest
568
+ .fn()
569
+ .mockImplementation(
570
+ async (args: {
571
+ data: { currentUserCount: number };
572
+ }): Promise<number> => {
573
+ events.push(`write:${args.data.currentUserCount}`);
574
+ await Promise.resolve();
575
+ return 1;
576
+ },
577
+ );
578
+
579
+ const makeConcurrentRequest: (
580
+ instanceId: string,
581
+ hash: string,
582
+ ) => OneUptimeRequest = (
583
+ instanceId: string,
584
+ hash: string,
585
+ ): OneUptimeRequest => {
586
+ return {
587
+ body: {
588
+ licenseKey: LICENSE_KEY,
589
+ userCount: 1,
590
+ instanceId,
591
+ userEmailHashes: [hash],
592
+ },
593
+ } as unknown as OneUptimeRequest;
594
+ };
595
+ const firstNext: NextFunction = jest.fn();
596
+ const secondNext: NextFunction = jest.fn();
597
+ const route: ReturnType<typeof mockRouter.match> = mockRouter.match(
598
+ "post",
599
+ REPORT_ROUTE,
600
+ );
601
+
602
+ await Promise.all([
603
+ route.handlerFunction(
604
+ makeConcurrentRequest("instance-a", hashOf("a")),
605
+ mockResponse,
606
+ firstNext,
607
+ ),
608
+ route.handlerFunction(
609
+ makeConcurrentRequest("instance-b", hashOf("b")),
610
+ mockResponse,
611
+ secondNext,
612
+ ),
613
+ ]);
614
+
615
+ expect(firstNext).not.toHaveBeenCalled();
616
+ expect(secondNext).not.toHaveBeenCalled();
617
+ expect(events).toEqual([
618
+ "upsert:instance-a",
619
+ "read:instance-a",
620
+ "write:1",
621
+ "upsert:instance-b",
622
+ "read:instance-a,instance-b",
623
+ "write:2",
624
+ ]);
625
+ expect(
626
+ (
627
+ EnterpriseLicenseService.updateOneById as unknown as jest.Mock
628
+ ).mock.calls.map((call: Array<unknown>): number => {
629
+ return (call[0] as { data: { currentUserCount: number } }).data
630
+ .currentUserCount;
631
+ }),
632
+ ).toEqual([1, 2]);
471
633
  });
472
634
 
473
635
  it("counts a user on two instances once", async () => {
@@ -521,23 +683,36 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/report-user-count", () =
521
683
  expect(Object.keys(updateCall["data"] as JSONObject)).toEqual([
522
684
  "currentUserCount",
523
685
  "userCountUpdatedAt",
686
+ "userCountSource",
524
687
  ]);
688
+ expect((updateCall["data"] as JSONObject)["userCountSource"]).toBe(
689
+ EnterpriseLicenseUserCountSource.Instance,
690
+ );
525
691
  });
526
692
 
527
- it("leaves a decommissioned instance out of the seat count but still lists it", async () => {
693
+ it("marks a week-silent instance inactive, excludes its users, and still lists it", async () => {
694
+ const reportedAt: Date = new Date("2026-09-02T12:00:00.000Z");
695
+
696
+ jest.spyOn(OneUptimeDate, "getCurrentDate").mockReturnValue(reportedAt);
697
+
528
698
  EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
529
699
  makeInstance({
530
700
  instanceId: "live",
531
701
  userCount: 1,
532
702
  userEmailHashes: [hashOf("1")],
703
+ lastReportedAt: reportedAt,
533
704
  }),
534
705
  makeInstance({
535
- instanceId: "retired",
706
+ instanceId: "inactive",
536
707
  userCount: 2,
537
708
  userEmailHashes: [hashOf("2"), hashOf("3")],
538
- lastReportedAt: OneUptimeDate.addRemoveDays(
539
- OneUptimeDate.getCurrentDate(),
540
- -400,
709
+ lastReportedAt: new Date(
710
+ reportedAt.getTime() -
711
+ EnterpriseLicenseUsageUtil.InstanceUsageFreshnessInDays *
712
+ 24 *
713
+ 60 *
714
+ 60 *
715
+ 1000,
541
716
  ),
542
717
  }),
543
718
  ]);
@@ -548,6 +723,25 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/report-user-count", () =
548
723
 
549
724
  expect(body["currentUserCount"]).toBe(1);
550
725
  expect(body["instances"]).toHaveLength(2);
726
+ expect(body["instances"]).toEqual(
727
+ expect.arrayContaining([
728
+ expect.objectContaining({
729
+ instanceId: "live",
730
+ isCountedTowardsUsage: true,
731
+ }),
732
+ expect.objectContaining({
733
+ instanceId: "inactive",
734
+ userCount: 2,
735
+ isCountedTowardsUsage: false,
736
+ }),
737
+ ]),
738
+ );
739
+ expect(EnterpriseLicenseService.updateOneById).toHaveBeenCalledWith(
740
+ expect.objectContaining({
741
+ id: LICENSE_ID,
742
+ data: expect.objectContaining({ currentUserCount: 1 }),
743
+ }),
744
+ );
551
745
  });
552
746
 
553
747
  it("counts users an instance could not send hashes for, so a huge install is not undercounted", async () => {
@@ -569,7 +763,7 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/report-user-count", () =
569
763
  expect(getResponseBody()["currentUserCount"]).toBe(10);
570
764
  });
571
765
 
572
- it("does not let a legacy report with no instance id stomp the deduplicated count", async () => {
766
+ it("retains a legacy heartbeat without letting it stomp the active modern count", async () => {
573
767
  mockRequest.body["instanceId"] = undefined;
574
768
  mockRequest.body["userCount"] = 99;
575
769
 
@@ -580,8 +774,541 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/report-user-count", () =
580
774
  await callRoute();
581
775
 
582
776
  expect(getResponseBody()["currentUserCount"]).toBe(2);
583
- expect(EnterpriseLicenseService.updateOneById).not.toHaveBeenCalled();
777
+ expect(EnterpriseLicenseService.updateOneById).toHaveBeenCalledWith(
778
+ expect.objectContaining({
779
+ id: LICENSE_ID,
780
+ data: {
781
+ legacyUserCount: 99,
782
+ legacyUserCountUpdatedAt: expect.any(Date),
783
+ },
784
+ }),
785
+ );
786
+ });
787
+
788
+ it("accepts a live legacy report after every modern instance becomes inactive", async () => {
789
+ const reportedAt: Date = new Date("2026-09-02T12:00:00.000Z");
790
+ jest.spyOn(OneUptimeDate, "getCurrentDate").mockReturnValue(reportedAt);
791
+ mockRequest.body["instanceId"] = undefined;
792
+ mockRequest.body["userCount"] = 19;
793
+
794
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
795
+ makeInstance({
796
+ lastReportedAt: OneUptimeDate.addRemoveDays(reportedAt, -7),
797
+ userCount: 50,
798
+ }),
799
+ ]);
800
+
801
+ await callRoute();
802
+
803
+ expect(getResponseBody()["currentUserCount"]).toBe(19);
804
+ expect(EnterpriseLicenseService.updateOneById).toHaveBeenCalledWith(
805
+ expect.objectContaining({
806
+ data: expect.objectContaining({
807
+ currentUserCount: 19,
808
+ legacyUserCount: 19,
809
+ legacyUserCountUpdatedAt: reportedAt,
810
+ userCountSource: EnterpriseLicenseUserCountSource.Legacy,
811
+ }),
812
+ }),
813
+ );
814
+ });
815
+
816
+ it("accepts a legacy report while the only tracked instance has registered but not reported usage", async () => {
817
+ mockRequest.body["instanceId"] = undefined;
818
+ mockRequest.body["userCount"] = 19;
819
+
820
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
821
+ makeInstance({
822
+ createdAt: OneUptimeDate.addRemoveDays(
823
+ OneUptimeDate.getCurrentDate(),
824
+ -1,
825
+ ),
826
+ lastReportedAt: undefined,
827
+ userCount: undefined,
828
+ userEmailHashes: undefined,
829
+ }),
830
+ ]);
831
+
832
+ await callRoute();
833
+
834
+ expect(getResponseBody()["currentUserCount"]).toBe(19);
835
+ expect(EnterpriseLicenseService.updateOneById).toHaveBeenCalledWith(
836
+ expect.objectContaining({
837
+ id: LICENSE_ID,
838
+ data: expect.objectContaining({ currentUserCount: 19 }),
839
+ }),
840
+ );
841
+ });
842
+ });
843
+ });
844
+
845
+ describe("EnterpriseLicenseAPI GET active usage snapshot", () => {
846
+ let mockRequest: OneUptimeRequest;
847
+ let mockResponse: OneUptimeResponse;
848
+ let nextFunction: NextFunction;
849
+ const calculatedAt: Date = new Date("2026-09-02T12:00:00.000Z");
850
+
851
+ beforeEach(() => {
852
+ jest.clearAllMocks();
853
+
854
+ new EnterpriseLicenseAPI();
855
+ jest.spyOn(OneUptimeDate, "getCurrentDate").mockReturnValue(calculatedAt);
856
+
857
+ EnterpriseLicenseService.findOneById = jest
858
+ .fn()
859
+ .mockResolvedValue(makeLicense({ userCountUpdatedAt: calculatedAt }));
860
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
861
+ .fn()
862
+ .mockImplementation(
863
+ async (data: { fn: () => Promise<unknown> }): Promise<unknown> => {
864
+ return await data.fn();
865
+ },
866
+ );
867
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([]);
868
+
869
+ mockRequest = {
870
+ params: {
871
+ enterpriseLicenseId: LICENSE_ID.toString(),
872
+ },
873
+ } as unknown as OneUptimeRequest;
874
+ mockResponse = {
875
+ send: jest.fn(),
876
+ json: jest.fn(),
877
+ status: jest.fn().mockReturnThis(),
878
+ } as unknown as OneUptimeResponse;
879
+ nextFunction = jest.fn();
880
+ });
881
+
882
+ afterEach(() => {
883
+ jest.restoreAllMocks();
884
+ });
885
+
886
+ const callRoute: () => Promise<void> = async (): Promise<void> => {
887
+ await mockRouter
888
+ .match("get", ACTIVE_USAGE_ROUTE)
889
+ .handlerFunction(mockRequest, mockResponse, nextFunction);
890
+ };
891
+
892
+ it("is restricted to authenticated master admins", () => {
893
+ expect(mockRouter.match("get", ACTIVE_USAGE_ROUTE).middleware).toBe(
894
+ MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
895
+ );
896
+ });
897
+
898
+ it("returns one cutoff-aligned count and active-instance list without hashes", async () => {
899
+ const active: EnterpriseLicenseInstance = makeInstance({
900
+ lastReportedAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
901
+ userEmailHashes: [hashOf("1"), hashOf("2")],
902
+ masterAdminEmails: [" Admin@Acme.com "],
903
+ });
904
+ const newlyRegistered: EnterpriseLicenseInstance = makeInstance({
905
+ lastReportedAt: undefined,
906
+ createdAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
907
+ userCount: undefined,
908
+ userEmailHashes: undefined,
909
+ masterAdminEmails: ["admin@acme.com", "owner@acme.com"],
910
+ });
911
+ const inactive: EnterpriseLicenseInstance = makeInstance({
912
+ lastReportedAt: OneUptimeDate.addRemoveDays(calculatedAt, -7),
913
+ userCount: 2,
914
+ userEmailHashes: [hashOf("2"), hashOf("3")],
915
+ });
916
+
917
+ EnterpriseLicenseInstanceService.findBy = jest
918
+ .fn()
919
+ .mockResolvedValue([active, newlyRegistered, inactive]);
920
+
921
+ await callRoute();
922
+
923
+ expect(nextFunction).not.toHaveBeenCalled();
924
+ expect(getResponseBody()).toEqual({
925
+ currentUserCount: 2,
926
+ activeInstanceIds: [
927
+ active.id!.toString(),
928
+ newlyRegistered.id!.toString(),
929
+ ],
930
+ masterAdminEmails: ["admin@acme.com", "owner@acme.com"],
931
+ calculatedAt: calculatedAt.toISOString(),
932
+ lastUsageReportedAt: calculatedAt.toISOString(),
933
+ nextInstanceStatusChangeAt: OneUptimeDate.addRemoveDays(
934
+ calculatedAt,
935
+ 6,
936
+ ).toISOString(),
937
+ });
938
+ expect(JSON.stringify(getResponseBody())).not.toContain(hashOf("1"));
939
+ });
940
+
941
+ it("returns zero when every tracked instance is inactive", async () => {
942
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
943
+ makeInstance({
944
+ lastReportedAt: OneUptimeDate.addRemoveDays(calculatedAt, -8),
945
+ userCount: 50,
946
+ }),
947
+ ]);
948
+
949
+ await callRoute();
950
+
951
+ expect(getResponseBody()).toEqual(
952
+ expect.objectContaining({
953
+ currentUserCount: 0,
954
+ activeInstanceIds: [],
955
+ nextInstanceStatusChangeAt: null,
956
+ }),
957
+ );
958
+ });
959
+
960
+ it("uses a fresh legacy count after every modern instance becomes inactive", async () => {
961
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
962
+ makeLicense({
963
+ currentUserCount: 17,
964
+ userCountUpdatedAt: OneUptimeDate.addRemoveDays(calculatedAt, -7),
965
+ legacyUserCount: 17,
966
+ legacyUserCountUpdatedAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
967
+ }),
968
+ );
969
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
970
+ makeInstance({
971
+ lastReportedAt: OneUptimeDate.addRemoveDays(calculatedAt, -7),
972
+ userCount: 50,
973
+ }),
974
+ ]);
975
+
976
+ await callRoute();
977
+
978
+ expect(getResponseBody()).toEqual(
979
+ expect.objectContaining({
980
+ currentUserCount: 17,
981
+ activeInstanceIds: [],
982
+ }),
983
+ );
984
+ });
985
+
986
+ it("does not preserve an inactive modern aggregate because another instance registered later", async () => {
987
+ const inactiveAt: Date = OneUptimeDate.addRemoveDays(calculatedAt, -7);
988
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
989
+ makeLicense({
990
+ currentUserCount: 50,
991
+ userCountUpdatedAt: inactiveAt,
992
+ }),
993
+ );
994
+ const newlyRegistered: EnterpriseLicenseInstance = makeInstance({
995
+ createdAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
996
+ lastReportedAt: undefined,
997
+ userCount: undefined,
998
+ userEmailHashes: undefined,
999
+ });
1000
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
1001
+ makeInstance({
1002
+ lastReportedAt: inactiveAt,
1003
+ userCount: 50,
1004
+ }),
1005
+ newlyRegistered,
1006
+ ]);
1007
+
1008
+ await callRoute();
1009
+
1010
+ expect(getResponseBody()).toEqual(
1011
+ expect.objectContaining({
1012
+ currentUserCount: 0,
1013
+ activeInstanceIds: [newlyRegistered.id!.toString()],
1014
+ }),
1015
+ );
1016
+ });
1017
+
1018
+ it("schedules an exact refresh when a legacy-only heartbeat expires", async () => {
1019
+ const legacyReportedAt: Date = OneUptimeDate.addRemoveDays(
1020
+ calculatedAt,
1021
+ -1,
1022
+ );
1023
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
1024
+ makeLicense({
1025
+ currentUserCount: 17,
1026
+ userCountUpdatedAt: legacyReportedAt,
1027
+ legacyUserCount: 17,
1028
+ legacyUserCountUpdatedAt: legacyReportedAt,
1029
+ }),
1030
+ );
1031
+
1032
+ await callRoute();
1033
+
1034
+ expect(getResponseBody()).toEqual(
1035
+ expect.objectContaining({
1036
+ currentUserCount: 17,
1037
+ activeInstanceIds: [],
1038
+ nextInstanceStatusChangeAt: OneUptimeDate.addRemoveDays(
1039
+ calculatedAt,
1040
+ 6,
1041
+ ).toISOString(),
1042
+ }),
1043
+ );
1044
+ });
1045
+
1046
+ it("preserves a legacy stored count when no instance rows exist", async () => {
1047
+ EnterpriseLicenseService.findOneById = jest
1048
+ .fn()
1049
+ .mockResolvedValue(makeLicense({ currentUserCount: 17 }));
1050
+
1051
+ await callRoute();
1052
+
1053
+ expect(getResponseBody()).toEqual(
1054
+ expect.objectContaining({
1055
+ currentUserCount: 17,
1056
+ activeInstanceIds: [],
1057
+ nextInstanceStatusChangeAt: OneUptimeDate.addRemoveDays(
1058
+ calculatedAt,
1059
+ 7,
1060
+ ).toISOString(),
1061
+ }),
1062
+ );
1063
+ });
1064
+
1065
+ it("preserves a legacy stored count when an instance has registered but not reported usage", async () => {
1066
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
1067
+ makeLicense({
1068
+ currentUserCount: 17,
1069
+ userCountUpdatedAt: OneUptimeDate.addRemoveDays(calculatedAt, -8),
1070
+ }),
1071
+ );
1072
+ const registeredInstance: EnterpriseLicenseInstance = makeInstance({
1073
+ createdAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
1074
+ lastReportedAt: undefined,
1075
+ userCount: undefined,
1076
+ userEmailHashes: undefined,
1077
+ });
1078
+ EnterpriseLicenseInstanceService.findBy = jest
1079
+ .fn()
1080
+ .mockResolvedValue([registeredInstance]);
1081
+
1082
+ await callRoute();
1083
+
1084
+ expect(getResponseBody()).toEqual(
1085
+ expect.objectContaining({
1086
+ currentUserCount: 17,
1087
+ activeInstanceIds: [registeredInstance.id!.toString()],
1088
+ }),
1089
+ );
1090
+ });
1091
+
1092
+ it("drops a legacy count when its report and every registration reach one week old", async () => {
1093
+ const inactiveAt: Date = OneUptimeDate.addRemoveDays(calculatedAt, -7);
1094
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
1095
+ makeLicense({
1096
+ currentUserCount: 17,
1097
+ userCountUpdatedAt: inactiveAt,
1098
+ }),
1099
+ );
1100
+ EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([
1101
+ makeInstance({
1102
+ createdAt: inactiveAt,
1103
+ lastReportedAt: undefined,
1104
+ userCount: undefined,
1105
+ userEmailHashes: undefined,
1106
+ }),
1107
+ ]);
1108
+
1109
+ await callRoute();
1110
+
1111
+ expect(getResponseBody()).toEqual(
1112
+ expect.objectContaining({
1113
+ currentUserCount: 0,
1114
+ activeInstanceIds: [],
1115
+ nextInstanceStatusChangeAt: null,
1116
+ }),
1117
+ );
1118
+ });
1119
+
1120
+ it("does not extend an expired dedicated legacy heartbeat with a newer modern timestamp", async () => {
1121
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
1122
+ makeLicense({
1123
+ currentUserCount: 17,
1124
+ userCountUpdatedAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
1125
+ userCountSource: EnterpriseLicenseUserCountSource.Legacy,
1126
+ legacyUserCount: 17,
1127
+ legacyUserCountUpdatedAt: OneUptimeDate.addRemoveDays(calculatedAt, -7),
1128
+ }),
1129
+ );
1130
+
1131
+ await callRoute();
1132
+
1133
+ expect(getResponseBody()).toEqual(
1134
+ expect.objectContaining({
1135
+ currentUserCount: 0,
1136
+ activeInstanceIds: [],
1137
+ nextInstanceStatusChangeAt: null,
1138
+ }),
1139
+ );
1140
+ });
1141
+
1142
+ it("selects the legacy report timestamp needed for the bounded fallback", async () => {
1143
+ await callRoute();
1144
+
1145
+ expect(EnterpriseLicenseService.findOneById).toHaveBeenCalledWith(
1146
+ expect.objectContaining({
1147
+ select: expect.objectContaining({
1148
+ currentUserCount: true,
1149
+ userCountUpdatedAt: true,
1150
+ userCountSource: true,
1151
+ legacyUserCount: true,
1152
+ legacyUserCountUpdatedAt: true,
1153
+ }),
1154
+ }),
1155
+ );
1156
+ });
1157
+
1158
+ it("serializes the license and instance reads with usage reports", async () => {
1159
+ await callRoute();
1160
+
1161
+ expect(
1162
+ EnterpriseLicenseService.runWithUsageAggregationLock,
1163
+ ).toHaveBeenCalledWith({
1164
+ licenseId: LICENSE_ID,
1165
+ fn: expect.any(Function),
1166
+ });
1167
+ expect(
1168
+ (EnterpriseLicenseService.findOneById as unknown as jest.Mock).mock
1169
+ .invocationCallOrder[0],
1170
+ ).toBeGreaterThan(
1171
+ (
1172
+ EnterpriseLicenseService.runWithUsageAggregationLock as unknown as jest.Mock
1173
+ ).mock.invocationCallOrder[0]!,
1174
+ );
1175
+ });
1176
+
1177
+ it("cannot expose a reported instance with the previous license timestamp", async () => {
1178
+ const previousReportAt: Date = OneUptimeDate.addRemoveDays(
1179
+ calculatedAt,
1180
+ -8,
1181
+ );
1182
+ const licenseState: EnterpriseLicense = makeLicense({
1183
+ currentUserCount: 50,
1184
+ userCountUpdatedAt: previousReportAt,
1185
+ });
1186
+ const instanceState: EnterpriseLicenseInstance = makeInstance({
1187
+ lastReportedAt: previousReportAt,
1188
+ userCount: 50,
1189
+ });
1190
+ let lockTail: Promise<void> = Promise.resolve();
1191
+ let releaseInstanceUpdate: () => void = (): void => {};
1192
+ let markInstanceUpdateStarted: () => void = (): void => {};
1193
+ const instanceUpdateStarted: Promise<void> = new Promise<void>(
1194
+ (resolve: () => void) => {
1195
+ markInstanceUpdateStarted = resolve;
1196
+ },
1197
+ );
1198
+ const instanceUpdateGate: Promise<void> = new Promise<void>(
1199
+ (resolve: () => void) => {
1200
+ releaseInstanceUpdate = resolve;
1201
+ },
1202
+ );
1203
+
1204
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
1205
+ .fn()
1206
+ .mockImplementation(
1207
+ async (data: { fn: () => Promise<unknown> }): Promise<unknown> => {
1208
+ const predecessor: Promise<void> = lockTail;
1209
+ let releaseLock: () => void = (): void => {};
1210
+ lockTail = new Promise<void>((resolve: () => void) => {
1211
+ releaseLock = resolve;
1212
+ });
1213
+
1214
+ await predecessor;
1215
+ try {
1216
+ return await data.fn();
1217
+ } finally {
1218
+ releaseLock();
1219
+ }
1220
+ },
1221
+ );
1222
+ EnterpriseLicenseService.findOneBy = jest
1223
+ .fn()
1224
+ .mockResolvedValue(licenseState);
1225
+ EnterpriseLicenseService.findOneById = jest
1226
+ .fn()
1227
+ .mockImplementation(async (): Promise<EnterpriseLicense> => {
1228
+ return licenseState;
1229
+ });
1230
+ EnterpriseLicenseService.updateOneById = jest
1231
+ .fn()
1232
+ .mockImplementation(
1233
+ async (args: { data: Partial<EnterpriseLicense> }): Promise<number> => {
1234
+ Object.assign(licenseState, args.data);
1235
+ return 1;
1236
+ },
1237
+ );
1238
+ EnterpriseLicenseInstanceService.findOneBy = jest
1239
+ .fn()
1240
+ .mockResolvedValue(instanceState);
1241
+ EnterpriseLicenseInstanceService.updateOneById = jest
1242
+ .fn()
1243
+ .mockImplementation(
1244
+ async (args: {
1245
+ data: Partial<EnterpriseLicenseInstance>;
1246
+ }): Promise<number> => {
1247
+ Object.assign(instanceState, args.data);
1248
+ markInstanceUpdateStarted();
1249
+ await instanceUpdateGate;
1250
+ return 1;
1251
+ },
1252
+ );
1253
+ EnterpriseLicenseInstanceService.findBy = jest
1254
+ .fn()
1255
+ .mockImplementation(
1256
+ async (): Promise<Array<EnterpriseLicenseInstance>> => {
1257
+ return [instanceState];
1258
+ },
1259
+ );
1260
+
1261
+ const reportRequest: OneUptimeRequest = {
1262
+ body: {
1263
+ licenseKey: LICENSE_KEY,
1264
+ userCount: 2,
1265
+ instanceId: "instance-1",
1266
+ userEmailHashes: [hashOf("1"), hashOf("2")],
1267
+ },
1268
+ } as unknown as OneUptimeRequest;
1269
+ const reportPromise: Promise<void> = Promise.resolve(
1270
+ mockRouter
1271
+ .match("post", REPORT_ROUTE)
1272
+ .handlerFunction(reportRequest, mockResponse, nextFunction),
1273
+ );
1274
+
1275
+ await instanceUpdateStarted;
1276
+
1277
+ const snapshotPromise: Promise<void> = callRoute();
1278
+
1279
+ releaseInstanceUpdate();
1280
+ await Promise.all([reportPromise, snapshotPromise]);
1281
+
1282
+ expect(nextFunction).not.toHaveBeenCalled();
1283
+ const responseBodies: Array<JSONObject> = (
1284
+ Response.sendJsonObjectResponse as unknown as jest.Mock
1285
+ ).mock.calls.map((call: Array<unknown>): JSONObject => {
1286
+ return call[2] as JSONObject;
584
1287
  });
1288
+ const snapshotBody: JSONObject = responseBodies.find(
1289
+ (body: JSONObject): boolean => {
1290
+ return Array.isArray(body["activeInstanceIds"]);
1291
+ },
1292
+ )!;
1293
+
1294
+ expect(snapshotBody["currentUserCount"]).toBe(2);
1295
+ expect(snapshotBody["activeInstanceIds"]).toEqual([
1296
+ instanceState.id!.toString(),
1297
+ ]);
1298
+ expect(snapshotBody["lastUsageReportedAt"]).toBe(
1299
+ calculatedAt.toISOString(),
1300
+ );
1301
+ });
1302
+
1303
+ it("does not disclose whether an unknown license has instance usage", async () => {
1304
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(null);
1305
+
1306
+ await callRoute();
1307
+
1308
+ expect(nextFunction).toHaveBeenCalledWith(
1309
+ new BadDataException("Enterprise license not found"),
1310
+ );
1311
+ expect(EnterpriseLicenseInstanceService.findBy).not.toHaveBeenCalled();
585
1312
  });
586
1313
  });
587
1314
 
@@ -598,6 +1325,16 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/validate", () => {
598
1325
  EnterpriseLicenseService.findOneBy = jest
599
1326
  .fn()
600
1327
  .mockResolvedValue(makeLicense());
1328
+ EnterpriseLicenseService.findOneById = jest
1329
+ .fn()
1330
+ .mockResolvedValue(makeLicense());
1331
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
1332
+ .fn()
1333
+ .mockImplementation(
1334
+ async (data: { fn: () => Promise<unknown> }): Promise<unknown> => {
1335
+ return await data.fn();
1336
+ },
1337
+ );
601
1338
  EnterpriseLicenseInstanceService.findBy = jest.fn().mockResolvedValue([]);
602
1339
  EnterpriseLicenseInstanceService.findOneBy = jest
603
1340
  .fn()
@@ -605,6 +1342,9 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/validate", () => {
605
1342
  EnterpriseLicenseInstanceService.create = jest
606
1343
  .fn()
607
1344
  .mockResolvedValue(undefined);
1345
+ EnterpriseLicenseInstanceService.updateOneById = jest
1346
+ .fn()
1347
+ .mockResolvedValue(undefined);
608
1348
  EnterpriseLicenseInstanceService.countBy = jest
609
1349
  .fn()
610
1350
  .mockResolvedValue(new PositiveNumber(0));
@@ -656,6 +1396,196 @@ describe("EnterpriseLicenseAPI POST /enterprise-license/validate", () => {
656
1396
  expect(body["token"]).toBe("signed.jwt.token");
657
1397
  });
658
1398
 
1399
+ it("registers a new instance without treating validation as a usage heartbeat", async () => {
1400
+ await mockRouter
1401
+ .match("post", VALIDATE_ROUTE)
1402
+ .handlerFunction(mockRequest, mockResponse, nextFunction);
1403
+
1404
+ expect(nextFunction).not.toHaveBeenCalled();
1405
+ expect(
1406
+ EnterpriseLicenseInstanceService.updateOneById,
1407
+ ).not.toHaveBeenCalled();
1408
+
1409
+ const createArguments: {
1410
+ data: EnterpriseLicenseInstance;
1411
+ } = (EnterpriseLicenseInstanceService.create as unknown as jest.Mock).mock
1412
+ .calls[0]![0] as {
1413
+ data: EnterpriseLicenseInstance;
1414
+ };
1415
+
1416
+ expect(createArguments.data).toEqual(
1417
+ expect.objectContaining({
1418
+ enterpriseLicenseId: LICENSE_ID,
1419
+ instanceId: "instance-1",
1420
+ host: "oneuptime.acme.internal",
1421
+ oneuptimeVersion: "12.0.19",
1422
+ }),
1423
+ );
1424
+ expect(createArguments.data.lastReportedAt).toBeUndefined();
1425
+ expect(createArguments.data.createdAt).toBeUndefined();
1426
+ });
1427
+
1428
+ it("updates instance metadata without refreshing its activity timestamps", async () => {
1429
+ const registeredAt: Date = new Date("2026-08-01T12:00:00.000Z");
1430
+ const lastReportedAt: Date = new Date("2026-08-20T12:00:00.000Z");
1431
+ const existingInstance: EnterpriseLicenseInstance = makeInstance({
1432
+ createdAt: registeredAt,
1433
+ lastReportedAt,
1434
+ });
1435
+
1436
+ EnterpriseLicenseInstanceService.findOneBy = jest
1437
+ .fn()
1438
+ .mockResolvedValue(existingInstance);
1439
+
1440
+ await mockRouter
1441
+ .match("post", VALIDATE_ROUTE)
1442
+ .handlerFunction(mockRequest, mockResponse, nextFunction);
1443
+
1444
+ expect(nextFunction).not.toHaveBeenCalled();
1445
+ expect(EnterpriseLicenseInstanceService.create).not.toHaveBeenCalled();
1446
+
1447
+ const updateArguments: {
1448
+ data: Partial<EnterpriseLicenseInstance>;
1449
+ } = (EnterpriseLicenseInstanceService.updateOneById as unknown as jest.Mock)
1450
+ .mock.calls[0]![0] as {
1451
+ data: Partial<EnterpriseLicenseInstance>;
1452
+ };
1453
+
1454
+ expect(updateArguments.data).toEqual(
1455
+ expect.objectContaining({
1456
+ host: "oneuptime.acme.internal",
1457
+ oneuptimeVersion: "12.0.19",
1458
+ }),
1459
+ );
1460
+ expect(
1461
+ Object.prototype.hasOwnProperty.call(
1462
+ updateArguments.data,
1463
+ "lastReportedAt",
1464
+ ),
1465
+ ).toBe(false);
1466
+ expect(
1467
+ Object.prototype.hasOwnProperty.call(updateArguments.data, "createdAt"),
1468
+ ).toBe(false);
1469
+ expect(existingInstance.lastReportedAt).toBe(lastReportedAt);
1470
+ expect(existingInstance.createdAt).toBe(registeredAt);
1471
+ });
1472
+
1473
+ it("serializes registration and re-reads usage inside the aggregation lock", async () => {
1474
+ const events: Array<string> = [];
1475
+ let isInsideUsageLock: boolean = false;
1476
+
1477
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
1478
+ .fn()
1479
+ .mockImplementation(
1480
+ async (data: { fn: () => Promise<unknown> }): Promise<unknown> => {
1481
+ events.push("lock");
1482
+ isInsideUsageLock = true;
1483
+
1484
+ try {
1485
+ return await data.fn();
1486
+ } finally {
1487
+ isInsideUsageLock = false;
1488
+ }
1489
+ },
1490
+ );
1491
+ EnterpriseLicenseInstanceService.create = jest
1492
+ .fn()
1493
+ .mockImplementation(async (): Promise<void> => {
1494
+ expect(isInsideUsageLock).toBe(true);
1495
+ events.push("register");
1496
+ });
1497
+ EnterpriseLicenseInstanceService.findBy = jest
1498
+ .fn()
1499
+ .mockImplementation(
1500
+ async (): Promise<Array<EnterpriseLicenseInstance>> => {
1501
+ expect(isInsideUsageLock).toBe(true);
1502
+ events.push("instances");
1503
+ return [];
1504
+ },
1505
+ );
1506
+ EnterpriseLicenseService.findOneById = jest
1507
+ .fn()
1508
+ .mockImplementation(async (): Promise<EnterpriseLicense> => {
1509
+ expect(isInsideUsageLock).toBe(true);
1510
+ events.push("license");
1511
+ return makeLicense({
1512
+ currentUserCount: 17,
1513
+ userCountUpdatedAt: new Date("2026-09-01T12:00:00.000Z"),
1514
+ });
1515
+ });
1516
+
1517
+ await mockRouter
1518
+ .match("post", VALIDATE_ROUTE)
1519
+ .handlerFunction(mockRequest, mockResponse, nextFunction);
1520
+
1521
+ expect(nextFunction).not.toHaveBeenCalled();
1522
+ expect(events).toEqual(["lock", "register", "instances", "license"]);
1523
+ expect(getResponseBody()).toEqual(
1524
+ expect.objectContaining({
1525
+ currentUserCount: 17,
1526
+ userCountUpdatedAt: "2026-09-01T12:00:00.000Z",
1527
+ }),
1528
+ );
1529
+ expect(
1530
+ EnterpriseLicenseService.runWithUsageAggregationLock,
1531
+ ).toHaveBeenCalledWith({
1532
+ licenseId: LICENSE_ID,
1533
+ fn: expect.any(Function),
1534
+ });
1535
+ });
1536
+
1537
+ it("returns an active-only aggregate with matching per-instance provenance", async () => {
1538
+ const calculatedAt: Date = new Date("2026-09-02T12:00:00.000Z");
1539
+ const inactiveInstance: EnterpriseLicenseInstance = makeInstance({
1540
+ instanceId: "instance-1",
1541
+ userCount: 80,
1542
+ lastReportedAt: OneUptimeDate.addRemoveDays(calculatedAt, -7),
1543
+ });
1544
+ const activeInstance: EnterpriseLicenseInstance = makeInstance({
1545
+ instanceId: "instance-2",
1546
+ userCount: 100,
1547
+ lastReportedAt: OneUptimeDate.addRemoveDays(calculatedAt, -1),
1548
+ });
1549
+
1550
+ jest.spyOn(OneUptimeDate, "getCurrentDate").mockReturnValue(calculatedAt);
1551
+ EnterpriseLicenseInstanceService.findOneBy = jest
1552
+ .fn()
1553
+ .mockResolvedValue(inactiveInstance);
1554
+ EnterpriseLicenseInstanceService.findBy = jest
1555
+ .fn()
1556
+ .mockResolvedValue([inactiveInstance, activeInstance]);
1557
+ EnterpriseLicenseService.findOneById = jest.fn().mockResolvedValue(
1558
+ makeLicense({
1559
+ currentUserCount: 180,
1560
+ userCountUpdatedAt: calculatedAt,
1561
+ userCountSource: EnterpriseLicenseUserCountSource.Instance,
1562
+ }),
1563
+ );
1564
+
1565
+ await mockRouter
1566
+ .match("post", VALIDATE_ROUTE)
1567
+ .handlerFunction(mockRequest, mockResponse, nextFunction);
1568
+
1569
+ expect(nextFunction).not.toHaveBeenCalled();
1570
+ expect(getResponseBody()).toEqual(
1571
+ expect.objectContaining({
1572
+ currentUserCount: 100,
1573
+ instances: expect.arrayContaining([
1574
+ expect.objectContaining({
1575
+ instanceId: "instance-1",
1576
+ userCount: 80,
1577
+ isCountedTowardsUsage: false,
1578
+ }),
1579
+ expect.objectContaining({
1580
+ instanceId: "instance-2",
1581
+ userCount: 100,
1582
+ isCountedTowardsUsage: true,
1583
+ }),
1584
+ ]),
1585
+ }),
1586
+ );
1587
+ });
1588
+
659
1589
  it("keeps rejecting an expired license, unlike the report route", async () => {
660
1590
  EnterpriseLicenseService.findOneBy = jest.fn().mockResolvedValue(
661
1591
  makeLicense({
@@ -690,6 +1620,13 @@ describe("the contract between the two halves of the sync", () => {
690
1620
  EnterpriseLicenseService.updateOneById = jest
691
1621
  .fn()
692
1622
  .mockResolvedValue(undefined);
1623
+ EnterpriseLicenseService.runWithUsageAggregationLock = jest
1624
+ .fn()
1625
+ .mockImplementation(
1626
+ async (data: { fn: () => Promise<unknown> }): Promise<unknown> => {
1627
+ return await data.fn();
1628
+ },
1629
+ );
693
1630
  EnterpriseLicenseInstanceService.findBy = jest
694
1631
  .fn()
695
1632
  .mockResolvedValue([makeInstance()]);
@@ -754,6 +1691,13 @@ describe("the contract between the two halves of the sync", () => {
754
1691
  expect(result.updateData.enterpriseLicenseToken).toBe("signed.jwt.token");
755
1692
  expect(result.updateData.enterpriseLicenseExpiresAt).toBeInstanceOf(Date);
756
1693
  expect(result.updateData.enterpriseLicenseInstances).toHaveLength(1);
1694
+ expect(
1695
+ (
1696
+ result.updateData.enterpriseLicenseInstances as
1697
+ | Array<EnterpriseLicenseInstanceSummary>
1698
+ | undefined
1699
+ )?.[0]?.isCountedTowardsUsage,
1700
+ ).toBe(true);
757
1701
 
758
1702
  // The installation never adopts a key handed to it by a response.
759
1703
  expect(Object.keys(result.updateData)).not.toContain(