@oneuptime/common 12.0.31 → 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 (747) hide show
  1. package/Models/AnalyticsModels/Span.ts +14 -0
  2. package/Models/DatabaseModels/AlertState.ts +0 -2
  3. package/Models/DatabaseModels/Domain.ts +1 -1
  4. package/Models/DatabaseModels/EnterpriseLicense.ts +56 -0
  5. package/Models/DatabaseModels/Incident.ts +1 -1
  6. package/Models/DatabaseModels/IncidentTemplate.ts +1 -1
  7. package/Models/DatabaseModels/Index.ts +12 -0
  8. package/Models/DatabaseModels/MetricType.ts +0 -2
  9. package/Models/DatabaseModels/Monitor.ts +76 -0
  10. package/Models/DatabaseModels/MonitorStatusTimeline.ts +0 -2
  11. package/Models/DatabaseModels/MonitorTest.ts +0 -2
  12. package/Models/DatabaseModels/NetworkAlertPolicy.ts +690 -0
  13. package/Models/DatabaseModels/NetworkDevice.ts +297 -3
  14. package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +3 -1
  15. package/Models/DatabaseModels/NetworkDeviceRole.ts +648 -0
  16. package/Models/DatabaseModels/NetworkSite.ts +201 -2
  17. package/Models/DatabaseModels/NetworkSiteType.ts +102 -3
  18. package/Models/DatabaseModels/NetworkSnmpCredentialProfile.ts +841 -0
  19. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -1
  20. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -1
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +0 -2
  22. package/Models/DatabaseModels/StatusPageAnnouncementTemplate.ts +0 -2
  23. package/Models/DatabaseModels/StatusPagePrivateUser.ts +0 -2
  24. package/Models/DatabaseModels/StatusPageResource.ts +0 -2
  25. package/Models/DatabaseModels/StatusPageSubscriber.ts +0 -2
  26. package/Models/DatabaseModels/TelemetryException.ts +119 -0
  27. package/Models/DatabaseModels/UserNotificationEmailRollupBatch.ts +334 -0
  28. package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +370 -0
  29. package/Models/DatabaseModels/UserNotificationEmailRollupSetting.ts +282 -0
  30. package/Server/API/EnterpriseLicenseAPI.ts +391 -64
  31. package/Server/API/LlmProviderAPI.ts +137 -22
  32. package/Server/EnvironmentConfig.ts +14 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.ts +55 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.ts +72 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.ts +31 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.ts +124 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.ts +132 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.ts +79 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.ts +191 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  41. package/Server/Infrastructure/QueueWorker.ts +38 -8
  42. package/Server/Middleware/BearerTokenAuthorization.ts +12 -0
  43. package/Server/Middleware/ProjectAuthorization.ts +11 -0
  44. package/Server/Middleware/SCIMAuthorization.ts +12 -0
  45. package/Server/Middleware/TelemetryIngest.ts +11 -0
  46. package/Server/Services/AIBillingService.ts +101 -27
  47. package/Server/Services/DatabaseService.ts +39 -9
  48. package/Server/Services/EnterpriseLicenseInstanceService.ts +197 -0
  49. package/Server/Services/EnterpriseLicenseService.ts +55 -0
  50. package/Server/Services/Index.ts +12 -0
  51. package/Server/Services/MonitorService.ts +143 -21
  52. package/Server/Services/MonitorStatusTimelineService.ts +150 -8
  53. package/Server/Services/MonitorTemplateService.ts +217 -1
  54. package/Server/Services/NetworkAlertPolicyEngineService.ts +2159 -0
  55. package/Server/Services/NetworkAlertPolicyService.ts +944 -0
  56. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +9 -10
  57. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +60 -15
  58. package/Server/Services/NetworkDeviceRoleService.ts +90 -0
  59. package/Server/Services/NetworkDeviceService.ts +1032 -73
  60. package/Server/Services/NetworkSiteService.ts +1502 -129
  61. package/Server/Services/NetworkSiteTypeService.ts +1216 -0
  62. package/Server/Services/NetworkSnmpCredentialProfileService.ts +238 -0
  63. package/Server/Services/NotificationService.ts +118 -28
  64. package/Server/Services/ProjectService.ts +186 -44
  65. package/Server/Services/StatusPagePrivateUserService.ts +103 -3
  66. package/Server/Services/UserNotificationEmailRollupBatchService.ts +78 -0
  67. package/Server/Services/UserNotificationEmailRollupItemService.ts +79 -0
  68. package/Server/Services/UserNotificationEmailRollupSettingService.ts +113 -0
  69. package/Server/Services/UserNotificationSettingService.ts +62 -31
  70. package/Server/Services/UserService.ts +95 -0
  71. package/Server/Types/Database/QueryHelper.ts +2 -2
  72. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +14 -3
  73. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +10 -0
  74. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +10 -0
  75. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +62 -1
  76. package/Server/Utils/AI/Toolbox/AlertTools.ts +15 -1
  77. package/Server/Utils/Billing/BillingFailureNoticeThrottle.ts +80 -0
  78. package/Server/Utils/DataSource/HttpFetch.ts +9 -2
  79. package/Server/Utils/EmailRollup/EmailRollupConstants.ts +98 -0
  80. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +872 -0
  81. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +781 -0
  82. package/Server/Utils/EmailRollup/EmailRollupWriter.ts +368 -0
  83. package/Server/Utils/LLM/LLMService.ts +78 -0
  84. package/Server/Utils/LogRedaction.ts +8 -0
  85. package/Server/Utils/Logger.ts +79 -1
  86. package/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.ts +190 -0
  87. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +9 -2
  88. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +61 -1
  89. package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +14 -0
  90. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +49 -0
  91. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +255 -4
  92. package/Server/Utils/Monitor/MonitorMetricUtil.ts +41 -0
  93. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +43 -0
  94. package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +499 -19
  95. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +66 -10
  96. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +238 -44
  97. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +146 -39
  98. package/Server/Utils/NetworkDevice/DeviceHealthAggregation.ts +31 -18
  99. package/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.ts +213 -0
  100. package/Server/Utils/OutboundUserAgent.ts +152 -0
  101. package/Server/Utils/StartServer.ts +51 -19
  102. package/Server/Utils/Telemetry/CaptureSpan.ts +32 -8
  103. package/Server/Utils/Telemetry/ErrorClassResolver.ts +120 -0
  104. package/Server/Utils/Telemetry/SpanUtil.ts +33 -0
  105. package/Server/Utils/Telemetry.ts +198 -51
  106. package/Server/Views/Partials/SensitiveUrlToken.ejs +140 -0
  107. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +158 -27
  108. package/Tests/App/Dashboard/AskAiSuggestedPrompts.test.ts +279 -0
  109. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +35 -8
  110. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +107 -3
  111. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +517 -0
  112. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +26 -0
  113. package/Tests/App/Dashboard/DashboardWideChartZoom.test.tsx +401 -0
  114. package/Tests/App/Dashboard/DeviceStatusHero.test.tsx +588 -0
  115. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +18 -9
  116. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +632 -0
  117. package/Tests/App/Dashboard/MonitorRecommendationCreateProgress.test.tsx +682 -0
  118. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +318 -0
  119. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +4 -2
  120. package/Tests/App/Dashboard/NetworkDeviceBulkDelete.test.tsx +357 -0
  121. package/Tests/App/Dashboard/NetworkDeviceCreateFormMonitorBinding.test.tsx +676 -0
  122. package/Tests/App/Dashboard/NetworkDeviceCreateFormPingMonitor.test.tsx +791 -0
  123. package/Tests/App/Dashboard/NetworkDeviceCreateFormProbeAndSnmp.test.tsx +833 -0
  124. package/Tests/App/Dashboard/NetworkDeviceCriteriaFilter.test.ts +161 -0
  125. package/Tests/App/Dashboard/NetworkDeviceSettingsMonitoringSection.test.tsx +458 -0
  126. package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +626 -0
  127. package/Tests/App/Dashboard/PingMonitorProvisioning.test.ts +368 -0
  128. package/Tests/App/Dashboard/RecommendationCard.test.tsx +796 -0
  129. package/Tests/App/Dashboard/RecommendationToolbar.test.tsx +519 -0
  130. package/Tests/App/Dashboard/RecommendationsList.test.tsx +661 -0
  131. package/Tests/App/Dashboard/SideMenuHarness.tsx +11 -3
  132. package/Tests/App/Dashboard/UseBulkCreatePingMonitors.test.tsx +899 -0
  133. package/Tests/App/Dashboard/UseBulkSnmpCredentialProfileActions.test.tsx +732 -0
  134. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +256 -0
  135. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +11 -1
  136. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +9 -4
  137. package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +115 -41
  138. package/Tests/Models/NetworkSiteHierarchy.test.ts +41 -4
  139. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +951 -7
  140. package/Tests/Server/API/LlmProviderConnectionTest.test.ts +522 -0
  141. package/Tests/Server/Infrastructure/Postgres/NetworkDeviceRoleTableMigration.test.ts +476 -0
  142. package/Tests/Server/Infrastructure/Postgres/NetworkSnmpCredentialProfilesAndAlertPoliciesMigration.test.ts +854 -0
  143. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +18 -0
  144. package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +535 -0
  145. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +560 -0
  146. package/Tests/Server/Services/BillingRechargeOwnerEmailGuards.test.ts +783 -0
  147. package/Tests/Server/Services/DatabaseServiceGenerateSlug.test.ts +215 -0
  148. package/Tests/Server/Services/EnterpriseLicenseInstanceServiceDeletionUsage.test.ts +707 -0
  149. package/Tests/Server/Services/EnterpriseLicenseServiceUsageAggregationLock.test.ts +329 -0
  150. package/Tests/Server/Services/MonitorServiceDeleteNetworkDeviceCleanup.test.ts +294 -0
  151. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +802 -0
  152. package/Tests/Server/Services/MonitorStatusTimelineService.test.ts +72 -2
  153. package/Tests/Server/Services/NetworkAlertPolicyEngineHooks.test.ts +752 -0
  154. package/Tests/Server/Services/NetworkAlertPolicyEngineService.test.ts +1849 -0
  155. package/Tests/Server/Services/NetworkAlertPolicyModel.test.ts +1793 -0
  156. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +47 -4
  157. package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +97 -12
  158. package/Tests/Server/Services/NetworkDeviceAutoMonitorLifecycle.test.ts +148 -3
  159. package/Tests/Server/Services/NetworkDeviceMonitorBindingUpdateGuard.test.ts +514 -0
  160. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +447 -58
  161. package/Tests/Server/Services/NetworkDeviceMonitoringMethodTransition.test.ts +739 -0
  162. package/Tests/Server/Services/NetworkDevicePollingTenancy.test.ts +501 -0
  163. package/Tests/Server/Services/NetworkDeviceRoleModel.test.ts +672 -0
  164. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +1 -1
  165. package/Tests/Server/Services/NetworkDeviceSiteDefaultProbe.test.ts +432 -0
  166. package/Tests/Server/Services/NetworkSiteMonitoringDefaults.test.ts +498 -0
  167. package/Tests/Server/Services/NetworkSiteRollupPolicyAndMaintenance.test.ts +337 -55
  168. package/Tests/Server/Services/NetworkSiteService.test.ts +1868 -131
  169. package/Tests/Server/Services/NetworkSiteTypeService.test.ts +1109 -0
  170. package/Tests/Server/Services/NetworkSnmpCredentialProfileModel.test.ts +926 -0
  171. package/Tests/Server/Services/NetworkSnmpCredentialProfileService.test.ts +796 -0
  172. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +67 -4
  173. package/Tests/Server/Services/ProjectServiceNetworkSiteTypeDefaults.test.ts +214 -0
  174. package/Tests/Server/Services/ScheduledMaintenanceSubscriberEndTime.test.ts +369 -0
  175. package/Tests/Server/Services/StatusPagePrivateUserEmailChangePasswordResetExpiry.test.ts +269 -0
  176. package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +472 -0
  177. package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +569 -0
  178. package/Tests/Server/Services/UserNotificationEmailRollupSettingModel.test.ts +314 -0
  179. package/Tests/Server/Services/UserNotificationEmailRollupSettingService.test.ts +409 -0
  180. package/Tests/Server/Services/UserNotificationSettingMonitorDefaults.test.ts +248 -0
  181. package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +856 -0
  182. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +145 -3
  183. package/Tests/Server/Types/Database/Permissions/DashboardAccessPermission.test.ts +321 -0
  184. package/Tests/Server/Types/Database/QueryHelperOperators.test.ts +17 -0
  185. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +124 -0
  186. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +208 -0
  187. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +235 -0
  188. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +253 -0
  189. package/Tests/Server/Utils/BasicCron.test.ts +283 -0
  190. package/Tests/Server/Utils/Captcha.test.ts +422 -0
  191. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +96 -2
  192. package/Tests/Server/Utils/Database/MigrationFailureLog.test.ts +466 -0
  193. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1096 -0
  194. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerClaim.test.ts +466 -0
  195. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +1428 -0
  196. package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +752 -0
  197. package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +864 -0
  198. package/Tests/Server/Utils/IncomingCallPhoneNumber.test.ts +174 -0
  199. package/Tests/Server/Utils/LoggerFaultDemotion.test.ts +346 -0
  200. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +633 -0
  201. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +196 -0
  202. package/Tests/Server/Utils/Monitor/DatabaseMetricWrite.test.ts +367 -0
  203. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +100 -0
  204. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +5 -0
  205. package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +512 -31
  206. package/Tests/Server/Utils/Monitor/NetworkDeviceMetricUtil.test.ts +252 -0
  207. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +479 -42
  208. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +351 -5
  209. package/Tests/Server/Utils/NetworkDevice/DeviceHealthAggregation.test.ts +136 -12
  210. package/Tests/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.test.ts +306 -0
  211. package/Tests/Server/Utils/OutboundUserAgent.test.ts +266 -0
  212. package/Tests/Server/Utils/SecurityEvent/ThreatIntel/TaxiiClientUserAgent.test.ts +141 -0
  213. package/Tests/Server/Utils/Telemetry/ErrorClassResolver.test.ts +298 -0
  214. package/Tests/Server/Utils/Telemetry.test.ts +456 -66
  215. package/Tests/Types/Monitor/DatabaseMetricCatalog.test.ts +388 -0
  216. package/Tests/Types/Monitor/MonitorStepDatabaseMonitor.test.ts +243 -0
  217. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
  218. package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
  219. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +16 -7
  220. package/Tests/Types/Monitor/NetworkDeviceAlertPack.test.ts +60 -0
  221. package/Tests/Types/NetworkDevice/DefaultNetworkDeviceRole.test.ts +417 -0
  222. package/Tests/Types/NetworkDevice/NetworkAlertPolicyScope.test.ts +655 -0
  223. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +115 -26
  224. package/Tests/Types/NetworkSite/DefaultNetworkSiteType.test.ts +39 -0
  225. package/Tests/Types/NotificationSetting/NotificationEmailRollupPolicy.test.ts +299 -0
  226. package/Tests/Types/Telemetry/ErrorClass.test.ts +483 -0
  227. package/Tests/UI/Components/BasicRadioButtons.test.tsx +336 -0
  228. package/Tests/UI/Components/Charts/ChartClickDoubleClickDisambiguation.test.tsx +221 -0
  229. package/Tests/UI/Components/Charts/ChartDoubleClickReset.test.tsx +220 -0
  230. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +751 -0
  231. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +475 -43
  232. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +89 -0
  233. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +15 -0
  234. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +103 -8
  235. package/Tests/UI/Components/ProgressBar.test.tsx +38 -2
  236. package/Tests/UI/Components/SideMenuItem.test.tsx +256 -4
  237. package/Tests/UI/Components/SideMenuSection.test.tsx +310 -0
  238. package/Tests/UI/Components/SideOverSubmitState.test.tsx +335 -0
  239. package/Tests/UI/Utils/PageScrollLock.test.tsx +377 -0
  240. package/Tests/UI/Utils/PermissionGate.test.ts +78 -0
  241. package/Tests/UI/Utils/SensitiveUrlToken.test.ts +140 -0
  242. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +30 -0
  243. package/Tests/UI/Utils/UseDashboardTimeRangeZoom.test.tsx +187 -0
  244. package/Tests/Utils/Dashboard/DashboardResourceListSharedArgs.test.ts +302 -0
  245. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +242 -0
  246. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +69 -0
  247. package/Tests/Utils/EnterpriseLicenseSync.test.ts +7 -1
  248. package/Tests/Utils/EnterpriseLicenseUsage.test.ts +433 -2
  249. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +280 -1
  250. package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
  251. package/Tests/Utils/Monitor/NetworkDeviceRoleCatalog.test.ts +981 -0
  252. package/Tests/Utils/Monitor/NetworkTopologyRoleCatalog.test.ts +865 -0
  253. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +38 -0
  254. package/Tests/Utils/NetworkDevice/DeviceHealthStateUtil.test.ts +284 -23
  255. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +177 -0
  256. package/Tests/Utils/NetworkDevice/DeviceRoleKeyUtil.test.ts +538 -0
  257. package/Tests/Utils/NetworkDevice/MonitoringMethodThreadedClassifiers.test.ts +992 -0
  258. package/Tests/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.test.ts +427 -0
  259. package/Tests/Utils/NetworkDevice/ReachabilityStampConsistency.test.ts +185 -0
  260. package/Tests/Utils/NetworkDevice/SnmpCredentialUtil.test.ts +268 -0
  261. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +55 -5
  262. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +98 -21
  263. package/Tests/Utils/NetworkDiscovery/DiscoveryImportEligibility.test.ts +98 -20
  264. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +8 -7
  265. package/Tests/Utils/NetworkDiscovery/PingMonitorBuilderForAddress.test.ts +274 -0
  266. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +230 -51
  267. package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +209 -0
  268. package/Tests/Utils/Slo/SloEvaluation.test.ts +171 -0
  269. package/Tests/Utils/Slug.test.ts +61 -1
  270. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +145 -0
  271. package/Tests/jest.setup.ts +19 -0
  272. package/Types/AI/ExceptionAIClassification.ts +10 -24
  273. package/Types/Email/EmailTemplateType.ts +2 -0
  274. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +5 -0
  275. package/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.ts +13 -0
  276. package/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.ts +6 -0
  277. package/Types/Exception/ApiException.ts +10 -0
  278. package/Types/Exception/BadDataException.ts +11 -0
  279. package/Types/Exception/BadRequestException.ts +11 -0
  280. package/Types/Exception/Exception.ts +65 -0
  281. package/Types/Exception/ForbiddenException.ts +10 -0
  282. package/Types/Exception/NotAuthenticatedException.ts +10 -0
  283. package/Types/Exception/NotAuthorizedException.ts +10 -0
  284. package/Types/Exception/NotFoundException.ts +11 -0
  285. package/Types/Exception/PayloadTooLargeException.ts +11 -0
  286. package/Types/Exception/PaymentRequiredException.ts +10 -0
  287. package/Types/Exception/ServiceUnavailableException.ts +10 -0
  288. package/Types/Exception/SsoAuthorizationException.ts +10 -0
  289. package/Types/Exception/TenantNotFoundException.ts +11 -0
  290. package/Types/Exception/TimeoutException.ts +10 -0
  291. package/Types/Exception/TooManyRequestsException.ts +10 -0
  292. package/Types/Exception/UnableToReachServer.ts +10 -0
  293. package/Types/Exception/WebsiteRequestException.ts +10 -0
  294. package/Types/Monitor/CriteriaFilter.ts +60 -2
  295. package/Types/Monitor/DatabaseMetricCatalog.ts +693 -0
  296. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +83 -0
  297. package/Types/Monitor/MonitorCriteriaInstance.ts +76 -0
  298. package/Types/Monitor/MonitorMetricType.ts +71 -0
  299. package/Types/Monitor/MonitorStep.ts +76 -0
  300. package/Types/Monitor/MonitorStepDatabaseMonitor.ts +171 -0
  301. package/Types/Monitor/MonitorStepSqlMonitor.ts +2 -20
  302. package/Types/Monitor/MonitorType.ts +47 -1
  303. package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +22 -1
  304. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +61 -0
  305. package/Types/Monitor/SqlConnectionConfig.ts +31 -0
  306. package/Types/NetworkDevice/DefaultNetworkDeviceRole.ts +145 -0
  307. package/Types/NetworkDevice/NetworkAlertPolicyScope.ts +343 -0
  308. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +42 -22
  309. package/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.ts +30 -0
  310. package/Types/NotificationSetting/NotificationEmailRollupCategory.ts +255 -0
  311. package/Types/NotificationSetting/NotificationEmailRollupPolicy.ts +104 -0
  312. package/Types/Permission.ts +135 -0
  313. package/Types/Probe/ProbeMonitorResponse.ts +16 -0
  314. package/Types/Telemetry/ErrorClass.ts +274 -0
  315. package/Types/Telemetry/UnitOfWork.ts +59 -0
  316. package/UI/Components/Charts/Area/AreaChart.tsx +7 -0
  317. package/UI/Components/Charts/Bar/BarChart.tsx +6 -0
  318. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +9 -1
  319. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +97 -35
  320. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +14 -0
  321. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +97 -35
  322. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +13 -0
  323. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  324. package/UI/Components/CommandPalette/CommandPalette.tsx +2 -8
  325. package/UI/Components/EditionLabel/EditionLabel.tsx +4 -0
  326. package/UI/Components/Modal/Modal.tsx +2 -8
  327. package/UI/Components/ModelTable/BaseModelTable.tsx +50 -15
  328. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +44 -0
  329. package/UI/Components/Navbar/NavBar.tsx +62 -13
  330. package/UI/Components/Navbar/NavBarMenuModal.tsx +7 -2
  331. package/UI/Components/ProgressBar/ProgressBar.tsx +12 -1
  332. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +37 -7
  333. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +2 -0
  334. package/UI/Components/SideMenu/SideMenu.tsx +135 -7
  335. package/UI/Components/SideMenu/SideMenuItem.tsx +80 -85
  336. package/UI/Components/SideMenu/SideMenuSection.tsx +69 -25
  337. package/UI/Components/SideOver/SideOver.tsx +21 -20
  338. package/UI/Utils/PageScrollLock.ts +79 -3
  339. package/UI/Utils/PermissionGate.ts +61 -0
  340. package/UI/Utils/SensitiveUrlToken.ts +112 -0
  341. package/UI/Utils/TestLLMProvider.ts +11 -0
  342. package/UI/Utils/UseDashboardTimeRangeZoom.ts +77 -0
  343. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +118 -0
  344. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +16 -0
  345. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +191 -11
  346. package/Utils/Monitor/MonitorMetricType.ts +153 -1
  347. package/Utils/Monitor/NetworkDeviceRoleCatalog.ts +232 -0
  348. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +2 -1
  349. package/Utils/Monitor/NetworkTopologyUtil.ts +139 -36
  350. package/Utils/NetworkDevice/DeviceHealthStateUtil.ts +65 -14
  351. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +43 -14
  352. package/Utils/NetworkDevice/DeviceRoleKeyUtil.ts +137 -0
  353. package/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.ts +247 -0
  354. package/Utils/NetworkDevice/SnmpCredentialUtil.ts +68 -0
  355. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +15 -7
  356. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +52 -17
  357. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +55 -23
  358. package/Utils/NetworkDiscovery/PingMonitorBuilder.ts +115 -13
  359. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +66 -20
  360. package/Utils/NetworkSite/TypeHierarchyUtil.ts +313 -0
  361. package/Utils/Rules/RulePatternMatchUtil.ts +1 -1
  362. package/Utils/Slug.ts +34 -1
  363. package/build/dist/Models/AnalyticsModels/Span.js +14 -0
  364. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  365. package/build/dist/Models/DatabaseModels/AlertState.js +0 -2
  366. package/build/dist/Models/DatabaseModels/AlertState.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/Domain.js +1 -1
  368. package/build/dist/Models/DatabaseModels/Domain.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +59 -0
  370. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  371. package/build/dist/Models/DatabaseModels/Incident.js +1 -1
  372. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  373. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +1 -1
  374. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  375. package/build/dist/Models/DatabaseModels/Index.js +12 -0
  376. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  377. package/build/dist/Models/DatabaseModels/MetricType.js +0 -2
  378. package/build/dist/Models/DatabaseModels/MetricType.js.map +1 -1
  379. package/build/dist/Models/DatabaseModels/Monitor.js +75 -0
  380. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  381. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +0 -2
  382. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  383. package/build/dist/Models/DatabaseModels/MonitorTest.js +0 -2
  384. package/build/dist/Models/DatabaseModels/MonitorTest.js.map +1 -1
  385. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js +713 -0
  386. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js.map +1 -0
  387. package/build/dist/Models/DatabaseModels/NetworkDevice.js +304 -4
  388. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  389. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +3 -1
  390. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -1
  391. package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js +677 -0
  392. package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js.map +1 -0
  393. package/build/dist/Models/DatabaseModels/NetworkSite.js +200 -2
  394. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  395. package/build/dist/Models/DatabaseModels/NetworkSiteType.js +102 -3
  396. package/build/dist/Models/DatabaseModels/NetworkSiteType.js.map +1 -1
  397. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js +878 -0
  398. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js.map +1 -0
  399. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -1
  400. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  401. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -1
  402. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  403. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +0 -2
  404. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  405. package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js +0 -2
  406. package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js.map +1 -1
  407. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +0 -2
  408. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  409. package/build/dist/Models/DatabaseModels/StatusPageResource.js +0 -2
  410. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  411. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +0 -2
  412. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  413. package/build/dist/Models/DatabaseModels/TelemetryException.js +123 -0
  414. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  415. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js +363 -0
  416. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js.map +1 -0
  417. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +402 -0
  418. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -0
  419. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js +296 -0
  420. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js.map +1 -0
  421. package/build/dist/Server/API/EnterpriseLicenseAPI.js +273 -60
  422. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  423. package/build/dist/Server/API/LlmProviderAPI.js +108 -13
  424. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  425. package/build/dist/Server/EnvironmentConfig.js +12 -0
  426. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  427. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js +24 -0
  428. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js.map +1 -0
  429. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js +39 -0
  430. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js.map +1 -0
  431. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js +16 -0
  432. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js.map +1 -0
  433. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js +111 -0
  434. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js.map +1 -0
  435. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js +77 -0
  436. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js.map +1 -0
  437. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js +50 -0
  438. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js.map +1 -0
  439. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js +83 -0
  440. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js.map +1 -0
  441. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  442. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  443. package/build/dist/Server/Infrastructure/QueueWorker.js +33 -5
  444. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  445. package/build/dist/Server/Middleware/BearerTokenAuthorization.js +12 -0
  446. package/build/dist/Server/Middleware/BearerTokenAuthorization.js.map +1 -1
  447. package/build/dist/Server/Middleware/ProjectAuthorization.js +11 -0
  448. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  449. package/build/dist/Server/Middleware/SCIMAuthorization.js +12 -0
  450. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +1 -1
  451. package/build/dist/Server/Middleware/TelemetryIngest.js +11 -0
  452. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  453. package/build/dist/Server/Services/AIBillingService.js +70 -16
  454. package/build/dist/Server/Services/AIBillingService.js.map +1 -1
  455. package/build/dist/Server/Services/DatabaseService.js +31 -5
  456. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  457. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +167 -0
  458. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -1
  459. package/build/dist/Server/Services/EnterpriseLicenseService.js +39 -0
  460. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  461. package/build/dist/Server/Services/Index.js +12 -0
  462. package/build/dist/Server/Services/Index.js.map +1 -1
  463. package/build/dist/Server/Services/MonitorService.js +119 -21
  464. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  465. package/build/dist/Server/Services/MonitorStatusTimelineService.js +127 -8
  466. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  467. package/build/dist/Server/Services/MonitorTemplateService.js +162 -0
  468. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  469. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js +1683 -0
  470. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js.map +1 -0
  471. package/build/dist/Server/Services/NetworkAlertPolicyService.js +732 -0
  472. package/build/dist/Server/Services/NetworkAlertPolicyService.js.map +1 -0
  473. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +10 -9
  474. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  475. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +54 -11
  476. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  477. package/build/dist/Server/Services/NetworkDeviceRoleService.js +70 -0
  478. package/build/dist/Server/Services/NetworkDeviceRoleService.js.map +1 -0
  479. package/build/dist/Server/Services/NetworkDeviceService.js +841 -70
  480. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  481. package/build/dist/Server/Services/NetworkSiteService.js +1145 -107
  482. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  483. package/build/dist/Server/Services/NetworkSiteTypeService.js +863 -0
  484. package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
  485. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js +217 -0
  486. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js.map +1 -0
  487. package/build/dist/Server/Services/NotificationService.js +80 -17
  488. package/build/dist/Server/Services/NotificationService.js.map +1 -1
  489. package/build/dist/Server/Services/ProjectService.js +131 -30
  490. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  491. package/build/dist/Server/Services/StatusPagePrivateUserService.js +92 -2
  492. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  493. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js +76 -0
  494. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js.map +1 -0
  495. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js +77 -0
  496. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js.map +1 -0
  497. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js +111 -0
  498. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js.map +1 -0
  499. package/build/dist/Server/Services/UserNotificationSettingService.js +46 -21
  500. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  501. package/build/dist/Server/Services/UserService.js +90 -0
  502. package/build/dist/Server/Services/UserService.js.map +1 -1
  503. package/build/dist/Server/Types/Database/QueryHelper.js +1 -1
  504. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  505. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +14 -3
  506. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  507. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +10 -0
  508. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  509. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +10 -0
  510. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  511. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +54 -1
  512. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  513. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +18 -4
  514. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  515. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js +59 -0
  516. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js.map +1 -0
  517. package/build/dist/Server/Utils/DataSource/HttpFetch.js +2 -1
  518. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  519. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +91 -0
  520. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -0
  521. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +691 -0
  522. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -0
  523. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +497 -0
  524. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -0
  525. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +257 -0
  526. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -0
  527. package/build/dist/Server/Utils/LLM/LLMService.js +64 -1
  528. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  529. package/build/dist/Server/Utils/LogRedaction.js +8 -0
  530. package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
  531. package/build/dist/Server/Utils/Logger.js +63 -1
  532. package/build/dist/Server/Utils/Logger.js.map +1 -1
  533. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js +141 -0
  534. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js.map +1 -0
  535. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  536. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  537. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +51 -1
  538. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  539. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +7 -0
  540. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
  541. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +37 -0
  542. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  543. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +158 -6
  544. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  545. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  546. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  547. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +32 -0
  548. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  549. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +330 -21
  550. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
  551. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +55 -10
  552. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  553. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +178 -49
  554. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  555. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +119 -29
  556. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  557. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js +30 -17
  558. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js.map +1 -1
  559. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js +158 -0
  560. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js.map +1 -0
  561. package/build/dist/Server/Utils/OutboundUserAgent.js +123 -0
  562. package/build/dist/Server/Utils/OutboundUserAgent.js.map +1 -0
  563. package/build/dist/Server/Utils/StartServer.js +38 -14
  564. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  565. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +28 -8
  566. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  567. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js +95 -0
  568. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js.map +1 -0
  569. package/build/dist/Server/Utils/Telemetry/SpanUtil.js +29 -0
  570. package/build/dist/Server/Utils/Telemetry/SpanUtil.js.map +1 -1
  571. package/build/dist/Server/Utils/Telemetry.js +162 -46
  572. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  573. package/build/dist/Types/AI/ExceptionAIClassification.js +10 -24
  574. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -1
  575. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  576. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  577. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js +2 -0
  578. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js.map +1 -0
  579. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js +7 -0
  580. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js.map +1 -0
  581. package/build/dist/Types/Exception/ApiException.js +9 -0
  582. package/build/dist/Types/Exception/ApiException.js.map +1 -1
  583. package/build/dist/Types/Exception/BadDataException.js +10 -0
  584. package/build/dist/Types/Exception/BadDataException.js.map +1 -1
  585. package/build/dist/Types/Exception/BadRequestException.js +10 -0
  586. package/build/dist/Types/Exception/BadRequestException.js.map +1 -1
  587. package/build/dist/Types/Exception/Exception.js +56 -0
  588. package/build/dist/Types/Exception/Exception.js.map +1 -1
  589. package/build/dist/Types/Exception/ForbiddenException.js +9 -0
  590. package/build/dist/Types/Exception/ForbiddenException.js.map +1 -1
  591. package/build/dist/Types/Exception/NotAuthenticatedException.js +9 -0
  592. package/build/dist/Types/Exception/NotAuthenticatedException.js.map +1 -1
  593. package/build/dist/Types/Exception/NotAuthorizedException.js +9 -0
  594. package/build/dist/Types/Exception/NotAuthorizedException.js.map +1 -1
  595. package/build/dist/Types/Exception/NotFoundException.js +10 -0
  596. package/build/dist/Types/Exception/NotFoundException.js.map +1 -1
  597. package/build/dist/Types/Exception/PayloadTooLargeException.js +10 -0
  598. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -1
  599. package/build/dist/Types/Exception/PaymentRequiredException.js +9 -0
  600. package/build/dist/Types/Exception/PaymentRequiredException.js.map +1 -1
  601. package/build/dist/Types/Exception/ServiceUnavailableException.js +9 -0
  602. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -1
  603. package/build/dist/Types/Exception/SsoAuthorizationException.js +9 -0
  604. package/build/dist/Types/Exception/SsoAuthorizationException.js.map +1 -1
  605. package/build/dist/Types/Exception/TenantNotFoundException.js +10 -0
  606. package/build/dist/Types/Exception/TenantNotFoundException.js.map +1 -1
  607. package/build/dist/Types/Exception/TimeoutException.js +9 -0
  608. package/build/dist/Types/Exception/TimeoutException.js.map +1 -1
  609. package/build/dist/Types/Exception/TooManyRequestsException.js +9 -0
  610. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -1
  611. package/build/dist/Types/Exception/UnableToReachServer.js +9 -0
  612. package/build/dist/Types/Exception/UnableToReachServer.js.map +1 -1
  613. package/build/dist/Types/Exception/WebsiteRequestException.js +9 -0
  614. package/build/dist/Types/Exception/WebsiteRequestException.js.map +1 -1
  615. package/build/dist/Types/Monitor/CriteriaFilter.js +45 -2
  616. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  617. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js +594 -0
  618. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js.map +1 -0
  619. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js +21 -0
  620. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js.map +1 -0
  621. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +70 -0
  622. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  623. package/build/dist/Types/Monitor/MonitorMetricType.js +61 -0
  624. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  625. package/build/dist/Types/Monitor/MonitorStep.js +54 -0
  626. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  627. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js +122 -0
  628. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js.map +1 -0
  629. package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js.map +1 -1
  630. package/build/dist/Types/Monitor/MonitorType.js +45 -1
  631. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  632. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +21 -1
  633. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -1
  634. package/build/dist/Types/Monitor/SqlConnectionConfig.js +2 -0
  635. package/build/dist/Types/Monitor/SqlConnectionConfig.js.map +1 -0
  636. package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js +105 -0
  637. package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js.map +1 -0
  638. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js +172 -0
  639. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js.map +1 -0
  640. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +40 -22
  641. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -1
  642. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js +26 -0
  643. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js.map +1 -0
  644. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js +186 -0
  645. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js.map +1 -0
  646. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js +87 -0
  647. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js.map +1 -0
  648. package/build/dist/Types/Permission.js +120 -0
  649. package/build/dist/Types/Permission.js.map +1 -1
  650. package/build/dist/Types/Telemetry/ErrorClass.js +216 -0
  651. package/build/dist/Types/Telemetry/ErrorClass.js.map +1 -0
  652. package/build/dist/Types/Telemetry/UnitOfWork.js +59 -0
  653. package/build/dist/Types/Telemetry/UnitOfWork.js.map +1 -0
  654. package/build/dist/UI/Components/Charts/Area/AreaChart.js +1 -1
  655. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  656. package/build/dist/UI/Components/Charts/Bar/BarChart.js +1 -1
  657. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  658. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -1
  659. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  660. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +79 -31
  661. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  662. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +7 -1
  663. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  664. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +79 -31
  665. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  666. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +14 -0
  667. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -0
  668. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  669. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  670. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +2 -7
  671. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  672. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +3 -0
  673. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  674. package/build/dist/UI/Components/Modal/Modal.js +2 -7
  675. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  676. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +39 -13
  677. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  678. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +39 -0
  679. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  680. package/build/dist/UI/Components/Navbar/NavBar.js +44 -9
  681. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  682. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  683. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  684. package/build/dist/UI/Components/ProgressBar/ProgressBar.js +12 -1
  685. package/build/dist/UI/Components/ProgressBar/ProgressBar.js.map +1 -1
  686. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +6 -6
  687. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  688. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +1 -1
  689. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  690. package/build/dist/UI/Components/SideMenu/SideMenu.js +76 -11
  691. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  692. package/build/dist/UI/Components/SideMenu/SideMenuItem.js +50 -54
  693. package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
  694. package/build/dist/UI/Components/SideMenu/SideMenuSection.js +32 -9
  695. package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
  696. package/build/dist/UI/Components/SideOver/SideOver.js +5 -20
  697. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  698. package/build/dist/UI/Utils/PageScrollLock.js +31 -3
  699. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -1
  700. package/build/dist/UI/Utils/PermissionGate.js +42 -1
  701. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  702. package/build/dist/UI/Utils/SensitiveUrlToken.js +97 -0
  703. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -0
  704. package/build/dist/UI/Utils/TestLLMProvider.js +4 -5
  705. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -1
  706. package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js +42 -0
  707. package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js.map +1 -0
  708. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +74 -0
  709. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -0
  710. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +15 -0
  711. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -1
  712. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +111 -9
  713. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  714. package/build/dist/Utils/Monitor/MonitorMetricType.js +109 -1
  715. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  716. package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js +126 -0
  717. package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js.map +1 -0
  718. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +2 -1
  719. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
  720. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +75 -35
  721. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  722. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js +27 -7
  723. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js.map +1 -1
  724. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +7 -5
  725. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  726. package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js +114 -0
  727. package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js.map +1 -0
  728. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js +180 -0
  729. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js.map +1 -0
  730. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js +12 -0
  731. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js.map +1 -0
  732. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +15 -5
  733. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  734. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +44 -15
  735. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  736. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +52 -23
  737. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -1
  738. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js +85 -13
  739. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js.map +1 -1
  740. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +30 -15
  741. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  742. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +191 -0
  743. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -0
  744. package/build/dist/Utils/Rules/RulePatternMatchUtil.js +1 -1
  745. package/build/dist/Utils/Slug.js +31 -1
  746. package/build/dist/Utils/Slug.js.map +1 -1
  747. package/package.json +2 -2
@@ -0,0 +1,872 @@
1
+ import DatabaseConfig from "../../DatabaseConfig";
2
+ import Semaphore, { SemaphoreMutex } from "../../Infrastructure/Semaphore";
3
+ import MailService from "../../Services/MailService";
4
+ import ProjectService from "../../Services/ProjectService";
5
+ import UserEmailService from "../../Services/UserEmailService";
6
+ import UserNotificationEmailRollupBatchService from "../../Services/UserNotificationEmailRollupBatchService";
7
+ import UserNotificationEmailRollupItemService from "../../Services/UserNotificationEmailRollupItemService";
8
+ import QueryHelper from "../../Types/Database/QueryHelper";
9
+ import PostgresErrorTranslator from "../Database/PostgresErrorTranslator";
10
+ import logger from "../Logger";
11
+ import CaptureSpan from "../Telemetry/CaptureSpan";
12
+ import {
13
+ CLAIM_EPOCH_MINUTES,
14
+ FLUSH_AFTER_MINUTES,
15
+ MAX_BUCKETS_PER_TICK,
16
+ MAX_DISCOVERY_PAGES_PER_TICK,
17
+ MAX_ITEMS_PER_ROLLUP,
18
+ MAX_ITEMS_SCANNED_PER_TICK,
19
+ ROLLUP_JOB_NAME,
20
+ ROLLUP_SEND_TIMEOUT_MS,
21
+ ROLLUP_SWEEP_BUDGET_MS,
22
+ ROLLUP_SWEEP_LOCK_NAMESPACE,
23
+ ROLLUP_SWEEP_LOCK_TIMEOUT_MS,
24
+ } from "./EmailRollupConstants";
25
+ import { buildRollupEmail, RollupEmail } from "./EmailRollupRenderer";
26
+ import Project from "../../../Models/DatabaseModels/Project";
27
+ import UserEmail from "../../../Models/DatabaseModels/UserEmail";
28
+ import UserNotificationEmailRollupBatch, {
29
+ RollupBatchStatus,
30
+ } from "../../../Models/DatabaseModels/UserNotificationEmailRollupBatch";
31
+ import UserNotificationEmailRollupItem from "../../../Models/DatabaseModels/UserNotificationEmailRollupItem";
32
+ import URL from "../../../Types/API/URL";
33
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
34
+ import ColumnLength from "../../../Types/Database/ColumnLength";
35
+ import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
36
+ import PartialEntity from "../../../Types/Database/PartialEntity";
37
+ import OneUptimeDate from "../../../Types/Date";
38
+ import Email from "../../../Types/Email";
39
+ import EmailTemplateType from "../../../Types/Email/EmailTemplateType";
40
+ import ObjectID from "../../../Types/ObjectID";
41
+ import PositiveNumber from "../../../Types/PositiveNumber";
42
+ import Text from "../../../Types/Text";
43
+
44
+ /*
45
+ * Why this file exists, and why it lives in Common rather than next to the
46
+ * cron that calls it: this is the half of the owner-email rollup that has to
47
+ * be provably exactly-once, and putting it here means Common/Tests can drive
48
+ * the REAL sweep end to end with no queue, no Redis and no Postgres. The App
49
+ * file under Workers/Jobs/EmailRollup is only the scheduling half - exactly
50
+ * the OnCallShiftReminderRunner / SendShiftReminders split.
51
+ *
52
+ * WHAT ONE TICK DOES. Find every rollup item that is still pending and old
53
+ * enough to be due, group them by (project, user, address), and for each such
54
+ * bucket send ONE email carrying everything still pending for that address -
55
+ * across every category. Nothing is suppressed and nothing is summarised
56
+ * away; the recipient gets the same information in one message instead of N.
57
+ *
58
+ * THE EXACTLY-ONCE ARGUMENT, IN THE ORDER THE LAYERS ACTUALLY MATTER.
59
+ *
60
+ * 1. A row INSERTED under the batch table's UNIQUE index over
61
+ * (projectId, userId, toEmail, claimEpochStartsAt). This is the real
62
+ * mutual exclusion and the only one that is genuinely atomic. Two
63
+ * replicas that decide the same address is due at the same instant both
64
+ * insert, Postgres lets exactly one through, and the loser recognises
65
+ * 23505 via PostgresErrorTranslator.isUniqueViolation and stands down.
66
+ *
67
+ * 2. Stamp the claimed items, then READ THEM BACK BY rollupBatchId, so a
68
+ * replica renders exactly the rows it wrote and never a row a concurrent
69
+ * flush stamped underneath it.
70
+ *
71
+ * 3. The Redis sweep lock, which is an OPTIMISATION and nothing more: it
72
+ * stops N replicas doing the same scan every minute. If Redis is down
73
+ * the tick is skipped and the next one covers the same work; if the lock
74
+ * were removed entirely, layers 1 and 2 would still be correct.
75
+ *
76
+ * WHAT IS DELIBERATELY NOT USED: a conditional updateOneBy as a
77
+ * compare-and-swap. DatabaseService._updateBy resolves its predicate in a
78
+ * separate find and then issues one update per matched row with NO predicate
79
+ * re-check at write time, so a "claim by conditional update" is a
80
+ * check-then-act race dressed up as an atomic one. An INSERT under a unique
81
+ * index is the only primitive here that actually is atomic, so the claim is
82
+ * built on that and on nothing else.
83
+ */
84
+
85
+ /*
86
+ * One (project, user, address) whose pending items are due. The address is
87
+ * part of the key because the write path already fans out one notification
88
+ * per verified address, and a rollup has to arrive at the same address the
89
+ * individual emails would have.
90
+ */
91
+ export interface RollupBucket {
92
+ projectId: ObjectID;
93
+ userId: ObjectID;
94
+ toEmail: Email;
95
+ }
96
+
97
+ /*
98
+ * What one flush attempt ended up doing. Returned rather than logged so the
99
+ * sweep can count outcomes and a test can assert on them without reading log
100
+ * lines.
101
+ */
102
+ export enum RollupFlushOutcome {
103
+ // Another replica, or an earlier flush in this same epoch, owns the bucket.
104
+ Collision = "collision",
105
+ // The claim won but nothing was still pending by the time it stamped.
106
+ Empty = "empty",
107
+ // Deliberately not sent: the address or the project went away.
108
+ Skipped = "skipped",
109
+ Sent = "sent",
110
+ Failed = "failed",
111
+ }
112
+
113
+ export interface RollupSweepStats {
114
+ now: Date;
115
+ cutoff: Date;
116
+ itemsScanned: number;
117
+ bucketsDue: number;
118
+ bucketsProcessed: number;
119
+ sent: number;
120
+ empty: number;
121
+ skipped: number;
122
+ failed: number;
123
+ claimCollisions: number;
124
+ errors: number;
125
+ // True when the wall-clock budget ended the loop before every bucket ran.
126
+ budgetExhausted: boolean;
127
+ }
128
+
129
+ const MILLISECONDS_PER_MINUTE: number = 60 * 1000;
130
+
131
+ /*
132
+ * The batch table's statusMessage column is LongText, and a value longer than
133
+ * the column throws inside DatabaseService's own length check - which would
134
+ * turn "the send failed and we recorded why" into "the send failed and the
135
+ * recording of why also failed". Taken from the column length itself so the
136
+ * two can never drift apart.
137
+ */
138
+ const STATUS_MESSAGE_MAX_LENGTH: number = ColumnLength.LongText;
139
+
140
+ export default class EmailRollupFlushRunner {
141
+ /**
142
+ * The once-a-minute cron body: one sweep under a cross-replica lock.
143
+ *
144
+ * The lock is an OPTIMISATION, not a correctness mechanism. It exists so
145
+ * that N replicas do not all run the same scan every minute; the claim
146
+ * insert underneath it is what actually makes a rollup exactly-once. A held
147
+ * lock or an unreachable Redis therefore just skips the tick - the holder is
148
+ * covering the same work, and if nobody is, the next tick will.
149
+ */
150
+ @CaptureSpan()
151
+ public static async runSweepUnderLock(options?: {
152
+ now?: Date | undefined;
153
+ }): Promise<RollupSweepStats | null> {
154
+ let mutex: SemaphoreMutex | null = null;
155
+
156
+ try {
157
+ mutex = await Semaphore.lock({
158
+ key: ROLLUP_JOB_NAME,
159
+ namespace: ROLLUP_SWEEP_LOCK_NAMESPACE,
160
+ lockTimeout: ROLLUP_SWEEP_LOCK_TIMEOUT_MS,
161
+ acquireAttemptsLimit: 1,
162
+ });
163
+ } catch (err) {
164
+ logger.debug(
165
+ `${ROLLUP_JOB_NAME}: could not acquire the sweep lock; a sweep is already in flight (or Redis is unavailable). Skipping this run: ${err}`,
166
+ );
167
+ return null;
168
+ }
169
+
170
+ try {
171
+ return await EmailRollupFlushRunner.runSweep(options);
172
+ } finally {
173
+ try {
174
+ await Semaphore.release(mutex);
175
+ } catch (err) {
176
+ logger.error(
177
+ `${ROLLUP_JOB_NAME}: error releasing the sweep lock: ${err}`,
178
+ );
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * One sweep: discover the due buckets oldest-first and flush each of them
185
+ * once. Never throws for one bucket's bad data - a bucket that fails is
186
+ * logged and the rest of the tick continues.
187
+ */
188
+ @CaptureSpan()
189
+ public static async runSweep(options?: {
190
+ now?: Date | undefined;
191
+ }): Promise<RollupSweepStats> {
192
+ const now: Date = options?.now || OneUptimeDate.getCurrentDate();
193
+ const startMs: number = Date.now();
194
+ const cutoff: Date = OneUptimeDate.addRemoveMinutes(
195
+ now,
196
+ FLUSH_AFTER_MINUTES * -1,
197
+ );
198
+
199
+ /*
200
+ * DISCOVERY IS BOUNDED, AND findBy IS THE POINT - not findAllBy.
201
+ * findAllBy pages to exhaustion, so a backlog spike would make this one
202
+ * query walk the entire pending table and blow the job's timeout part way
203
+ * through the loop, AFTER some buckets had already stamped their items but
204
+ * before they sent. An explicit limit means a backlog drains over several
205
+ * ticks instead, oldest first, which is also fairer across tenants.
206
+ *
207
+ * IT PAGES, THOUGH, AND THAT IS NOT AN OPTIMISATION. One page of
208
+ * MAX_ITEMS_SCANNED_PER_TICK rows sorted oldest-first can be entirely one
209
+ * saturated recipient: a single (project, user, address) with more than
210
+ * that many pending items would fill the page by itself, and every OTHER
211
+ * tenant's due rollup would be invisible for as long as the saturation
212
+ * lasted - a fleet-wide stall caused by one project, in exactly the storm
213
+ * this feature exists for. Paging past a hog costs a few extra indexed
214
+ * reads in a case that should never happen, and buys the guarantee that it
215
+ * cannot starve anybody.
216
+ */
217
+ const seen: Set<string> = new Set<string>();
218
+ const buckets: Array<RollupBucket> = [];
219
+ let itemsScanned: number = 0;
220
+
221
+ for (let page: number = 0; page < MAX_DISCOVERY_PAGES_PER_TICK; page++) {
222
+ const due: Array<UserNotificationEmailRollupItem> =
223
+ await UserNotificationEmailRollupItemService.findBy({
224
+ query: {
225
+ sentAt: QueryHelper.isNull(),
226
+ createdAt: QueryHelper.lessThanEqualTo(cutoff),
227
+ },
228
+ select: {
229
+ projectId: true,
230
+ userId: true,
231
+ toEmail: true,
232
+ createdAt: true,
233
+ },
234
+ sort: {
235
+ createdAt: SortOrder.Ascending,
236
+ },
237
+ limit: MAX_ITEMS_SCANNED_PER_TICK,
238
+ skip: page * MAX_ITEMS_SCANNED_PER_TICK,
239
+ props: {
240
+ isRoot: true,
241
+ },
242
+ });
243
+
244
+ itemsScanned += due.length;
245
+
246
+ /*
247
+ * Dedupe in JS, preserving first-seen order. The rows arrive
248
+ * oldest-first, so first-seen order IS oldest-bucket-first: the address
249
+ * that has been waiting longest is served first when the tick cannot
250
+ * serve everybody.
251
+ */
252
+ for (const item of due) {
253
+ if (!item.projectId || !item.userId || !item.toEmail) {
254
+ continue;
255
+ }
256
+
257
+ const key: string = `${item.projectId.toString()}|${item.userId.toString()}|${item.toEmail.toString()}`;
258
+
259
+ if (seen.has(key)) {
260
+ continue;
261
+ }
262
+
263
+ seen.add(key);
264
+ buckets.push({
265
+ projectId: item.projectId,
266
+ userId: item.userId,
267
+ toEmail: item.toEmail,
268
+ });
269
+ }
270
+
271
+ // A short page is the end of the pending set; there is nothing behind it.
272
+ if (due.length < MAX_ITEMS_SCANNED_PER_TICK) {
273
+ break;
274
+ }
275
+
276
+ // Enough work for this tick. The rest keeps until the next one.
277
+ if (buckets.length >= MAX_BUCKETS_PER_TICK) {
278
+ break;
279
+ }
280
+ }
281
+
282
+ const bucketsThisTick: Array<RollupBucket> = buckets.slice(
283
+ 0,
284
+ MAX_BUCKETS_PER_TICK,
285
+ );
286
+
287
+ const stats: RollupSweepStats = {
288
+ now: now,
289
+ cutoff: cutoff,
290
+ itemsScanned: itemsScanned,
291
+ bucketsDue: buckets.length,
292
+ bucketsProcessed: 0,
293
+ sent: 0,
294
+ empty: 0,
295
+ skipped: 0,
296
+ failed: 0,
297
+ claimCollisions: 0,
298
+ errors: 0,
299
+ budgetExhausted: false,
300
+ };
301
+
302
+ for (const bucket of bucketsThisTick) {
303
+ if (Date.now() - startMs > ROLLUP_SWEEP_BUDGET_MS) {
304
+ stats.budgetExhausted = true;
305
+ logger.warn(
306
+ `${ROLLUP_JOB_NAME}: sweep budget exhausted after ${stats.bucketsProcessed} of ${bucketsThisTick.length} bucket(s); the rest are picked up next tick.`,
307
+ );
308
+ break;
309
+ }
310
+
311
+ try {
312
+ const outcome: RollupFlushOutcome =
313
+ await EmailRollupFlushRunner.flushBucket(bucket, now);
314
+
315
+ stats.bucketsProcessed = stats.bucketsProcessed + 1;
316
+
317
+ if (outcome === RollupFlushOutcome.Sent) {
318
+ stats.sent = stats.sent + 1;
319
+ } else if (outcome === RollupFlushOutcome.Empty) {
320
+ stats.empty = stats.empty + 1;
321
+ } else if (outcome === RollupFlushOutcome.Skipped) {
322
+ stats.skipped = stats.skipped + 1;
323
+ } else if (outcome === RollupFlushOutcome.Failed) {
324
+ stats.failed = stats.failed + 1;
325
+ } else {
326
+ stats.claimCollisions = stats.claimCollisions + 1;
327
+ }
328
+ } catch (err) {
329
+ /*
330
+ * PER-BUCKET ISOLATION. One tenant's unreadable row, or a claim that
331
+ * failed for a reason that is not a duplicate, must not cost every
332
+ * other address on this tick its rollup.
333
+ */
334
+ stats.errors = stats.errors + 1;
335
+ logger.error(err, {
336
+ projectId: bucket.projectId.toString(),
337
+ userId: bucket.userId.toString(),
338
+ });
339
+ }
340
+ }
341
+
342
+ return stats;
343
+ }
344
+
345
+ /**
346
+ * Claim one bucket's epoch, stamp its pending items, and send one rollup.
347
+ */
348
+ private static async flushBucket(
349
+ bucket: RollupBucket,
350
+ now: Date,
351
+ ): Promise<RollupFlushOutcome> {
352
+ /*
353
+ * THE CLAIM KEY IS DERIVED FROM THE WALL CLOCK AND NOTHING ELSE.
354
+ *
355
+ * That is the whole reason the unique index is a real mutual exclusion:
356
+ * two replicas computing floor(now / epoch) at any instant inside the same
357
+ * epoch necessarily get the same value, so they necessarily collide.
358
+ * Derive it from the data instead - min(createdAt) of the pending rows,
359
+ * say - and two replicas reading slightly different snapshots would mint
360
+ * two different keys and both send.
361
+ *
362
+ * AND IT NEVER BLOCKS A LEGITIMATE FLUSH, because CLAIM_EPOCH_MINUTES
363
+ * equals FLUSH_AFTER_MINUTES. A bucket only becomes due once its OLDEST
364
+ * pending item is FLUSH_AFTER_MINUTES old, and a flush stamps every
365
+ * pending row in the bucket - so the next legitimate flush of the same
366
+ * bucket cannot happen until at least FLUSH_AFTER_MINUTES later, which is
367
+ * a strictly later epoch. What the index does block is a SECOND flush
368
+ * inside the same epoch, which is exactly the duplicate we want gone, and
369
+ * that is also where the hard "at most 60 / CLAIM_EPOCH_MINUTES rollups
370
+ * per hour per address" bound comes from.
371
+ */
372
+ const epochMs: number = CLAIM_EPOCH_MINUTES * MILLISECONDS_PER_MINUTE;
373
+ const epochStart: Date = new Date(
374
+ Math.floor(now.getTime() / epochMs) * epochMs,
375
+ );
376
+
377
+ const batch: UserNotificationEmailRollupBatch =
378
+ new UserNotificationEmailRollupBatch();
379
+ batch.projectId = bucket.projectId;
380
+ batch.userId = bucket.userId;
381
+ batch.toEmail = bucket.toEmail;
382
+ batch.claimEpochStartsAt = epochStart;
383
+ batch.claimedAt = now;
384
+ batch.status = RollupBatchStatus.Claimed;
385
+
386
+ let created: UserNotificationEmailRollupBatch;
387
+
388
+ try {
389
+ created = await UserNotificationEmailRollupBatchService.create({
390
+ data: batch,
391
+ props: {
392
+ isRoot: true,
393
+ },
394
+ });
395
+ } catch (err) {
396
+ if (PostgresErrorTranslator.isUniqueViolation(err)) {
397
+ /*
398
+ * Somebody else owns this bucket's epoch. Silently, and without
399
+ * touching a single item row: the winner is about to stamp them.
400
+ */
401
+ return RollupFlushOutcome.Collision;
402
+ }
403
+
404
+ throw err;
405
+ }
406
+
407
+ const batchId: ObjectID | null = created.id;
408
+
409
+ if (!batchId) {
410
+ throw new Error(
411
+ `${ROLLUP_JOB_NAME}: the rollup batch claim returned no id.`,
412
+ );
413
+ }
414
+
415
+ /*
416
+ * EVERYTHING PAST THE CLAIM IS INSIDE THIS TRY, and that is the point of
417
+ * splitting the method here.
418
+ *
419
+ * Once the stamp lands, up to MAX_ITEMS_PER_ROLLUP notifications exist
420
+ * ONLY as an obligation to send this one email. If anything between the
421
+ * stamp and the send throws - the recipient re-validation, the project
422
+ * lookup, the dashboard URL, the renderer, a bug added here later - and
423
+ * the exception is merely logged by the sweep's per-bucket handler, those
424
+ * notifications are gone: stamped so nothing will ever pick them up again,
425
+ * with a batch row still saying Claimed and no trace of what happened.
426
+ * Recording Failed with the reason is the difference between a bounded,
427
+ * diagnosable loss and a silent one.
428
+ */
429
+ try {
430
+ return await EmailRollupFlushRunner.sendClaimedBatch(
431
+ bucket,
432
+ batchId,
433
+ now,
434
+ );
435
+ } catch (err) {
436
+ logger.error(err, {
437
+ projectId: bucket.projectId.toString(),
438
+ userId: bucket.userId.toString(),
439
+ });
440
+
441
+ try {
442
+ /*
443
+ * Counted rather than assumed zero. If the throw happened after the
444
+ * stamp, rows are already carrying this batch's id and the whole point
445
+ * of recording Failed is to say how many notifications went with it; a
446
+ * hard-coded 0 would make the ledger claim a loss of nothing. One
447
+ * extra query, only on the failure path.
448
+ */
449
+ const stampedCount: PositiveNumber =
450
+ await UserNotificationEmailRollupItemService.countBy({
451
+ query: {
452
+ rollupBatchId: batchId,
453
+ },
454
+ props: {
455
+ isRoot: true,
456
+ },
457
+ });
458
+
459
+ await EmailRollupFlushRunner.finish({
460
+ batchId: batchId,
461
+ status: RollupBatchStatus.Failed,
462
+ itemCount: stampedCount.toNumber(),
463
+ now: now,
464
+ message: EmailRollupFlushRunner.describeError(err),
465
+ });
466
+ } catch (finishErr) {
467
+ // Nothing further to try; the batch row keeps its Claimed status.
468
+ logger.error(finishErr);
469
+ }
470
+
471
+ return RollupFlushOutcome.Failed;
472
+ }
473
+ }
474
+
475
+ /*
476
+ * The half of a flush that runs after the epoch has been claimed: stamp the
477
+ * oldest pending rows into this batch, render them, and send. Separated so
478
+ * every failure in it is caught and recorded against the batch rather than
479
+ * escaping to the sweep loop, where it would leave stamped-but-unsent rows.
480
+ */
481
+ private static async sendClaimedBatch(
482
+ bucket: RollupBucket,
483
+ batchId: ObjectID,
484
+ now: Date,
485
+ ): Promise<RollupFlushOutcome> {
486
+ /*
487
+ * STAMP BEFORE SEND, and bounded.
488
+ *
489
+ * The alternative - send first, stamp after - means a permanently broken
490
+ * SMTP server produces a rollup that grows every epoch and is re-sent
491
+ * every epoch, forever. That is precisely the storm this feature exists to
492
+ * stop, so it is not an acceptable failure mode here even though it is the
493
+ * more forgiving one elsewhere. The price is that a hard send failure
494
+ * loses that batch's notifications; that loss is bounded at
495
+ * MAX_ITEMS_PER_ROLLUP, is written down on the batch row with its reason,
496
+ * and the underlying resources are all still in the dashboard.
497
+ *
498
+ * The query deliberately DROPS the cutoff and takes every pending row for
499
+ * the bucket, including ones written seconds ago. Coalescing more is
500
+ * strictly better for the recipient, and it can only ever make an item's
501
+ * latency shorter than the schedule promised, never longer.
502
+ *
503
+ * THE OLDEST ROWS ARE SELECTED EXPLICITLY, AND THAT TAKES TWO STEPS.
504
+ * updateBy resolves the rows it will write with an internal _findBy that
505
+ * takes no sort from the caller (UpdateBy has no sort field) and therefore
506
+ * gets DatabaseService's default of createdAt DESCENDING. A bare
507
+ * `updateBy(..., limit: 500)` on an over-full bucket would consequently
508
+ * stamp the NEWEST five hundred and leave the oldest pending - a LIFO
509
+ * queue whose tail is never served, and whose tail is then hard-deleted
510
+ * unsent at ROLLUP_ITEM_RETENTION_DAYS. Selecting the ids oldest-first and
511
+ * updating exactly those makes the drain FIFO, which is the only ordering
512
+ * a notification queue may have.
513
+ */
514
+ const oldestPending: Array<UserNotificationEmailRollupItem> =
515
+ await UserNotificationEmailRollupItemService.findBy({
516
+ query: {
517
+ projectId: bucket.projectId,
518
+ userId: bucket.userId,
519
+ toEmail: bucket.toEmail,
520
+ sentAt: QueryHelper.isNull(),
521
+ },
522
+ select: {
523
+ _id: true,
524
+ },
525
+ sort: {
526
+ createdAt: SortOrder.Ascending,
527
+ },
528
+ limit: MAX_ITEMS_PER_ROLLUP,
529
+ skip: 0,
530
+ props: {
531
+ isRoot: true,
532
+ },
533
+ });
534
+
535
+ const pendingIds: Array<ObjectID> = oldestPending
536
+ .map((item: UserNotificationEmailRollupItem): ObjectID | null => {
537
+ return item.id;
538
+ })
539
+ .filter((id: ObjectID | null): id is ObjectID => {
540
+ return id !== null;
541
+ });
542
+
543
+ if (pendingIds.length === 0) {
544
+ await EmailRollupFlushRunner.finish({
545
+ batchId: batchId,
546
+ status: RollupBatchStatus.Empty,
547
+ itemCount: 0,
548
+ now: now,
549
+ });
550
+ return RollupFlushOutcome.Empty;
551
+ }
552
+
553
+ const stamped: number =
554
+ await UserNotificationEmailRollupItemService.updateBy({
555
+ query: {
556
+ _id: QueryHelper.any(
557
+ pendingIds.map((id: ObjectID): string => {
558
+ return id.toString();
559
+ }),
560
+ ),
561
+ /*
562
+ * Belt and braces with the id list: a row that somebody else stamped
563
+ * between the select above and this write must not be re-stamped
564
+ * into this batch, or it would appear in two rollups.
565
+ */
566
+ sentAt: QueryHelper.isNull(),
567
+ },
568
+ data: {
569
+ sentAt: now,
570
+ rollupBatchId: batchId,
571
+ },
572
+ limit: MAX_ITEMS_PER_ROLLUP,
573
+ skip: 0,
574
+ props: {
575
+ isRoot: true,
576
+ ignoreHooks: true,
577
+ },
578
+ });
579
+
580
+ if (stamped === 0) {
581
+ await EmailRollupFlushRunner.finish({
582
+ batchId: batchId,
583
+ status: RollupBatchStatus.Empty,
584
+ itemCount: 0,
585
+ now: now,
586
+ });
587
+ return RollupFlushOutcome.Empty;
588
+ }
589
+
590
+ /*
591
+ * READ BACK BY BATCH, never by "still pending for this bucket": this is
592
+ * what guarantees the email describes exactly the rows this attempt owns.
593
+ */
594
+ const items: Array<UserNotificationEmailRollupItem> =
595
+ await UserNotificationEmailRollupItemService.findBy({
596
+ query: {
597
+ rollupBatchId: batchId,
598
+ },
599
+ select: {
600
+ eventType: true,
601
+ rollupCategory: true,
602
+ subject: true,
603
+ viewLink: true,
604
+ createdAt: true,
605
+ },
606
+ sort: {
607
+ createdAt: SortOrder.Ascending,
608
+ },
609
+ limit: MAX_ITEMS_PER_ROLLUP,
610
+ skip: 0,
611
+ props: {
612
+ isRoot: true,
613
+ },
614
+ });
615
+
616
+ if (items.length === 0) {
617
+ await EmailRollupFlushRunner.finish({
618
+ batchId: batchId,
619
+ status: RollupBatchStatus.Empty,
620
+ itemCount: 0,
621
+ now: now,
622
+ });
623
+ return RollupFlushOutcome.Empty;
624
+ }
625
+
626
+ /*
627
+ * RE-VALIDATE THE RECIPIENT. The address was snapshotted minutes ago when
628
+ * the first item was queued, and the individual sends this rollup replaces
629
+ * would each have re-read the verified set at send time. An address that
630
+ * has since been unverified or removed must not be handed a list of this
631
+ * project's incident subjects.
632
+ */
633
+ const verified: Array<UserEmail> = await UserEmailService.findBy({
634
+ query: {
635
+ userId: bucket.userId,
636
+ projectId: bucket.projectId,
637
+ isVerified: true,
638
+ },
639
+ select: {
640
+ email: true,
641
+ },
642
+ limit: LIMIT_PER_PROJECT,
643
+ skip: 0,
644
+ props: {
645
+ isRoot: true,
646
+ },
647
+ });
648
+
649
+ const stillVerified: boolean = verified.some(
650
+ (userEmail: UserEmail): boolean => {
651
+ return userEmail.email?.toString() === bucket.toEmail.toString();
652
+ },
653
+ );
654
+
655
+ if (!stillVerified) {
656
+ await EmailRollupFlushRunner.finish({
657
+ batchId: batchId,
658
+ status: RollupBatchStatus.Skipped,
659
+ itemCount: items.length,
660
+ now: now,
661
+ message: "address is no longer a verified email for this user",
662
+ });
663
+ return RollupFlushOutcome.Skipped;
664
+ }
665
+
666
+ /*
667
+ * The project name is READ, not snapshotted at enqueue: one query per
668
+ * rollup email, always showing the project's current name, and it doubles
669
+ * as the "has this project been deleted while items were queued?" check.
670
+ */
671
+ const project: Project | null = await ProjectService.findOneById({
672
+ id: bucket.projectId,
673
+ select: {
674
+ name: true,
675
+ },
676
+ props: {
677
+ isRoot: true,
678
+ },
679
+ });
680
+
681
+ if (!project) {
682
+ await EmailRollupFlushRunner.finish({
683
+ batchId: batchId,
684
+ status: RollupBatchStatus.Skipped,
685
+ itemCount: items.length,
686
+ now: now,
687
+ message: "project not found",
688
+ });
689
+ return RollupFlushOutcome.Skipped;
690
+ }
691
+
692
+ const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
693
+ /*
694
+ * addRoute mutates the URL it is called on, so each link starts from a
695
+ * fresh URL parsed out of the dashboard URL's string rather than from the
696
+ * shared instance.
697
+ */
698
+ const projectHomeLink: string = URL.fromString(dashboardUrl.toString())
699
+ .addRoute(`/${bucket.projectId.toString()}/home`)
700
+ .toString();
701
+ const preferencesLink: string = URL.fromString(dashboardUrl.toString())
702
+ .addRoute(
703
+ `/${bucket.projectId.toString()}/user-settings/notification-settings`,
704
+ )
705
+ .toString();
706
+
707
+ const built: RollupEmail = buildRollupEmail({
708
+ projectName: project.name ?? "",
709
+ projectHomeLink: projectHomeLink,
710
+ preferencesLink: preferencesLink,
711
+ items: items,
712
+ });
713
+
714
+ try {
715
+ /*
716
+ * AWAITED, unlike the fire-and-forget individual sends: a rollup that
717
+ * failed has to be recorded as Failed, and the only way to know is to
718
+ * wait for it.
719
+ *
720
+ * The options carry projectId and userId and NOTHING else. projectId is
721
+ * required for MailService to write an EmailLog row at all, so omitting
722
+ * it would make rollups invisible in Notification Logs; and a rollup
723
+ * spans many resources, so there is no single incident, alert or monitor
724
+ * it could honestly be correlated with.
725
+ */
726
+ await EmailRollupFlushRunner.withSendTimeout(
727
+ MailService.sendMail(
728
+ {
729
+ toEmail: bucket.toEmail,
730
+ subject: built.subject,
731
+ templateType: EmailTemplateType.NotificationRollup,
732
+ vars: built.vars,
733
+ },
734
+ {
735
+ projectId: bucket.projectId,
736
+ userId: bucket.userId,
737
+ },
738
+ ),
739
+ );
740
+ } catch (err) {
741
+ logger.error(err, {
742
+ projectId: bucket.projectId.toString(),
743
+ userId: bucket.userId.toString(),
744
+ });
745
+
746
+ await EmailRollupFlushRunner.finish({
747
+ batchId: batchId,
748
+ status: RollupBatchStatus.Failed,
749
+ itemCount: items.length,
750
+ now: now,
751
+ message: EmailRollupFlushRunner.describeError(err),
752
+ });
753
+
754
+ return RollupFlushOutcome.Failed;
755
+ }
756
+
757
+ await EmailRollupFlushRunner.finish({
758
+ batchId: batchId,
759
+ status: RollupBatchStatus.Sent,
760
+ itemCount: items.length,
761
+ now: now,
762
+ });
763
+
764
+ return RollupFlushOutcome.Sent;
765
+ }
766
+
767
+ /**
768
+ * Close out a claim row. sentAt is stamped only for a real send, so a batch
769
+ * row with a NULL sentAt and a non-Claimed status is always a rollup that
770
+ * did not go out, and statusMessage says why.
771
+ */
772
+ private static async finish(data: {
773
+ batchId: ObjectID;
774
+ status: RollupBatchStatus;
775
+ itemCount: number;
776
+ now: Date;
777
+ message?: string | undefined;
778
+ }): Promise<void> {
779
+ const update: PartialEntity<UserNotificationEmailRollupBatch> = {
780
+ status: data.status,
781
+ itemCount: data.itemCount,
782
+ };
783
+
784
+ if (data.status === RollupBatchStatus.Sent) {
785
+ update.sentAt = data.now;
786
+ }
787
+
788
+ if (data.message !== undefined) {
789
+ /*
790
+ * Guarded assignment: exactOptionalPropertyTypes means an optional
791
+ * property will not take an explicit undefined.
792
+ */
793
+ update.statusMessage =
794
+ Text.truncate(data.message, STATUS_MESSAGE_MAX_LENGTH) ?? "";
795
+ }
796
+
797
+ await UserNotificationEmailRollupBatchService.updateOneById({
798
+ id: data.batchId,
799
+ data: update,
800
+ props: {
801
+ isRoot: true,
802
+ },
803
+ });
804
+ }
805
+
806
+ /*
807
+ * A thrown value is `unknown`, and the one thing the batch row must never do
808
+ * is fail to record a failure - so this never touches anything that could
809
+ * throw on a hostile value.
810
+ */
811
+ private static describeError(err: unknown): string {
812
+ if (err instanceof Error && err.message) {
813
+ return err.message;
814
+ }
815
+
816
+ if (typeof err === "string") {
817
+ return err;
818
+ }
819
+
820
+ return "unknown error";
821
+ }
822
+
823
+ /*
824
+ * Rejects if the send has not settled within ROLLUP_SEND_TIMEOUT_MS. See
825
+ * that constant for why a hung send is worse here than anywhere else.
826
+ *
827
+ * The underlying request is NOT cancelled - nothing in the stack can cancel
828
+ * it - so the mail may still go out afterwards. That is the right trade: the
829
+ * cost of being wrong is one duplicate rollup for one recipient, and the
830
+ * cost of not doing it is every rollup on the install stopping forever. The
831
+ * batch is recorded Failed either way, so the duplicate is explicable.
832
+ *
833
+ * The timer is always cleared, including on the happy path, so a pending
834
+ * setTimeout can never hold the worker process open at shutdown.
835
+ */
836
+ private static async withSendTimeout(send: Promise<unknown>): Promise<void> {
837
+ let timer: ReturnType<typeof setTimeout> | undefined = undefined;
838
+
839
+ const timeout: Promise<never> = new Promise<never>(
840
+ (
841
+ _resolve: (value: never) => void,
842
+ reject: (err: Error) => void,
843
+ ): void => {
844
+ timer = setTimeout((): void => {
845
+ reject(
846
+ new Error(
847
+ `${ROLLUP_JOB_NAME}: the rollup send did not complete within ${ROLLUP_SEND_TIMEOUT_MS}ms.`,
848
+ ),
849
+ );
850
+ }, ROLLUP_SEND_TIMEOUT_MS);
851
+ },
852
+ );
853
+
854
+ try {
855
+ await Promise.race([send, timeout]);
856
+ } finally {
857
+ if (timer !== undefined) {
858
+ clearTimeout(timer);
859
+ }
860
+
861
+ /*
862
+ * Promise.race leaves the loser unhandled. When the timeout wins, the
863
+ * send is still in flight and will eventually settle; without this its
864
+ * rejection would surface as an unhandled rejection and, depending on
865
+ * the runtime's setting, take the worker down.
866
+ */
867
+ send.catch((err: unknown): void => {
868
+ logger.error(err);
869
+ });
870
+ }
871
+ }
872
+ }