@oneuptime/common 12.0.7 → 12.0.9

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 (873) hide show
  1. package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
  2. package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
  3. package/Models/DatabaseModels/AIConversation.ts +32 -0
  4. package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
  5. package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
  6. package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
  7. package/Models/DatabaseModels/Index.ts +12 -4
  8. package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +184 -9
  9. package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
  10. package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +33 -7
  11. package/Models/DatabaseModels/NetworkDevice.ts +141 -0
  12. package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
  13. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
  14. package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
  15. package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
  16. package/Models/DatabaseModels/Project.ts +78 -0
  17. package/Models/DatabaseModels/UserCall.ts +42 -0
  18. package/Models/DatabaseModels/UserEmail.ts +44 -0
  19. package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
  20. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
  21. package/Models/DatabaseModels/UserPush.ts +49 -0
  22. package/Models/DatabaseModels/UserSMS.ts +43 -0
  23. package/Models/DatabaseModels/UserTelegram.ts +59 -0
  24. package/Models/DatabaseModels/UserWebhook.ts +52 -0
  25. package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
  26. package/Models/DatabaseModels/WorkflowLog.ts +38 -0
  27. package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
  28. package/Server/API/AIChatAPI.ts +315 -1
  29. package/Server/API/DashboardAPI.ts +217 -1
  30. package/Server/API/OnCallReadinessAPI.ts +841 -0
  31. package/Server/API/TeamComplianceAPI.ts +69 -17
  32. package/Server/API/TelemetryAPI.ts +220 -12
  33. package/Server/API/UserAPI.ts +16 -1
  34. package/Server/EnvironmentConfig.ts +52 -0
  35. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
  47. package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
  48. package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
  49. package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
  50. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +24 -0
  51. package/Server/Infrastructure/Queue.ts +78 -13
  52. package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
  53. package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
  54. package/Server/Services/AIService.ts +7 -0
  55. package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
  56. package/Server/Services/AlertSeverityService.ts +63 -0
  57. package/Server/Services/DashboardService.ts +9 -10
  58. package/Server/Services/DatabaseService.ts +32 -2
  59. package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
  60. package/Server/Services/IncidentSeverityService.ts +76 -0
  61. package/Server/Services/Index.ts +12 -4
  62. package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
  63. package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +7 -4
  64. package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +203 -21
  65. package/Server/Services/LogAggregationService.ts +45 -8
  66. package/Server/Services/MetricAggregationService.ts +121 -0
  67. package/Server/Services/MetricService.ts +7 -7
  68. package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
  69. package/Server/Services/NetworkDeviceLinkService.ts +84 -0
  70. package/Server/Services/NetworkDeviceService.ts +140 -0
  71. package/Server/Services/NetworkSiteService.ts +77 -25
  72. package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
  73. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
  74. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
  75. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
  76. package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
  77. package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
  78. package/Server/Services/OnCallReadinessService.ts +2803 -0
  79. package/Server/Services/OnCallSetupReminderService.ts +955 -0
  80. package/Server/Services/ProfileAggregationService.ts +123 -0
  81. package/Server/Services/StatusPageService.ts +9 -10
  82. package/Server/Services/TeamComplianceService.ts +429 -252
  83. package/Server/Services/UserCallService.ts +26 -1
  84. package/Server/Services/UserEmailService.ts +26 -1
  85. package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
  86. package/Server/Services/UserNotificationRuleService.ts +3812 -333
  87. package/Server/Services/UserOnCallLogService.ts +561 -48
  88. package/Server/Services/UserPushService.ts +29 -0
  89. package/Server/Services/UserService.ts +11 -0
  90. package/Server/Services/UserSmsService.ts +26 -1
  91. package/Server/Services/UserTelegramService.ts +24 -1
  92. package/Server/Services/UserWebhookService.ts +28 -1
  93. package/Server/Services/UserWhatsAppService.ts +24 -1
  94. package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
  95. package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
  96. package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
  97. package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
  98. package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
  99. package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
  100. package/Server/Types/Workflow/Components/API/Get.ts +1 -1
  101. package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
  102. package/Server/Types/Workflow/Components/API/Post.ts +1 -1
  103. package/Server/Types/Workflow/Components/API/Put.ts +1 -1
  104. package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
  105. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
  106. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
  107. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
  108. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
  109. package/Server/Types/Workflow/Components/Email.ts +25 -7
  110. package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
  111. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
  112. package/Server/Types/Workflow/TriggerCode.ts +12 -0
  113. package/Server/Types/Workflow/Workflow.ts +5 -0
  114. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
  115. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
  116. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
  117. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
  118. package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
  119. package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
  120. package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
  121. package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
  122. package/Server/Utils/AI/Toolbox/Index.ts +48 -0
  123. package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
  124. package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
  125. package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
  126. package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
  127. package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
  128. package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
  129. package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
  130. package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
  131. package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
  132. package/Server/Utils/ClientIp.ts +221 -0
  133. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
  134. package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
  135. package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
  136. package/Server/Utils/Express.ts +12 -17
  137. package/Server/Utils/LLM/LLMService.ts +85 -8
  138. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
  139. package/Server/Utils/SSRFProtection.ts +98 -23
  140. package/Server/Utils/StartServer.ts +12 -3
  141. package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
  142. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
  143. package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
  144. package/Server/Utils/VM/VMAPI.ts +56 -8
  145. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
  146. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
  147. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
  148. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
  149. package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
  150. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
  151. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
  152. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
  153. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
  154. package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
  155. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
  156. package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
  157. package/Tests/Models/InventoryItemModel.test.ts +174 -0
  158. package/Tests/Models/InventoryItemNaming.test.ts +302 -0
  159. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
  160. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
  161. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
  162. package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
  163. package/Tests/Server/API/Helpers.ts +24 -15
  164. package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
  165. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
  166. package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
  167. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
  168. package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
  169. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
  170. package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
  171. package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
  172. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
  173. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
  174. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
  175. package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
  176. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
  177. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +248 -0
  178. package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
  179. package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
  180. package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
  181. package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
  182. package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
  183. package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
  184. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
  185. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
  186. package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
  187. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
  188. package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
  189. package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
  190. package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
  191. package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
  192. package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
  193. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
  194. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
  195. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
  196. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
  197. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
  198. package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
  199. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
  200. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
  201. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
  202. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
  203. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
  204. package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
  205. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
  206. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
  207. package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
  208. package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
  209. package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
  210. package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
  211. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
  212. package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
  213. package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
  214. package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
  215. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
  216. package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
  217. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
  218. package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
  219. package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
  220. package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
  221. package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
  222. package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
  223. package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
  224. package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
  225. package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
  226. package/Tests/Server/Utils/ClientIp.test.ts +438 -0
  227. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
  228. package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
  229. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
  230. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
  231. package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
  232. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
  233. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
  234. package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
  235. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
  236. package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
  237. package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
  238. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
  239. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
  240. package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
  241. package/Tests/Types/IP/IP.test.ts +263 -0
  242. package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
  243. package/Tests/Types/IP/IPv6.test.ts +12 -1
  244. package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
  245. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
  246. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
  247. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
  248. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
  249. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
  250. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
  251. package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
  252. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
  253. package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
  254. package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
  255. package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
  256. package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
  257. package/Tests/Types/Workflow/TemplateSyntax.test.ts +491 -0
  258. package/Tests/Types/Workflow/Templates.test.ts +1218 -0
  259. package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
  260. package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
  261. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
  262. package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
  263. package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
  264. package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
  265. package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
  266. package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
  267. package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
  268. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
  269. package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
  270. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
  271. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
  272. package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
  273. package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
  274. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
  275. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
  276. package/Tests/UI/Components/Workflow/GraphLint.test.ts +893 -0
  277. package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
  278. package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +522 -0
  279. package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +193 -0
  280. package/Tests/UI/Components/Workflow/ModelSchema.test.ts +388 -0
  281. package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +210 -0
  282. package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
  283. package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
  284. package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
  285. package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
  286. package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
  287. package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
  288. package/Tests/UI/EsbuildConfig.test.ts +607 -0
  289. package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
  290. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  291. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
  292. package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
  293. package/Tests/UI/Utils/Platform.test.ts +147 -0
  294. package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
  295. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
  296. package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
  297. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +727 -0
  298. package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
  299. package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
  300. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
  301. package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
  302. package/Tests/__mocks__/bullmq.js +55 -0
  303. package/Types/AI/AIChatMessageStatus.ts +8 -1
  304. package/Types/AI/AIChatTypes.ts +12 -0
  305. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
  306. package/Types/Exception/ExceptionCode.ts +2 -0
  307. package/Types/Exception/ServiceUnavailableException.ts +8 -0
  308. package/Types/Exception/TooManyRequestsException.ts +8 -0
  309. package/Types/IP/IP.ts +93 -47
  310. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +80 -3
  311. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
  312. package/Types/OnCallDutyPolicy/Layer.ts +203 -149
  313. package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
  314. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
  315. package/Types/Permission.ts +193 -0
  316. package/Types/SerializableObjectDictionary.ts +133 -39
  317. package/Types/Telemetry/EntityRelationshipType.ts +1 -1
  318. package/Types/Telemetry/EntitySource.ts +40 -0
  319. package/Types/Telemetry/EntityType.ts +28 -1
  320. package/Types/Telemetry/EntityTypeGroups.ts +65 -0
  321. package/Types/Workflow/Component.ts +29 -0
  322. package/Types/Workflow/Components/API.ts +35 -0
  323. package/Types/Workflow/Components/BaseModel.ts +77 -27
  324. package/Types/Workflow/Components/Discord.ts +1 -0
  325. package/Types/Workflow/Components/Email.ts +12 -3
  326. package/Types/Workflow/Components/JavaScript.ts +7 -0
  327. package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
  328. package/Types/Workflow/Components/Slack.ts +1 -0
  329. package/Types/Workflow/Components/Telegram.ts +1 -0
  330. package/Types/Workflow/StepTrace.ts +181 -0
  331. package/Types/Workflow/TemplateSyntax.ts +543 -0
  332. package/Types/Workflow/Templates.ts +2368 -0
  333. package/UI/Components/Calendar/Calendar.css +43 -0
  334. package/UI/Components/Calendar/Calendar.tsx +8 -0
  335. package/UI/Components/Card/Card.tsx +2 -2
  336. package/UI/Components/Checkbox/Checkbox.tsx +16 -0
  337. package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
  338. package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
  339. package/UI/Components/Dictionary/Dictionary.tsx +71 -15
  340. package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
  341. package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
  342. package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
  343. package/UI/Components/Forms/Types/Field.ts +7 -0
  344. package/UI/Components/Forms/Validation.ts +63 -1
  345. package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
  346. package/UI/Components/Input/Input.tsx +32 -3
  347. package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
  348. package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
  349. package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
  350. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
  351. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
  352. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
  353. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
  354. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
  355. package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
  356. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
  357. package/UI/Components/LogsViewer/types.ts +23 -0
  358. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
  359. package/UI/Components/Modal/Modal.tsx +37 -4
  360. package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
  361. package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
  362. package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
  363. package/UI/Components/Table/Table.tsx +49 -16
  364. package/UI/Components/Table/TableBody.tsx +53 -28
  365. package/UI/Components/Table/TableHeader.tsx +13 -0
  366. package/UI/Components/Table/TableRow.tsx +58 -26
  367. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
  368. package/UI/Components/Workflow/ArgumentsForm.tsx +341 -8
  369. package/UI/Components/Workflow/Component.tsx +28 -26
  370. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
  371. package/UI/Components/Workflow/ComponentSettingsModal.tsx +79 -9
  372. package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
  373. package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
  374. package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
  375. package/UI/Components/Workflow/GraphLint.ts +628 -0
  376. package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
  377. package/UI/Components/Workflow/ModelColumnEditor.tsx +528 -0
  378. package/UI/Components/Workflow/ModelSchema.ts +278 -0
  379. package/UI/Components/Workflow/RunForm.tsx +41 -7
  380. package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
  381. package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
  382. package/UI/Components/Workflow/UseRunWatch.ts +212 -0
  383. package/UI/Components/Workflow/Utils.ts +93 -1
  384. package/UI/Components/Workflow/VariableModal.tsx +6 -2
  385. package/UI/Components/Workflow/Workflow.tsx +126 -7
  386. package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
  387. package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
  388. package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
  389. package/UI/Types/LayeredDismissal.ts +31 -0
  390. package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
  391. package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
  392. package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
  393. package/UI/Utils/Platform.ts +149 -0
  394. package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
  395. package/UI/esbuild-config.js +22 -1
  396. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
  397. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
  398. package/Utils/Monitor/NetworkTopologyUtil.ts +917 -155
  399. package/Utils/Telemetry/CrossSignalScope.ts +502 -0
  400. package/Utils/Telemetry/EntityKey.ts +71 -5
  401. package/Utils/Telemetry/EntityRelationship.ts +1 -1
  402. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
  403. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
  404. package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
  405. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  406. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
  407. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
  408. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
  409. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  410. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
  411. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  412. package/build/dist/Models/DatabaseModels/Index.js +12 -4
  413. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  414. package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +212 -29
  415. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
  416. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
  417. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
  418. package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +52 -25
  419. package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
  420. package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
  421. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  422. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
  423. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
  424. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
  425. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
  426. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
  427. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
  428. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
  429. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
  430. package/build/dist/Models/DatabaseModels/Project.js +80 -0
  431. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  432. package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
  433. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  434. package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
  435. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  436. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
  437. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  438. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
  439. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  440. package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
  441. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  442. package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
  443. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  444. package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
  445. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
  446. package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
  447. package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
  448. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
  449. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  450. package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
  451. package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
  452. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
  453. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  454. package/build/dist/Server/API/AIChatAPI.js +237 -1
  455. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  456. package/build/dist/Server/API/DashboardAPI.js +165 -13
  457. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  458. package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
  459. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
  460. package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
  461. package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
  462. package/build/dist/Server/API/TelemetryAPI.js +129 -18
  463. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  464. package/build/dist/Server/API/UserAPI.js +16 -1
  465. package/build/dist/Server/API/UserAPI.js.map +1 -1
  466. package/build/dist/Server/EnvironmentConfig.js +45 -0
  467. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  468. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
  469. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  470. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
  471. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
  472. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
  473. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
  474. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
  475. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
  476. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
  477. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
  478. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
  479. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
  480. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
  481. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
  482. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
  483. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
  484. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
  485. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
  486. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
  487. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
  488. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
  489. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
  490. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
  491. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
  492. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
  493. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
  494. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
  495. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +24 -0
  496. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  497. package/build/dist/Server/Infrastructure/Queue.js +72 -13
  498. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  499. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
  500. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  501. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
  502. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
  503. package/build/dist/Server/Services/AIService.js +1 -0
  504. package/build/dist/Server/Services/AIService.js.map +1 -1
  505. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
  506. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
  507. package/build/dist/Server/Services/AlertSeverityService.js +54 -0
  508. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  509. package/build/dist/Server/Services/DashboardService.js +10 -9
  510. package/build/dist/Server/Services/DashboardService.js.map +1 -1
  511. package/build/dist/Server/Services/DatabaseService.js +24 -2
  512. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  513. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
  514. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  515. package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
  516. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  517. package/build/dist/Server/Services/Index.js +12 -4
  518. package/build/dist/Server/Services/Index.js.map +1 -1
  519. package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
  520. package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
  521. package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +9 -6
  522. package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
  523. package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +158 -23
  524. package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
  525. package/build/dist/Server/Services/LogAggregationService.js +27 -8
  526. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  527. package/build/dist/Server/Services/MetricAggregationService.js +80 -0
  528. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  529. package/build/dist/Server/Services/MetricService.js +6 -6
  530. package/build/dist/Server/Services/MetricService.js.map +1 -1
  531. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
  532. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
  533. package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
  534. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
  535. package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
  536. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  537. package/build/dist/Server/Services/NetworkSiteService.js +53 -13
  538. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  539. package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
  540. package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
  541. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
  542. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  543. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
  544. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  545. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
  546. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  547. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
  548. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  549. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
  550. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
  551. package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
  552. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
  553. package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
  554. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
  555. package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
  556. package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
  557. package/build/dist/Server/Services/StatusPageService.js +11 -10
  558. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  559. package/build/dist/Server/Services/TeamComplianceService.js +312 -160
  560. package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
  561. package/build/dist/Server/Services/UserCallService.js +24 -1
  562. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  563. package/build/dist/Server/Services/UserEmailService.js +24 -1
  564. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  565. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
  566. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
  567. package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
  568. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  569. package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
  570. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  571. package/build/dist/Server/Services/UserPushService.js +26 -0
  572. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  573. package/build/dist/Server/Services/UserService.js +10 -0
  574. package/build/dist/Server/Services/UserService.js.map +1 -1
  575. package/build/dist/Server/Services/UserSmsService.js +24 -1
  576. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  577. package/build/dist/Server/Services/UserTelegramService.js +22 -1
  578. package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
  579. package/build/dist/Server/Services/UserWebhookService.js +25 -1
  580. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  581. package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
  582. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  583. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
  584. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  585. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
  586. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  587. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
  588. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
  589. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
  590. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
  591. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
  592. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  593. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
  594. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  595. package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
  596. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  597. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
  598. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  599. package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
  600. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  601. package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
  602. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  603. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
  604. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  605. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
  606. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  607. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
  608. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  609. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
  610. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
  611. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
  612. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  613. package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
  614. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  615. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
  616. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  617. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
  618. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  619. package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
  620. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
  621. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  622. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
  623. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  624. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
  625. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  626. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
  627. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  628. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
  629. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  630. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
  631. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
  632. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
  633. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  634. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
  635. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
  636. package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
  637. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  638. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
  639. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
  640. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
  641. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
  642. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
  643. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
  644. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
  645. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
  646. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
  647. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
  648. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
  649. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
  650. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
  651. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
  652. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
  653. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
  654. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
  655. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
  656. package/build/dist/Server/Utils/ClientIp.js +137 -0
  657. package/build/dist/Server/Utils/ClientIp.js.map +1 -0
  658. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
  659. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  660. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
  661. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
  662. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
  663. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
  664. package/build/dist/Server/Utils/Express.js +12 -12
  665. package/build/dist/Server/Utils/Express.js.map +1 -1
  666. package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
  667. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  668. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
  669. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  670. package/build/dist/Server/Utils/SSRFProtection.js +82 -21
  671. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  672. package/build/dist/Server/Utils/StartServer.js +12 -4
  673. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  674. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
  675. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  676. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
  677. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
  678. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
  679. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  680. package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
  681. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  682. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
  683. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  684. package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
  685. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
  686. package/build/dist/Types/AI/AIChatTypes.js +12 -0
  687. package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
  688. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
  689. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
  690. package/build/dist/Types/Exception/ExceptionCode.js +2 -0
  691. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  692. package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
  693. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
  694. package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
  695. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
  696. package/build/dist/Types/IP/IP.js +87 -43
  697. package/build/dist/Types/IP/IP.js.map +1 -1
  698. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
  699. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
  700. package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
  701. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  702. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
  703. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
  704. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
  705. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  706. package/build/dist/Types/Permission.js +174 -0
  707. package/build/dist/Types/Permission.js.map +1 -1
  708. package/build/dist/Types/SerializableObjectDictionary.js +133 -39
  709. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  710. package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
  711. package/build/dist/Types/Telemetry/EntitySource.js +39 -0
  712. package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
  713. package/build/dist/Types/Telemetry/EntityType.js +28 -1
  714. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  715. package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
  716. package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
  717. package/build/dist/Types/Workflow/Component.js +21 -0
  718. package/build/dist/Types/Workflow/Component.js.map +1 -1
  719. package/build/dist/Types/Workflow/Components/API.js +35 -0
  720. package/build/dist/Types/Workflow/Components/API.js.map +1 -1
  721. package/build/dist/Types/Workflow/Components/BaseModel.js +68 -26
  722. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  723. package/build/dist/Types/Workflow/Components/Discord.js +1 -0
  724. package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
  725. package/build/dist/Types/Workflow/Components/Email.js +12 -3
  726. package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
  727. package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
  728. package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
  729. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
  730. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
  731. package/build/dist/Types/Workflow/Components/Slack.js +1 -0
  732. package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
  733. package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
  734. package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
  735. package/build/dist/Types/Workflow/StepTrace.js +104 -0
  736. package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
  737. package/build/dist/Types/Workflow/TemplateSyntax.js +338 -0
  738. package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
  739. package/build/dist/Types/Workflow/Templates.js +2111 -0
  740. package/build/dist/Types/Workflow/Templates.js.map +1 -0
  741. package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
  742. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  743. package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
  744. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  745. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
  746. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
  747. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
  748. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
  749. package/build/dist/UI/Components/Dictionary/Dictionary.js +47 -17
  750. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  751. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  752. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
  753. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
  754. package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
  755. package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
  756. package/build/dist/UI/Components/Forms/Validation.js +44 -0
  757. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  758. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
  759. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  760. package/build/dist/UI/Components/Input/Input.js +13 -4
  761. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  762. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
  763. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
  764. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
  765. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
  766. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
  767. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  768. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
  769. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  770. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
  771. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  772. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
  773. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  774. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
  775. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  776. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
  777. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  778. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
  779. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  780. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
  781. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  782. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  783. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
  784. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  785. package/build/dist/UI/Components/Modal/Modal.js +31 -5
  786. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  787. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
  788. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  789. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
  790. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
  791. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
  792. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
  793. package/build/dist/UI/Components/Table/Table.js +27 -15
  794. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  795. package/build/dist/UI/Components/Table/TableBody.js +24 -18
  796. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  797. package/build/dist/UI/Components/Table/TableHeader.js +9 -1
  798. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  799. package/build/dist/UI/Components/Table/TableRow.js +29 -21
  800. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  801. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
  802. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  803. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +261 -14
  804. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  805. package/build/dist/UI/Components/Workflow/Component.js +20 -19
  806. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  807. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
  808. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  809. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +35 -7
  810. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  811. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
  812. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
  813. package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
  814. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  815. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
  816. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  817. package/build/dist/UI/Components/Workflow/GraphLint.js +425 -0
  818. package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
  819. package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
  820. package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
  821. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +320 -0
  822. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
  823. package/build/dist/UI/Components/Workflow/ModelSchema.js +156 -0
  824. package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
  825. package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
  826. package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
  827. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
  828. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
  829. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
  830. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
  831. package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
  832. package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
  833. package/build/dist/UI/Components/Workflow/Utils.js +73 -1
  834. package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
  835. package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
  836. package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
  837. package/build/dist/UI/Components/Workflow/Workflow.js +92 -7
  838. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  839. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
  840. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
  841. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
  842. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
  843. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
  844. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
  845. package/build/dist/UI/Types/LayeredDismissal.js +21 -0
  846. package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
  847. package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
  848. package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
  849. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
  850. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
  851. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
  852. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  853. package/build/dist/UI/Utils/Platform.js +118 -0
  854. package/build/dist/UI/Utils/Platform.js.map +1 -0
  855. package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
  856. package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
  857. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
  858. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
  859. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
  860. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
  861. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +661 -126
  862. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  863. package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
  864. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
  865. package/build/dist/Utils/Telemetry/EntityKey.js +57 -5
  866. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  867. package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
  868. package/jest.config.json +1 -0
  869. package/package.json +1 -1
  870. package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
  871. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
  872. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
  873. package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
@@ -0,0 +1,1961 @@
1
+ import AlertSeverityService from "./AlertSeverityService";
2
+ import IncidentSeverityService from "./IncidentSeverityService";
3
+ import OnCallDutyPolicyEscalationRuleScheduleService from "./OnCallDutyPolicyEscalationRuleScheduleService";
4
+ import OnCallDutyPolicyEscalationRuleTeamService from "./OnCallDutyPolicyEscalationRuleTeamService";
5
+ import OnCallDutyPolicyEscalationRuleUserService from "./OnCallDutyPolicyEscalationRuleUserService";
6
+ import OnCallDutyPolicyScheduleLayerUserService from "./OnCallDutyPolicyScheduleLayerUserService";
7
+ import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
8
+ import OnCallDutyPolicyUserOverrideService from "./OnCallDutyPolicyUserOverrideService";
9
+ import ProjectService from "./ProjectService";
10
+ import TeamMemberService from "./TeamMemberService";
11
+ import TeamService from "./TeamService";
12
+ import UserCallService from "./UserCallService";
13
+ import UserEmailService from "./UserEmailService";
14
+ import UserNotificationRuleService from "./UserNotificationRuleService";
15
+ import UserPushService from "./UserPushService";
16
+ import UserService from "./UserService";
17
+ import UserSmsService from "./UserSmsService";
18
+ import UserTelegramService from "./UserTelegramService";
19
+ import UserWebhookService from "./UserWebhookService";
20
+ import UserWhatsAppService from "./UserWhatsAppService";
21
+ import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
22
+ import QueryHelper from "../Types/Database/QueryHelper";
23
+ import logger from "../Utils/Logger";
24
+ import Includes from "../../Types/BaseDatabase/Includes";
25
+ import SortOrder from "../../Types/BaseDatabase/SortOrder";
26
+ import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
27
+ import OneUptimeDate from "../../Types/Date";
28
+ import BadDataException from "../../Types/Exception/BadDataException";
29
+ import NotificationRuleType from "../../Types/NotificationRule/NotificationRuleType";
30
+ import ObjectID from "../../Types/ObjectID";
31
+ /*
32
+ * "Can this responder actually be paged?" — computed, not configured.
33
+ *
34
+ * This is the single place that answers that question, and every readiness surface in
35
+ * the product renders what it returns. It exists because the question was previously
36
+ * answered by TeamComplianceService, which answered it wrongly in seven separate ways —
37
+ * each of which is a real defect with a real missed page behind it, and each of which is
38
+ * fixed here deliberately rather than incidentally:
39
+ *
40
+ * 1. It was opt-in per team and OFF by default, so the common case was no answer at
41
+ * all. Readiness here is always computed; there is nothing to switch on.
42
+ * 2. It was TEAM-SCOPED. A user attached directly to an escalation rule, reached
43
+ * through a schedule layer, or substituted in by a user override was never checked —
44
+ * which is to say the three ways a responder most often gets paged were invisible.
45
+ * resolveResponders below is the union of all four, and the union is the point.
46
+ * 3. It capped members and users at a bare `limit: 100`, silently truncating a large
47
+ * project into a comfortable lie. NOTHING here is capped: every read goes through
48
+ * readEveryPage, which pages until the table is exhausted. See the essay on that
49
+ * method for why raising the cap instead would have been the same bug with a bigger
50
+ * number — a responder who falls off the end of a page is reported in no count, no
51
+ * list and no summary, which is indistinguishable from a responder who is fine.
52
+ * 4. It ignored `ruleType`, so a "when I go off call" rule counted as incident
53
+ * coverage. Coverage below is keyed on (userId, ruleType, severityId); a rule for
54
+ * the wrong ruleType covers nothing.
55
+ * 5. It counted only call/SMS/email/push, so a responder whose only method was
56
+ * Telegram, WhatsApp or Webhook was reported non-compliant while being perfectly
57
+ * reachable. All seven channels count here.
58
+ * 6. It ran one findBy per severity per user. Every read below is batched with
59
+ * Includes(userIds); the query count is constant in the number of responders and
60
+ * in the number of severities. It grows only with the number of PAGES of rows that
61
+ * come back, which is the unavoidable price of not truncating.
62
+ *
63
+ * The seventh defect was that it was read-only prose. This service does not fix that on
64
+ * its own, but everything it returns is shaped to be acted on: `reasons` are sentences
65
+ * naming a specific missing thing, and `coverage` is a grid an admin can fix cell by
66
+ * cell.
67
+ */
68
+ /**
69
+ * Ready — every coverage cell either has a rule or is explicitly muted.
70
+ * PartiallyReady — reachable, but at least one cell falls back.
71
+ * NotReachable — zero USABLE notification methods; nothing will reach this person.
72
+ */
73
+ export var ReadinessStatus;
74
+ (function (ReadinessStatus) {
75
+ ReadinessStatus["Ready"] = "Ready";
76
+ ReadinessStatus["PartiallyReady"] = "PartiallyReady";
77
+ ReadinessStatus["NotReachable"] = "NotReachable";
78
+ })(ReadinessStatus || (ReadinessStatus = {}));
79
+ /**
80
+ * WHY a user is on this policy. A user reached two ways carries both sources, because
81
+ * removing them from one attachment does not stop them being paged through the other —
82
+ * an admin looking at an unreachable responder needs to know every door they came in by.
83
+ */
84
+ export var ResponderSource;
85
+ (function (ResponderSource) {
86
+ ResponderSource["Direct"] = "Direct";
87
+ ResponderSource["Team"] = "Team";
88
+ ResponderSource["Schedule"] = "Schedule";
89
+ ResponderSource["Override"] = "Override";
90
+ })(ResponderSource || (ResponderSource = {}));
91
+ /**
92
+ * The seven channels a page can be delivered on. These strings are the same literals the
93
+ * fallback uses for `channelsUsed` (UserNotificationRuleService.chooseFallbackChannels),
94
+ * deliberately: an operator reading "notified via fallback (Push, Email)" in an execution
95
+ * log and "Push, Email" in the readiness table must not have to translate between two
96
+ * vocabularies for the same thing.
97
+ */
98
+ export var ReadinessMethodType;
99
+ (function (ReadinessMethodType) {
100
+ ReadinessMethodType["Push"] = "Push";
101
+ ReadinessMethodType["Email"] = "Email";
102
+ ReadinessMethodType["SMS"] = "SMS";
103
+ ReadinessMethodType["Call"] = "Call";
104
+ ReadinessMethodType["WhatsApp"] = "WhatsApp";
105
+ ReadinessMethodType["Telegram"] = "Telegram";
106
+ ReadinessMethodType["Webhook"] = "Webhook";
107
+ })(ReadinessMethodType || (ReadinessMethodType = {}));
108
+ /*
109
+ * The bullet used for every redaction. A single shared constant so a test can assert on
110
+ * the mask without hard-coding a character that is easy to typo into a look-alike (there
111
+ * are several bullet-ish code points and they are indistinguishable on screen).
112
+ */
113
+ export const IDENTIFIER_MASK = "•••";
114
+ /**
115
+ * What SHAPE an identifier has, which is all masking needs to know. Kept separate from
116
+ * ReadinessMethodType because five of the seven channels mask identically — a phone is a
117
+ * phone whether it rings, texts or WhatsApps — and collapsing them here means a new
118
+ * channel cannot arrive with no masking rule at all.
119
+ */
120
+ export var MaskedIdentifierKind;
121
+ (function (MaskedIdentifierKind) {
122
+ MaskedIdentifierKind["Email"] = "Email";
123
+ MaskedIdentifierKind["Phone"] = "Phone";
124
+ MaskedIdentifierKind["Handle"] = "Handle";
125
+ })(MaskedIdentifierKind || (MaskedIdentifierKind = {}));
126
+ /*
127
+ * How many trailing digits of a phone number are revealed, and the shortest value that
128
+ * may have them revealed. The two are deliberately different numbers: revealing the last
129
+ * four digits of a four-digit value reveals the value, and revealing the last four of a
130
+ * FIVE-digit value would be a mask in name only. A number has to be longer than what the
131
+ * mask keeps for the mask to be hiding anything at all, so the floor is "more digits than
132
+ * we reveal".
133
+ */
134
+ const REVEALED_PHONE_DIGITS = 4;
135
+ /**
136
+ * Redact an identifier down to just enough for its owner to recognise it.
137
+ *
138
+ * Exported as a free function, and used by this service for every single identifier it
139
+ * emits, for two reasons. The first is that masking is the one rule in this file that
140
+ * must never be got wrong even slightly, and a pure function of (string, kind) is
141
+ * directly unit-testable in a way that "call the service and inspect the summary" is
142
+ * not. The second is structural: because the ONLY way an identifier reaches a
143
+ * ReadinessMethod is through this function, the API layer has no unmasked value
144
+ * available to leak by accident. Bypassing the masking would require deliberately
145
+ * writing a second query, not merely forgetting a call.
146
+ *
147
+ * Email jane@example.com -> j•••@example.com
148
+ * Phone +14155554821 -> +1 ••• ••• 4821
149
+ * Handle @jamesbond -> @ja•••
150
+ *
151
+ * The phone rule keeps everything before the last ten digits as the country code, which
152
+ * is a heuristic rather than a parse — national numbers are ~10 digits nearly
153
+ * everywhere, so "+1" and "+44" both come out right, and a country whose numbering plan
154
+ * disagrees loses a cosmetic digit and leaks nothing. Correctness here is measured in
155
+ * what is HIDDEN, and the last four digits plus the country code is the same disclosure
156
+ * every bank confirmation screen makes.
157
+ */
158
+ export const maskIdentifier = (value, kind) => {
159
+ const trimmed = (value || "").trim();
160
+ if (!trimmed) {
161
+ /*
162
+ * Nothing to mask and nothing to reveal. Returning the bare mask rather than an
163
+ * empty string keeps the UI cell from collapsing into blank space that reads as
164
+ * "no method" when a method demonstrably exists.
165
+ */
166
+ return IDENTIFIER_MASK;
167
+ }
168
+ if (kind === MaskedIdentifierKind.Email) {
169
+ const atIndex = trimmed.lastIndexOf("@");
170
+ /*
171
+ * An address with no "@" is not an address. Rather than guess, fall through to the
172
+ * handle rule, which is strictly more conservative than the email rule (it reveals
173
+ * two characters and no domain).
174
+ */
175
+ if (atIndex <= 0) {
176
+ return maskIdentifier(trimmed, MaskedIdentifierKind.Handle);
177
+ }
178
+ const localPart = trimmed.substring(0, atIndex);
179
+ const domain = trimmed.substring(atIndex + 1);
180
+ return `${localPart.substring(0, 1)}${IDENTIFIER_MASK}@${domain}`;
181
+ }
182
+ if (kind === MaskedIdentifierKind.Phone) {
183
+ const digits = trimmed.replace(/\D/g, "");
184
+ /*
185
+ * Note the <=, not <. At exactly four digits the "last four" IS the whole value, so
186
+ * the old strict comparison handed the number back in full while looking, on the
187
+ * screen and in a code review, exactly like a mask. Anything at or below the number
188
+ * of digits we reveal is therefore masked entirely: a value we cannot hide half of
189
+ * is a value we do not show.
190
+ */
191
+ if (digits.length <= REVEALED_PHONE_DIGITS) {
192
+ return IDENTIFIER_MASK;
193
+ }
194
+ const lastFour = digits.substring(digits.length - REVEALED_PHONE_DIGITS);
195
+ const countryCode = digits.length > 10 ? digits.substring(0, digits.length - 10) : "";
196
+ const prefix = countryCode ? `+${countryCode} ` : "";
197
+ return `${prefix}${IDENTIFIER_MASK} ${IDENTIFIER_MASK} ${lastFour}`;
198
+ }
199
+ /*
200
+ * Handles cover Telegram handles, push device names and webhook names. Two characters
201
+ * is enough for the owner to say "yes, that is my phone" and not enough for anyone
202
+ * else to say whose phone it is.
203
+ */
204
+ const hasLeadingAt = trimmed.startsWith("@");
205
+ const body = hasLeadingAt ? trimmed.substring(1) : trimmed;
206
+ return `${hasLeadingAt ? "@" : ""}${body.substring(0, 2)}${IDENTIFIER_MASK}`;
207
+ };
208
+ /*
209
+ * Which severity list scopes which rule type, and the noun to use when telling an admin
210
+ * about it. Incident and incident-episode pages are severity-scoped by IncidentSeverity;
211
+ * alert and alert-episode by AlertSeverity. Getting this pairing wrong is not a cosmetic
212
+ * error — an alert rule matched against an incident severity id matches nothing at all,
213
+ * which is exactly the shape of Gap G, where episode default rules were written with a
214
+ * NULL severity and were therefore invisible and unreachable at the same time.
215
+ */
216
+ var SeverityKind;
217
+ (function (SeverityKind) {
218
+ SeverityKind["Incident"] = "Incident";
219
+ SeverityKind["Alert"] = "Alert";
220
+ })(SeverityKind || (SeverityKind = {}));
221
+ /*
222
+ * The four rule types a PAGE can arrive under. The two handoff types
223
+ * (WHEN_USER_GOES_ON_CALL / WHEN_USER_GOES_OFF_CALL) are deliberately absent: they are
224
+ * courtesy notifications about a shift change, nobody is waiting on them, and counting a
225
+ * missing one as "not ready" would flood the amber state with users whose paging is
226
+ * perfectly healthy. Readiness is about pages.
227
+ */
228
+ const RULE_TYPE_SCOPES = [
229
+ {
230
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
231
+ severityKind: SeverityKind.Incident,
232
+ subjectNoun: "incidents",
233
+ },
234
+ {
235
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT_EPISODE,
236
+ severityKind: SeverityKind.Incident,
237
+ subjectNoun: "incident episodes",
238
+ },
239
+ {
240
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
241
+ severityKind: SeverityKind.Alert,
242
+ subjectNoun: "alerts",
243
+ },
244
+ {
245
+ ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT_EPISODE,
246
+ severityKind: SeverityKind.Alert,
247
+ subjectNoun: "alert episodes",
248
+ },
249
+ ];
250
+ /*
251
+ * Display order for `methods`, and simultaneously the order the fallback would try them
252
+ * in. Sharing one order means the first entry in the list an admin looks at is the
253
+ * channel a fallback page would actually arrive on.
254
+ */
255
+ const METHOD_DISPLAY_ORDER = [
256
+ ReadinessMethodType.Push,
257
+ ReadinessMethodType.Email,
258
+ ReadinessMethodType.SMS,
259
+ ReadinessMethodType.Call,
260
+ ReadinessMethodType.WhatsApp,
261
+ ReadinessMethodType.Telegram,
262
+ ReadinessMethodType.Webhook,
263
+ ];
264
+ const RESPONDER_SOURCE_ORDER = [
265
+ ResponderSource.Direct,
266
+ ResponderSource.Team,
267
+ ResponderSource.Schedule,
268
+ ResponderSource.Override,
269
+ ];
270
+ /*
271
+ * Most-broken-first. The readiness table is read by somebody looking for a problem, so
272
+ * the problems sort to the top and the healthy majority sorts out of the way.
273
+ */
274
+ const STATUS_SORT_RANK = {
275
+ [ReadinessStatus.NotReachable]: 0,
276
+ [ReadinessStatus.PartiallyReady]: 1,
277
+ [ReadinessStatus.Ready]: 2,
278
+ };
279
+ /*
280
+ * 60 seconds, the same window ProjectService.currentPlanCache uses and for the same
281
+ * reason: this is computed on page load and on every responder chip render, the inputs
282
+ * (escalation rules, notification rules, verified methods) change on a human timescale,
283
+ * and a minute of staleness on "this person has no SMS rule" costs nothing. There is no
284
+ * cross-process invalidation — each replica holds its own copy — so the TTL is the only
285
+ * guarantee, which is why it is short.
286
+ */
287
+ const READINESS_CACHE_TTL_IN_MS = 60 * 1000;
288
+ /*
289
+ * Rows per page. LIMIT_PER_PROJECT is the largest read the database layer will serve
290
+ * (DatabaseService clamps anything above it), so it is the biggest page that survives a
291
+ * round trip, and a bigger page means fewer round trips for the same total.
292
+ */
293
+ const READ_PAGE_SIZE = LIMIT_PER_PROJECT;
294
+ /*
295
+ * A ceiling on the number of pages ONE read may take, so a fetcher that keeps returning
296
+ * full pages — a paging bug, a query whose sort is not total, a table that is genuinely
297
+ * growing faster than we can read it — cannot spin this service forever holding a
298
+ * connection. Five million rows is far past any real project; a read that hits it is a
299
+ * bug report, not a big customer, which is why hitting it is logged as an error AND
300
+ * reported as isTruncated rather than quietly stopping.
301
+ */
302
+ const MAX_PAGES_PER_READ = 500;
303
+ /*
304
+ * Pipe-separated because none of the three components can contain a pipe: user and
305
+ * severity ids are uuids, and NotificationRuleType's values are fixed English sentences.
306
+ */
307
+ const buildCoverageKey = (userId, ruleType, severityId) => {
308
+ return `${userId}|${ruleType}|${severityId}`;
309
+ };
310
+ class OnCallReadinessService {
311
+ /**
312
+ * Readiness for every responder a single policy can reach — the union of its direct
313
+ * users, its teams' members, its schedules' layer users, and anyone an override routes
314
+ * pages to.
315
+ */
316
+ static async getReadinessForPolicy(policyId, projectId) {
317
+ var _a;
318
+ const cacheKey = `${projectId.toString()}:policy:${policyId.toString()}`;
319
+ const cached = this.summaryCache.get(cacheKey);
320
+ if (cached) {
321
+ return cached;
322
+ }
323
+ /*
324
+ * Look the policy up rather than letting a bad id fall through. Every query below is
325
+ * scoped by projectId, so a policy id from another project would return zero
326
+ * responders — and a summary that says "0 responders, nothing wrong" is the most
327
+ * dangerous possible answer to "is this policy safe to rely on?".
328
+ */
329
+ const policy = await OnCallDutyPolicyService.findOneById({
330
+ id: policyId,
331
+ select: {
332
+ _id: true,
333
+ projectId: true,
334
+ },
335
+ props: {
336
+ isRoot: true,
337
+ },
338
+ });
339
+ if (!policy || ((_a = policy.projectId) === null || _a === void 0 ? void 0 : _a.toString()) !== projectId.toString()) {
340
+ throw new BadDataException("On-call duty policy not found");
341
+ }
342
+ const summary = await this.computeSummary(projectId, policyId);
343
+ this.summaryCache.set(cacheKey, summary, READINESS_CACHE_TTL_IN_MS);
344
+ return summary;
345
+ }
346
+ /**
347
+ * Readiness for every responder reachable through ANY policy in the project. Same
348
+ * resolution as the per-policy call with the policy filter dropped, so a user who is
349
+ * ready on one policy and unreachable on another appears exactly once, with the union
350
+ * of their sources.
351
+ */
352
+ static async getReadinessForProject(projectId) {
353
+ const cacheKey = `${projectId.toString()}:project`;
354
+ const cached = this.summaryCache.get(cacheKey);
355
+ if (cached) {
356
+ return cached;
357
+ }
358
+ const summary = await this.computeSummary(projectId, undefined);
359
+ this.summaryCache.set(cacheKey, summary, READINESS_CACHE_TTL_IN_MS);
360
+ return summary;
361
+ }
362
+ /**
363
+ * Readiness for a SET of users, at the cost of one user.
364
+ *
365
+ * This is the entry point every list-shaped caller must use — a team roster, a
366
+ * responder table, a page of chips. It exists because the obvious alternative,
367
+ * `Promise.all(userIds.map(getReadinessForUser))`, is not a small inefficiency but a
368
+ * different order of cost: each of those calls used to resolve the ENTIRE project's
369
+ * responder set (six heavy reads) purely to work out one user's `reachedVia`, so a
370
+ * forty-member team issued several hundred queries where a handful would do, and one
371
+ * rejected promise threw the whole page away.
372
+ *
373
+ * The query count here is constant in the number of users asked about: every read is
374
+ * `Includes(userIds)` over the whole set, and the membership resolution is targeted at
375
+ * exactly those users rather than at the project. One user and five hundred users cost
376
+ * the same round trips — the only thing that grows is the number of PAGES each read
377
+ * takes, which is a function of how many rows exist, not of how many users were asked
378
+ * about, and is the price of never truncating.
379
+ *
380
+ * Users that are not members of the project are OMITTED rather than thrown for: a
381
+ * roster read races with somebody being removed from a team, and one departed member
382
+ * must not blank out the readiness of the other thirty-nine. A caller that needs to
383
+ * know about the omission should compare the returned userIds with the ones it asked
384
+ * for.
385
+ */
386
+ static async getReadinessForUsers(userIds, projectId) {
387
+ if (userIds.length === 0) {
388
+ return [];
389
+ }
390
+ const batch = await this.computeReadinessForUsers(userIds, projectId);
391
+ return batch.readiness;
392
+ }
393
+ /**
394
+ * Readiness for one user, whether or not they are on a policy at all.
395
+ *
396
+ * The "whether or not" matters: this is what the add-responder modal calls BEFORE the
397
+ * user has been attached to anything, which is the only moment at which the mistake is
398
+ * cheap to fix. Such a user has an empty `reachedVia` and a status computed exactly as
399
+ * it would be once they are attached.
400
+ *
401
+ * A thin wrapper over getReadinessForUsers, deliberately: one code path computes
402
+ * readiness for a set, and "one" is a set of size one. The only thing this adds is
403
+ * turning the two ways of getting nothing back — not a member of the project, member
404
+ * with no User row — into the two different exceptions callers have always seen.
405
+ */
406
+ static async getReadinessForUser(userId, projectId) {
407
+ const batch = await this.computeReadinessForUsers([userId], projectId);
408
+ /*
409
+ * User is a GLOBAL model — it is not scoped by project — so without this check any
410
+ * caller holding a project's credentials could ask for the readiness of an arbitrary
411
+ * user id and get their name and login email back. Team membership is what "in this
412
+ * project" means, so that is what is checked, and it is checked BEFORE any other
413
+ * read is issued (see computeReadinessForUsers), so a caller probing arbitrary user
414
+ * ids learns nothing at all.
415
+ */
416
+ if (!batch.memberUserIds.has(userId.toString())) {
417
+ throw new BadDataException("User is not a member of this project");
418
+ }
419
+ const readiness = batch.readiness[0];
420
+ if (!readiness) {
421
+ throw new BadDataException("User not found");
422
+ }
423
+ return readiness;
424
+ }
425
+ /**
426
+ * Throw away every cached answer in this process.
427
+ *
428
+ * Deliberately coarse — it does not take a projectId — because the cache is keyed
429
+ * three different ways (project, policy, user) and a write to one notification rule
430
+ * can invalidate all three at once: the rule's owner, every policy that reaches them,
431
+ * and the project roll-up. Working out the affected key set would be more code than
432
+ * the saving is worth when the entries expire in sixty seconds anyway, and a
433
+ * too-clever invalidation that misses a key is indistinguishable from the stale
434
+ * readiness this whole service exists to eliminate.
435
+ *
436
+ * This is PUBLIC and must actually be CALLED. Until it is, a "Recheck" button re-reads
437
+ * the same sixty-second-old answer and redraws it unchanged, which reads to an admin
438
+ * who has just fixed something as "my fix did not work". The exact call sites, all of
439
+ * which currently do not call it:
440
+ *
441
+ * - UserNotificationRuleService — on create and on delete of a rule (this is the
442
+ * write the Recheck button is nearly always chasing).
443
+ * - The seven notification-method services (UserEmailService, UserSmsService,
444
+ * UserCallService, UserPushService, UserWhatsAppService, UserTelegramService,
445
+ * UserWebhookService) — on create, on delete, and on VERIFICATION, which is the
446
+ * write that flips a responder from NotReachable to Ready.
447
+ * - OnCallDutyPolicyEscalationRuleUserService / ...TeamService / ...ScheduleService
448
+ * and OnCallDutyPolicyScheduleLayerUserService — on create and delete, because
449
+ * they change who the responder set even contains.
450
+ * - OnCallDutyPolicyUserOverrideService — on create, update and delete.
451
+ * - TeamMemberService — on create and delete, for the same reason.
452
+ * - ProjectService — on any update that touches disableOnCallNotificationFallback or
453
+ * the four enable*Notifications switches, since those change every user's status
454
+ * at once.
455
+ * - IncidentSeverityService / AlertSeverityService — on create and delete, which add
456
+ * and remove whole columns of the coverage grid.
457
+ * - OnCallReadinessAPI — on an explicit refresh request, so "Recheck" means recheck.
458
+ *
459
+ * Note that each replica holds its own copy and clears only its own; the TTL remains
460
+ * the only cross-process guarantee.
461
+ */
462
+ static clearCache() {
463
+ this.summaryCache.clear();
464
+ this.userCache.clear();
465
+ }
466
+ static async computeSummary(projectId, onCallDutyPolicyId) {
467
+ const completeness = this.newReadCompleteness();
468
+ /*
469
+ * Loaded here rather than inside the per-user pass because the summary itself has to
470
+ * report isFallbackEnabled even when the responder set is empty — a project with no
471
+ * responders and the fallback switched off is exactly the project somebody is about
472
+ * to attach a responder to.
473
+ */
474
+ const projectSettings = await this.loadProjectSettings(projectId);
475
+ const responders = await this.resolveResponders(projectId, onCallDutyPolicyId, completeness);
476
+ const users = await this.buildReadiness({
477
+ projectId: projectId,
478
+ responders: responders,
479
+ projectSettings: projectSettings,
480
+ completeness: completeness,
481
+ });
482
+ let readyCount = 0;
483
+ let partiallyReadyCount = 0;
484
+ let notReachableCount = 0;
485
+ for (const user of users) {
486
+ if (user.status === ReadinessStatus.Ready) {
487
+ readyCount++;
488
+ }
489
+ else if (user.status === ReadinessStatus.PartiallyReady) {
490
+ partiallyReadyCount++;
491
+ }
492
+ else {
493
+ notReachableCount++;
494
+ }
495
+ }
496
+ return {
497
+ projectId: projectId,
498
+ onCallDutyPolicyId: onCallDutyPolicyId,
499
+ readyCount: readyCount,
500
+ partiallyReadyCount: partiallyReadyCount,
501
+ notReachableCount: notReachableCount,
502
+ isFallbackEnabled: !projectSettings.isFallbackDisabled,
503
+ isTruncated: completeness.isTruncated,
504
+ users: users,
505
+ };
506
+ }
507
+ /**
508
+ * The batch computation both public user-shaped entry points share.
509
+ *
510
+ * Membership is resolved FIRST and everything else is filtered to the members it
511
+ * found, which does three jobs with one read: it is the cross-project guard, it is the
512
+ * source of each user's team list (which is how the Team source is resolved without
513
+ * expanding the whole project), and it is what makes "user removed mid-request" a
514
+ * quiet omission rather than an exception.
515
+ */
516
+ static async computeReadinessForUsers(userIds, projectId) {
517
+ const requestedUserIds = this.distinctIds(userIds);
518
+ const readiness = [];
519
+ const memberUserIds = new Set();
520
+ const uncachedUserIds = [];
521
+ for (const userId of requestedUserIds) {
522
+ const cached = this.userCache.get(this.userCacheKey(projectId, userId));
523
+ if (cached) {
524
+ /*
525
+ * Only members are ever written to this cache, so a hit answers the membership
526
+ * question too. That matters: re-issuing the membership read for a cached user
527
+ * would make a cache hit cost a round trip, which is most of what the cache is
528
+ * for.
529
+ */
530
+ memberUserIds.add(userId.toString());
531
+ readiness.push(cached);
532
+ continue;
533
+ }
534
+ uncachedUserIds.push(userId);
535
+ }
536
+ if (uncachedUserIds.length === 0) {
537
+ return {
538
+ readiness: this.sortReadiness(readiness),
539
+ memberUserIds: memberUserIds,
540
+ };
541
+ }
542
+ const completeness = this.newReadCompleteness();
543
+ const teamIdsByUserId = await this.loadProjectMembership(projectId, uncachedUserIds, completeness);
544
+ const memberIds = uncachedUserIds.filter((userId) => {
545
+ return teamIdsByUserId.has(userId.toString());
546
+ });
547
+ for (const memberId of memberIds) {
548
+ memberUserIds.add(memberId.toString());
549
+ }
550
+ if (memberIds.length === 0) {
551
+ return {
552
+ readiness: this.sortReadiness(readiness),
553
+ memberUserIds: memberUserIds,
554
+ };
555
+ }
556
+ const projectSettings = await this.loadProjectSettings(projectId);
557
+ const responders = await this.resolveRespondersForUsers({
558
+ projectId: projectId,
559
+ userIds: memberIds,
560
+ teamIdsByUserId: teamIdsByUserId,
561
+ completeness: completeness,
562
+ });
563
+ const computed = await this.buildReadiness({
564
+ projectId: projectId,
565
+ responders: responders,
566
+ projectSettings: projectSettings,
567
+ completeness: completeness,
568
+ });
569
+ for (const one of computed) {
570
+ /*
571
+ * A truncated read can only make a user look WORSE here — a rule that did not
572
+ * arrive reads as a missing rule, a method that did not arrive as a missing method
573
+ * — so the answer is safe to RETURN. It is not safe to cache for a minute, and the
574
+ * reason is the asymmetry with the summary: ReadinessSummary carries isTruncated,
575
+ * so a cached truncated summary is still telling the truth about itself, whereas
576
+ * UserReadiness carries no such field and a cached one would be an unlabelled false
577
+ * amber sitting on somebody's card for sixty seconds. Recomputing is cheap next to
578
+ * teaching admins that amber means nothing.
579
+ */
580
+ if (!completeness.isTruncated) {
581
+ this.userCache.set(this.userCacheKey(projectId, one.userId), one, READINESS_CACHE_TTL_IN_MS);
582
+ }
583
+ readiness.push(one);
584
+ }
585
+ return {
586
+ readiness: this.sortReadiness(readiness),
587
+ memberUserIds: memberUserIds,
588
+ };
589
+ }
590
+ static userCacheKey(projectId, userId) {
591
+ return `${projectId.toString()}:user:${userId.toString()}`;
592
+ }
593
+ static newReadCompleteness() {
594
+ return {
595
+ isTruncated: false,
596
+ };
597
+ }
598
+ /**
599
+ * Read a whole table, one page at a time, folding each page as it arrives.
600
+ *
601
+ * This is the single most important method in the file, because the alternative it
602
+ * replaces — one findBy capped at LIMIT_PER_PROJECT — is not a performance choice but
603
+ * a correctness one, and it fails in the worst available direction. UserNotificationRule
604
+ * rows grow as users x (2 x incidentSeverities + 2 x alertSeverities) x verified
605
+ * methods, so a five thousand responder project with eight severities of each kind and
606
+ * two methods each holds around 340,000 of them. A single capped read returns the first
607
+ * 10,000 — roughly a hundred and fifty users' worth, in whatever order the database
608
+ * felt like — and every other responder is then scored against ZERO rules. The same
609
+ * shape on a responder-producing read (team member expansion, schedule layers) is worse
610
+ * still: those users never enter the map at all, so they appear in no count, no list and
611
+ * no "needs attention" section. The feature reports them as though they do not exist.
612
+ *
613
+ * Raising the cap does not fix that; it moves it to a slightly larger project and makes
614
+ * it harder to notice. Paging does fix it, at the cost of one round trip per full page,
615
+ * which is a price worth paying to never quietly lie about who can be paged.
616
+ *
617
+ * Three details that are load-bearing rather than incidental:
618
+ *
619
+ * - Pages are folded by the CALLER as they arrive, not accumulated and returned. The
620
+ * coverage read would otherwise materialise all 340,000 rows at once purely to
621
+ * collapse them into a map of a few thousand entries.
622
+ * - Every read is sorted, with `_id` ascending as the final tiebreak. OFFSET paging
623
+ * over an unordered — or non-totally-ordered — query may return the same row twice
624
+ * and skip another, and the default sort here would be `createdAt DESC`, which is
625
+ * emphatically not unique when a migration wrote a project's default rules in one
626
+ * transaction.
627
+ * - Hitting MAX_PAGES_PER_READ sets isTruncated AND logs an error. A truncation that
628
+ * is merely logged is invisible to the person reading the readiness table, and a
629
+ * truncation that is merely flagged is invisible to whoever has to work out why.
630
+ */
631
+ static async readEveryPage(data) {
632
+ const sort = Object.assign(Object.assign({}, (data.sort || {})), { _id: SortOrder.Ascending });
633
+ let skip = 0;
634
+ for (let page = 0; page < MAX_PAGES_PER_READ; page++) {
635
+ const rows = await data.service.findBy({
636
+ /*
637
+ * A fresh shallow copy per page: findBy hands the query object to the permission
638
+ * layer, which is free to add its own predicates to it, and a query that
639
+ * accumulated them across pages would silently narrow as it went.
640
+ */
641
+ query: Object.assign({}, data.query),
642
+ select: data.select,
643
+ sort: sort,
644
+ limit: READ_PAGE_SIZE,
645
+ skip: skip,
646
+ props: {
647
+ isRoot: true,
648
+ },
649
+ });
650
+ data.consumePage(rows);
651
+ if (rows.length < READ_PAGE_SIZE) {
652
+ return;
653
+ }
654
+ skip += rows.length;
655
+ }
656
+ data.completeness.isTruncated = true;
657
+ logger.error(`OnCallReadinessService stopped reading ${data.description} for project ${data.projectId.toString()} after ${MAX_PAGES_PER_READ} pages of ${READ_PAGE_SIZE} rows. The readiness answer for this project is INCOMPLETE: responders may be missing from it entirely, and its counts understate the number of people who cannot be paged.`);
658
+ }
659
+ /**
660
+ * The effective responder set, deduped on userId, with every source a user was reached
661
+ * by.
662
+ *
663
+ * This mirrors OnCallDutyPolicyEscalationRuleService.startRuleExecution, which is the
664
+ * runtime's own answer to "who does this page". Any divergence between the two is a
665
+ * lie in the UI: a readiness table that omits a user the runtime pages is worse than
666
+ * no table, because it actively certifies a gap as covered. Three deliberate
667
+ * alignments with the runtime:
668
+ *
669
+ * - Team members are NOT filtered by hasAcceptedInvitation, because
670
+ * TeamMemberService.getUsersInTeam does not filter either. A member who never
671
+ * accepted their invite still gets paged, so they still have to be checked.
672
+ * - Schedule layer users are taken WHOLE, not sampled for who is on call right now.
673
+ * Readiness is a property of the roster, not of this instant; a user in next
674
+ * week's rotation with no notification rule is a page that will be missed next
675
+ * week, and that is precisely what this is for.
676
+ * - Overrides contribute the user pages are ROUTED TO, not the user being covered
677
+ * for. During an override the covered user is not paged at all, while the
678
+ * substitute is — and the substitute may not be attached to the policy by any
679
+ * other means, which makes them the single most likely responder to be silently
680
+ * unreachable.
681
+ */
682
+ static async resolveResponders(projectId, onCallDutyPolicyId, completeness) {
683
+ const responders = new Map();
684
+ const addResponder = (userId, source, teamId) => {
685
+ if (!userId) {
686
+ return;
687
+ }
688
+ const key = userId.toString();
689
+ let existing = responders.get(key);
690
+ if (!existing) {
691
+ existing = {
692
+ sources: new Set(),
693
+ teamIds: new Set(),
694
+ };
695
+ responders.set(key, existing);
696
+ }
697
+ existing.sources.add(source);
698
+ if (teamId) {
699
+ existing.teamIds.add(teamId.toString());
700
+ }
701
+ };
702
+ // 1. Users attached directly to an escalation rule.
703
+ const directQuery = {
704
+ projectId: projectId,
705
+ };
706
+ if (onCallDutyPolicyId) {
707
+ directQuery.onCallDutyPolicyId = onCallDutyPolicyId;
708
+ }
709
+ await this.readEveryPage({
710
+ description: "escalation rule users",
711
+ projectId: projectId,
712
+ completeness: completeness,
713
+ service: OnCallDutyPolicyEscalationRuleUserService,
714
+ query: directQuery,
715
+ select: {
716
+ _id: true,
717
+ userId: true,
718
+ },
719
+ consumePage: (rows) => {
720
+ for (const row of rows) {
721
+ addResponder(row.userId, ResponderSource.Direct);
722
+ }
723
+ },
724
+ });
725
+ // 2. Teams attached to an escalation rule, expanded to their members.
726
+ const teamQuery = {
727
+ projectId: projectId,
728
+ };
729
+ if (onCallDutyPolicyId) {
730
+ teamQuery.onCallDutyPolicyId = onCallDutyPolicyId;
731
+ }
732
+ const escalationTeams = [];
733
+ await this.readEveryPage({
734
+ description: "escalation rule teams",
735
+ projectId: projectId,
736
+ completeness: completeness,
737
+ service: OnCallDutyPolicyEscalationRuleTeamService,
738
+ query: teamQuery,
739
+ select: {
740
+ _id: true,
741
+ teamId: true,
742
+ },
743
+ consumePage: (rows) => {
744
+ escalationTeams.push(...rows);
745
+ },
746
+ });
747
+ const teamIds = this.distinctIds(escalationTeams.map((team) => {
748
+ return team.teamId;
749
+ }));
750
+ if (teamIds.length > 0) {
751
+ /*
752
+ * ONE paged read for every team on the policy, not one per team. This is the N+1
753
+ * that made TeamComplianceService unusable on a project of any size — and the page
754
+ * loop is what keeps the fix from re-introducing the truncation it replaced, since
755
+ * five thousand users across three teams is fifteen thousand membership rows and
756
+ * the old single read returned ten thousand of them.
757
+ */
758
+ await this.readEveryPage({
759
+ description: "team members of escalation rule teams",
760
+ projectId: projectId,
761
+ completeness: completeness,
762
+ service: TeamMemberService,
763
+ query: {
764
+ projectId: projectId,
765
+ teamId: new Includes(teamIds),
766
+ },
767
+ select: {
768
+ _id: true,
769
+ userId: true,
770
+ /*
771
+ * WHICH team, not merely that a team was involved. One extra column on a read
772
+ * that already runs, and it is what lets the readiness table be filtered down
773
+ * to a team without a second pass that could disagree with this one about who
774
+ * is on it.
775
+ */
776
+ teamId: true,
777
+ },
778
+ consumePage: (rows) => {
779
+ for (const row of rows) {
780
+ addResponder(row.userId, ResponderSource.Team, row.teamId);
781
+ }
782
+ },
783
+ });
784
+ }
785
+ // 3. Schedules attached to an escalation rule, expanded to their layer users.
786
+ const scheduleQuery = {
787
+ projectId: projectId,
788
+ };
789
+ if (onCallDutyPolicyId) {
790
+ scheduleQuery.onCallDutyPolicyId = onCallDutyPolicyId;
791
+ }
792
+ const escalationSchedules = [];
793
+ await this.readEveryPage({
794
+ description: "escalation rule schedules",
795
+ projectId: projectId,
796
+ completeness: completeness,
797
+ service: OnCallDutyPolicyEscalationRuleScheduleService,
798
+ query: scheduleQuery,
799
+ select: {
800
+ _id: true,
801
+ onCallDutyPolicyScheduleId: true,
802
+ },
803
+ consumePage: (rows) => {
804
+ escalationSchedules.push(...rows);
805
+ },
806
+ });
807
+ const scheduleIds = this.distinctIds(escalationSchedules.map((schedule) => {
808
+ return schedule.onCallDutyPolicyScheduleId;
809
+ }));
810
+ if (scheduleIds.length > 0) {
811
+ /*
812
+ * Going through the escalation-rule join even for the project-wide scope is
813
+ * deliberate: a schedule that is not attached to any policy pages nobody, and
814
+ * listing its members as unready responders would be noise an admin cannot act on.
815
+ */
816
+ await this.readEveryPage({
817
+ description: "schedule layer users",
818
+ projectId: projectId,
819
+ completeness: completeness,
820
+ service: OnCallDutyPolicyScheduleLayerUserService,
821
+ query: {
822
+ projectId: projectId,
823
+ onCallDutyPolicyScheduleId: new Includes(scheduleIds),
824
+ },
825
+ select: {
826
+ _id: true,
827
+ userId: true,
828
+ },
829
+ consumePage: (rows) => {
830
+ for (const row of rows) {
831
+ addResponder(row.userId, ResponderSource.Schedule);
832
+ }
833
+ },
834
+ });
835
+ }
836
+ // 4. Users that overrides route pages to.
837
+ const overrideQuery = {
838
+ projectId: projectId,
839
+ /*
840
+ * An override that has already ended routes nothing, so it is not a reason anyone
841
+ * is a responder. Future overrides ARE included: the substitute needs to be
842
+ * reachable before their window opens, not discovered to be unreachable during it.
843
+ */
844
+ endsAt: QueryHelper.greaterThanEqualTo(OneUptimeDate.getCurrentDate()),
845
+ };
846
+ if (onCallDutyPolicyId) {
847
+ /*
848
+ * equalToOrNull, matching getRouteAlertToUserId exactly: an override with a NULL
849
+ * policy id is a GLOBAL override and applies to this policy too. Filtering on
850
+ * equality alone would drop every global override, which is the single most
851
+ * commonly configured kind.
852
+ */
853
+ overrideQuery.onCallDutyPolicyId =
854
+ QueryHelper.equalToOrNull(onCallDutyPolicyId);
855
+ }
856
+ await this.readEveryPage({
857
+ description: "on-call user overrides",
858
+ projectId: projectId,
859
+ completeness: completeness,
860
+ service: OnCallDutyPolicyUserOverrideService,
861
+ query: overrideQuery,
862
+ select: {
863
+ _id: true,
864
+ routeAlertsToUserId: true,
865
+ },
866
+ consumePage: (rows) => {
867
+ for (const row of rows) {
868
+ addResponder(row.routeAlertsToUserId, ResponderSource.Override);
869
+ }
870
+ },
871
+ });
872
+ return responders;
873
+ }
874
+ /**
875
+ * The same four sources as resolveResponders, but asked the other way round: not "who
876
+ * does this project page" but "how, if at all, does this project page THESE people".
877
+ *
878
+ * The difference is the entire fix for the amplified N+1. Answering `reachedVia` for a
879
+ * forty-member team by resolving the whole project's responder set means reading every
880
+ * escalation rule, every team's full membership and every schedule's full layer roster
881
+ * — work proportional to the project, repeated per caller, to produce four booleans per
882
+ * user. Every read here is keyed on the userIds actually asked about instead, and the
883
+ * two that cannot be (which teams and which schedules are attached to a policy) are
884
+ * keyed on just those users' teams and schedules.
885
+ *
886
+ * Users with no source at all stay in the map with an empty set, because a user who is
887
+ * on no policy yet is exactly who the add-responder modal is asking about.
888
+ */
889
+ static async resolveRespondersForUsers(data) {
890
+ const responders = new Map();
891
+ for (const userId of data.userIds) {
892
+ responders.set(userId.toString(), {
893
+ sources: new Set(),
894
+ teamIds: new Set(),
895
+ });
896
+ }
897
+ const addSource = (userId, source, teamId) => {
898
+ if (!userId) {
899
+ return;
900
+ }
901
+ /*
902
+ * A row for somebody we were not asked about is dropped rather than added. The
903
+ * reads are all filtered on the user set already; this is the guard that keeps a
904
+ * future unfiltered read from silently widening the answer.
905
+ */
906
+ const attachment = responders.get(userId.toString());
907
+ if (!attachment) {
908
+ return;
909
+ }
910
+ attachment.sources.add(source);
911
+ if (teamId) {
912
+ attachment.teamIds.add(teamId.toString());
913
+ }
914
+ };
915
+ // 1. Attached directly to an escalation rule.
916
+ await this.readEveryPage({
917
+ description: "escalation rule users for a user set",
918
+ projectId: data.projectId,
919
+ completeness: data.completeness,
920
+ service: OnCallDutyPolicyEscalationRuleUserService,
921
+ query: {
922
+ projectId: data.projectId,
923
+ userId: new Includes(data.userIds),
924
+ },
925
+ select: {
926
+ _id: true,
927
+ userId: true,
928
+ },
929
+ consumePage: (rows) => {
930
+ for (const row of rows) {
931
+ addSource(row.userId, ResponderSource.Direct);
932
+ }
933
+ },
934
+ });
935
+ // 2. In a team that is attached to an escalation rule.
936
+ const memberTeamIds = this.distinctIds(Array.from(data.teamIdsByUserId.values()).flat());
937
+ const attachedTeamIds = new Set();
938
+ if (memberTeamIds.length > 0) {
939
+ await this.readEveryPage({
940
+ description: "escalation rule teams for a user set",
941
+ projectId: data.projectId,
942
+ completeness: data.completeness,
943
+ service: OnCallDutyPolicyEscalationRuleTeamService,
944
+ query: {
945
+ projectId: data.projectId,
946
+ teamId: new Includes(memberTeamIds),
947
+ },
948
+ select: {
949
+ _id: true,
950
+ teamId: true,
951
+ },
952
+ consumePage: (rows) => {
953
+ for (const row of rows) {
954
+ if (row.teamId) {
955
+ attachedTeamIds.add(row.teamId.toString());
956
+ }
957
+ }
958
+ },
959
+ });
960
+ }
961
+ if (attachedTeamIds.size > 0) {
962
+ for (const userId of data.userIds) {
963
+ const teamIds = data.teamIdsByUserId.get(userId.toString()) || [];
964
+ /*
965
+ * Every matching team, not the first one and not a boolean. A user on two
966
+ * attached teams is paged by both, so both belong on the row and both have to
967
+ * match a team filter — the boolean this replaced could only ever have said
968
+ * "some team", which is precisely the answer that made the team filter
969
+ * impossible to build.
970
+ */
971
+ for (const teamId of teamIds) {
972
+ if (attachedTeamIds.has(teamId.toString())) {
973
+ addSource(userId, ResponderSource.Team, teamId);
974
+ }
975
+ }
976
+ }
977
+ }
978
+ // 3. On a layer of a schedule that is attached to an escalation rule.
979
+ const scheduleIdsByUserId = new Map();
980
+ await this.readEveryPage({
981
+ description: "schedule layer users for a user set",
982
+ projectId: data.projectId,
983
+ completeness: data.completeness,
984
+ service: OnCallDutyPolicyScheduleLayerUserService,
985
+ query: {
986
+ projectId: data.projectId,
987
+ userId: new Includes(data.userIds),
988
+ },
989
+ select: {
990
+ _id: true,
991
+ userId: true,
992
+ onCallDutyPolicyScheduleId: true,
993
+ },
994
+ consumePage: (rows) => {
995
+ for (const row of rows) {
996
+ if (!row.userId || !row.onCallDutyPolicyScheduleId) {
997
+ continue;
998
+ }
999
+ const key = row.userId.toString();
1000
+ const existing = scheduleIdsByUserId.get(key);
1001
+ if (existing) {
1002
+ existing.push(row.onCallDutyPolicyScheduleId);
1003
+ continue;
1004
+ }
1005
+ scheduleIdsByUserId.set(key, [row.onCallDutyPolicyScheduleId]);
1006
+ }
1007
+ },
1008
+ });
1009
+ const memberScheduleIds = this.distinctIds(Array.from(scheduleIdsByUserId.values()).flat());
1010
+ const attachedScheduleIds = new Set();
1011
+ if (memberScheduleIds.length > 0) {
1012
+ await this.readEveryPage({
1013
+ description: "escalation rule schedules for a user set",
1014
+ projectId: data.projectId,
1015
+ completeness: data.completeness,
1016
+ service: OnCallDutyPolicyEscalationRuleScheduleService,
1017
+ query: {
1018
+ projectId: data.projectId,
1019
+ onCallDutyPolicyScheduleId: new Includes(memberScheduleIds),
1020
+ },
1021
+ select: {
1022
+ _id: true,
1023
+ onCallDutyPolicyScheduleId: true,
1024
+ },
1025
+ consumePage: (rows) => {
1026
+ for (const row of rows) {
1027
+ if (row.onCallDutyPolicyScheduleId) {
1028
+ attachedScheduleIds.add(row.onCallDutyPolicyScheduleId.toString());
1029
+ }
1030
+ }
1031
+ },
1032
+ });
1033
+ }
1034
+ if (attachedScheduleIds.size > 0) {
1035
+ for (const userId of data.userIds) {
1036
+ const scheduleIds = scheduleIdsByUserId.get(userId.toString()) || [];
1037
+ const isOnAnAttachedSchedule = scheduleIds.some((scheduleId) => {
1038
+ return attachedScheduleIds.has(scheduleId.toString());
1039
+ });
1040
+ if (isOnAnAttachedSchedule) {
1041
+ addSource(userId, ResponderSource.Schedule);
1042
+ }
1043
+ }
1044
+ }
1045
+ // 4. Substituted in by an override that has not ended.
1046
+ await this.readEveryPage({
1047
+ description: "on-call user overrides for a user set",
1048
+ projectId: data.projectId,
1049
+ completeness: data.completeness,
1050
+ service: OnCallDutyPolicyUserOverrideService,
1051
+ query: {
1052
+ projectId: data.projectId,
1053
+ routeAlertsToUserId: new Includes(data.userIds),
1054
+ endsAt: QueryHelper.greaterThanEqualTo(OneUptimeDate.getCurrentDate()),
1055
+ },
1056
+ select: {
1057
+ _id: true,
1058
+ routeAlertsToUserId: true,
1059
+ },
1060
+ consumePage: (rows) => {
1061
+ for (const row of rows) {
1062
+ addSource(row.routeAlertsToUserId, ResponderSource.Override);
1063
+ }
1064
+ },
1065
+ });
1066
+ return responders;
1067
+ }
1068
+ /**
1069
+ * Which teams each of these users belongs to in this project — and, by existing at
1070
+ * all, whether they belong to the project.
1071
+ *
1072
+ * One read doing both jobs is not a trick: team membership IS what "in this project"
1073
+ * means for a User, which is a global model. Reading the teamIds at the same time is
1074
+ * free and is what lets the Team responder source be resolved without expanding every
1075
+ * team in the project into its full membership.
1076
+ */
1077
+ static async loadProjectMembership(projectId, userIds, completeness) {
1078
+ const teamIdsByUserId = new Map();
1079
+ await this.readEveryPage({
1080
+ description: "project membership for a user set",
1081
+ projectId: projectId,
1082
+ completeness: completeness,
1083
+ service: TeamMemberService,
1084
+ query: {
1085
+ projectId: projectId,
1086
+ userId: new Includes(userIds),
1087
+ },
1088
+ select: {
1089
+ _id: true,
1090
+ userId: true,
1091
+ teamId: true,
1092
+ },
1093
+ consumePage: (rows) => {
1094
+ for (const row of rows) {
1095
+ if (!row.userId) {
1096
+ continue;
1097
+ }
1098
+ const key = row.userId.toString();
1099
+ const existing = teamIdsByUserId.get(key);
1100
+ if (existing) {
1101
+ if (row.teamId) {
1102
+ existing.push(row.teamId);
1103
+ }
1104
+ continue;
1105
+ }
1106
+ teamIdsByUserId.set(key, row.teamId ? [row.teamId] : []);
1107
+ }
1108
+ },
1109
+ });
1110
+ return teamIdsByUserId;
1111
+ }
1112
+ /**
1113
+ * Turn a resolved responder set into per-user readiness. Every read here is batched
1114
+ * over the whole set; the number of queries does not grow with the number of users or
1115
+ * the number of severities.
1116
+ */
1117
+ static async buildReadiness(data) {
1118
+ var _a;
1119
+ const userIds = Array.from(data.responders.keys()).map((userId) => {
1120
+ return new ObjectID(userId);
1121
+ });
1122
+ if (userIds.length === 0) {
1123
+ return [];
1124
+ }
1125
+ const inputs = await this.loadInputs({
1126
+ projectId: data.projectId,
1127
+ userIds: userIds,
1128
+ projectSettings: data.projectSettings,
1129
+ completeness: data.completeness,
1130
+ });
1131
+ /*
1132
+ * ONE read for every team named anywhere in the responder set, rather than one per
1133
+ * responder. A team on an escalation rule is shared by every one of its members, so
1134
+ * the per-member read would be the same N+1 that made the report this service
1135
+ * replaced unusable — see readEveryPage.
1136
+ */
1137
+ const teamNamesById = await this.loadTeamNames({
1138
+ projectId: data.projectId,
1139
+ teamIds: this.distinctIds(Array.from(data.responders.values())
1140
+ .flatMap((attachment) => {
1141
+ return Array.from(attachment.teamIds);
1142
+ })
1143
+ .map((teamId) => {
1144
+ return new ObjectID(teamId);
1145
+ })),
1146
+ completeness: data.completeness,
1147
+ });
1148
+ const readiness = [];
1149
+ for (const user of inputs.users) {
1150
+ const userIdString = ((_a = user.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
1151
+ const attachment = data.responders.get(userIdString) || {
1152
+ sources: new Set(),
1153
+ teamIds: new Set(),
1154
+ };
1155
+ readiness.push(this.buildUserReadiness(user, attachment, userIdString, inputs, teamNamesById));
1156
+ }
1157
+ return this.sortReadiness(readiness);
1158
+ }
1159
+ /**
1160
+ * Team id -> team name, for every team that pages somebody in the responder set.
1161
+ *
1162
+ * A team whose row did not come back is simply absent from the map, and
1163
+ * buildUserReadiness then drops it from the responder's `teams` rather than rendering an
1164
+ * id or an empty chip. That is the right direction to fail in for a filter: an option
1165
+ * that cannot be labelled is an option nobody can choose deliberately, whereas a chip
1166
+ * reading a bare uuid is one an admin might act on.
1167
+ */
1168
+ static async loadTeamNames(data) {
1169
+ const teamNamesById = new Map();
1170
+ if (data.teamIds.length === 0) {
1171
+ return teamNamesById;
1172
+ }
1173
+ await this.readEveryPage({
1174
+ description: "teams that page a responder",
1175
+ projectId: data.projectId,
1176
+ completeness: data.completeness,
1177
+ service: TeamService,
1178
+ query: {
1179
+ projectId: data.projectId,
1180
+ _id: new Includes(data.teamIds),
1181
+ },
1182
+ select: {
1183
+ _id: true,
1184
+ name: true,
1185
+ },
1186
+ consumePage: (rows) => {
1187
+ for (const row of rows) {
1188
+ if (row.id && row.name) {
1189
+ teamNamesById.set(row.id.toString(), row.name);
1190
+ }
1191
+ }
1192
+ },
1193
+ });
1194
+ return teamNamesById;
1195
+ }
1196
+ static sortReadiness(readiness) {
1197
+ readiness.sort((a, b) => {
1198
+ const rankDifference = STATUS_SORT_RANK[a.status] - STATUS_SORT_RANK[b.status];
1199
+ if (rankDifference !== 0) {
1200
+ return rankDifference;
1201
+ }
1202
+ return a.userName.localeCompare(b.userName);
1203
+ });
1204
+ return readiness;
1205
+ }
1206
+ /*
1207
+ * Read the project's switches once. They are what decides whether "no rule for Sev4"
1208
+ * means "falls back to email" or "is dropped on the floor", and whether a verified SMS
1209
+ * number is a way to reach somebody or a decoration. A readiness surface that cannot
1210
+ * tell those apart is not diagnosing anything.
1211
+ */
1212
+ static async loadProjectSettings(projectId) {
1213
+ const project = await ProjectService.findOneById({
1214
+ id: projectId,
1215
+ select: {
1216
+ _id: true,
1217
+ disableOnCallNotificationFallback: true,
1218
+ enableSmsNotifications: true,
1219
+ enableCallNotifications: true,
1220
+ enableWhatsAppNotifications: true,
1221
+ enableTelegramNotifications: true,
1222
+ },
1223
+ props: {
1224
+ isRoot: true,
1225
+ },
1226
+ });
1227
+ /*
1228
+ * A missing project row reads as every paid channel OFF and the fallback ON, which
1229
+ * is the pairing that produces the loudest answer rather than the most convenient
1230
+ * one. Defaulting a switch to "on" would let a project we could not read certify
1231
+ * responders as reachable on channels that may be switched off.
1232
+ */
1233
+ return {
1234
+ isFallbackDisabled: Boolean(project === null || project === void 0 ? void 0 : project.disableOnCallNotificationFallback),
1235
+ enableSmsNotifications: Boolean(project === null || project === void 0 ? void 0 : project.enableSmsNotifications),
1236
+ enableCallNotifications: Boolean(project === null || project === void 0 ? void 0 : project.enableCallNotifications),
1237
+ enableWhatsAppNotifications: Boolean(project === null || project === void 0 ? void 0 : project.enableWhatsAppNotifications),
1238
+ enableTelegramNotifications: Boolean(project === null || project === void 0 ? void 0 : project.enableTelegramNotifications),
1239
+ };
1240
+ }
1241
+ static async loadInputs(data) {
1242
+ const users = [];
1243
+ await this.readEveryPage({
1244
+ description: "responder user records",
1245
+ projectId: data.projectId,
1246
+ completeness: data.completeness,
1247
+ service: UserService,
1248
+ query: {
1249
+ _id: new Includes(data.userIds),
1250
+ },
1251
+ select: {
1252
+ _id: true,
1253
+ name: true,
1254
+ email: true,
1255
+ profilePictureId: true,
1256
+ },
1257
+ consumePage: (rows) => {
1258
+ users.push(...rows);
1259
+ },
1260
+ });
1261
+ const methodsByUserId = await this.loadMethods(data.projectId, data.userIds, data.completeness);
1262
+ const coverageByKey = await this.loadCoverageIndex(data.projectId, data.userIds, data.completeness);
1263
+ const incidentSeverityModels = [];
1264
+ await this.readEveryPage({
1265
+ description: "incident severities",
1266
+ projectId: data.projectId,
1267
+ completeness: data.completeness,
1268
+ service: IncidentSeverityService,
1269
+ query: {
1270
+ projectId: data.projectId,
1271
+ },
1272
+ select: {
1273
+ _id: true,
1274
+ name: true,
1275
+ },
1276
+ sort: {
1277
+ order: SortOrder.Ascending,
1278
+ },
1279
+ consumePage: (rows) => {
1280
+ incidentSeverityModels.push(...rows);
1281
+ },
1282
+ });
1283
+ const alertSeverityModels = [];
1284
+ await this.readEveryPage({
1285
+ description: "alert severities",
1286
+ projectId: data.projectId,
1287
+ completeness: data.completeness,
1288
+ service: AlertSeverityService,
1289
+ query: {
1290
+ projectId: data.projectId,
1291
+ },
1292
+ select: {
1293
+ _id: true,
1294
+ name: true,
1295
+ },
1296
+ sort: {
1297
+ order: SortOrder.Ascending,
1298
+ },
1299
+ consumePage: (rows) => {
1300
+ alertSeverityModels.push(...rows);
1301
+ },
1302
+ });
1303
+ return {
1304
+ users: users,
1305
+ methodsByUserId: methodsByUserId,
1306
+ coverageByKey: coverageByKey,
1307
+ incidentSeverities: this.toSeverityRefs(incidentSeverityModels),
1308
+ alertSeverities: this.toSeverityRefs(alertSeverityModels),
1309
+ projectSettings: data.projectSettings,
1310
+ };
1311
+ }
1312
+ /**
1313
+ * One paged read per method model, each over the whole responder set.
1314
+ *
1315
+ * All SEVEN channels are here. TeamComplianceService looked at four and therefore told
1316
+ * a responder whose only method was Telegram, WhatsApp or Webhook that they were
1317
+ * non-compliant while the runtime was quite happily paging them — a false alarm that
1318
+ * teaches admins to ignore the table, which is worse than the table not existing.
1319
+ */
1320
+ static async loadMethods(projectId, userIds, completeness) {
1321
+ const methodsByUserId = new Map();
1322
+ const addMethod = (row, method) => {
1323
+ const methodId = row.id;
1324
+ /*
1325
+ * A row with no owner, or no id of its own, is dropped. Neither is reachable while
1326
+ * every select below asks for `_id` and every method row is owned — a primary key
1327
+ * is not optional in the database — so this is a guard against a future select
1328
+ * being trimmed rather than a case that happens.
1329
+ *
1330
+ * It is a drop rather than a partial emit because the alternatives are both worse.
1331
+ * Emitting the method without an id would mean typing methodId as optional, which
1332
+ * pushes this impossible case out to every caller and gives the rule form an option
1333
+ * it cannot submit. Dropping errs towards reporting the responder as LESS reachable
1334
+ * than they are, which is the direction this service always errs in: a false amber
1335
+ * gets investigated, and a false green does not.
1336
+ */
1337
+ if (!row.userId || !methodId) {
1338
+ return;
1339
+ }
1340
+ const readinessMethod = Object.assign({ methodId: methodId }, method);
1341
+ const key = row.userId.toString();
1342
+ const existing = methodsByUserId.get(key);
1343
+ if (existing) {
1344
+ existing.push(readinessMethod);
1345
+ return;
1346
+ }
1347
+ methodsByUserId.set(key, [readinessMethod]);
1348
+ };
1349
+ await this.readEveryPage({
1350
+ description: "push notification methods",
1351
+ projectId: projectId,
1352
+ completeness: completeness,
1353
+ service: UserPushService,
1354
+ query: {
1355
+ projectId: projectId,
1356
+ userId: new Includes(userIds),
1357
+ },
1358
+ select: {
1359
+ _id: true,
1360
+ userId: true,
1361
+ deviceName: true,
1362
+ isVerified: true,
1363
+ },
1364
+ consumePage: (rows) => {
1365
+ for (const row of rows) {
1366
+ addMethod(row, {
1367
+ methodType: ReadinessMethodType.Push,
1368
+ maskedIdentifier: maskIdentifier(row.deviceName, MaskedIdentifierKind.Handle),
1369
+ isVerified: Boolean(row.isVerified),
1370
+ });
1371
+ }
1372
+ },
1373
+ });
1374
+ await this.readEveryPage({
1375
+ description: "email notification methods",
1376
+ projectId: projectId,
1377
+ completeness: completeness,
1378
+ service: UserEmailService,
1379
+ query: {
1380
+ projectId: projectId,
1381
+ userId: new Includes(userIds),
1382
+ },
1383
+ select: {
1384
+ _id: true,
1385
+ userId: true,
1386
+ email: true,
1387
+ isVerified: true,
1388
+ },
1389
+ consumePage: (rows) => {
1390
+ var _a;
1391
+ for (const row of rows) {
1392
+ addMethod(row, {
1393
+ methodType: ReadinessMethodType.Email,
1394
+ maskedIdentifier: maskIdentifier((_a = row.email) === null || _a === void 0 ? void 0 : _a.toString(), MaskedIdentifierKind.Email),
1395
+ isVerified: Boolean(row.isVerified),
1396
+ });
1397
+ }
1398
+ },
1399
+ });
1400
+ await this.readEveryPage({
1401
+ description: "SMS notification methods",
1402
+ projectId: projectId,
1403
+ completeness: completeness,
1404
+ service: UserSmsService,
1405
+ query: {
1406
+ projectId: projectId,
1407
+ userId: new Includes(userIds),
1408
+ },
1409
+ select: {
1410
+ _id: true,
1411
+ userId: true,
1412
+ phone: true,
1413
+ isVerified: true,
1414
+ },
1415
+ consumePage: (rows) => {
1416
+ var _a;
1417
+ for (const row of rows) {
1418
+ addMethod(row, {
1419
+ methodType: ReadinessMethodType.SMS,
1420
+ maskedIdentifier: maskIdentifier((_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(), MaskedIdentifierKind.Phone),
1421
+ isVerified: Boolean(row.isVerified),
1422
+ });
1423
+ }
1424
+ },
1425
+ });
1426
+ await this.readEveryPage({
1427
+ description: "call notification methods",
1428
+ projectId: projectId,
1429
+ completeness: completeness,
1430
+ service: UserCallService,
1431
+ query: {
1432
+ projectId: projectId,
1433
+ userId: new Includes(userIds),
1434
+ },
1435
+ select: {
1436
+ _id: true,
1437
+ userId: true,
1438
+ phone: true,
1439
+ isVerified: true,
1440
+ },
1441
+ consumePage: (rows) => {
1442
+ var _a;
1443
+ for (const row of rows) {
1444
+ addMethod(row, {
1445
+ methodType: ReadinessMethodType.Call,
1446
+ maskedIdentifier: maskIdentifier((_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(), MaskedIdentifierKind.Phone),
1447
+ isVerified: Boolean(row.isVerified),
1448
+ });
1449
+ }
1450
+ },
1451
+ });
1452
+ await this.readEveryPage({
1453
+ description: "WhatsApp notification methods",
1454
+ projectId: projectId,
1455
+ completeness: completeness,
1456
+ service: UserWhatsAppService,
1457
+ query: {
1458
+ projectId: projectId,
1459
+ userId: new Includes(userIds),
1460
+ },
1461
+ select: {
1462
+ _id: true,
1463
+ userId: true,
1464
+ phone: true,
1465
+ isVerified: true,
1466
+ },
1467
+ consumePage: (rows) => {
1468
+ var _a;
1469
+ for (const row of rows) {
1470
+ addMethod(row, {
1471
+ methodType: ReadinessMethodType.WhatsApp,
1472
+ maskedIdentifier: maskIdentifier((_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(), MaskedIdentifierKind.Phone),
1473
+ isVerified: Boolean(row.isVerified),
1474
+ });
1475
+ }
1476
+ },
1477
+ });
1478
+ await this.readEveryPage({
1479
+ description: "Telegram notification methods",
1480
+ projectId: projectId,
1481
+ completeness: completeness,
1482
+ service: UserTelegramService,
1483
+ query: {
1484
+ projectId: projectId,
1485
+ userId: new Includes(userIds),
1486
+ },
1487
+ /*
1488
+ * The handle only — never telegramChatId. The chat id is the addressable target a
1489
+ * bot sends to; the handle is the human-facing label, and it is the one a user can
1490
+ * recognise as theirs.
1491
+ */
1492
+ select: {
1493
+ _id: true,
1494
+ userId: true,
1495
+ telegramUserHandle: true,
1496
+ isVerified: true,
1497
+ },
1498
+ consumePage: (rows) => {
1499
+ for (const row of rows) {
1500
+ addMethod(row, {
1501
+ methodType: ReadinessMethodType.Telegram,
1502
+ maskedIdentifier: maskIdentifier(row.telegramUserHandle, MaskedIdentifierKind.Handle),
1503
+ isVerified: Boolean(row.isVerified),
1504
+ });
1505
+ }
1506
+ },
1507
+ });
1508
+ await this.readEveryPage({
1509
+ description: "webhook notification methods",
1510
+ projectId: projectId,
1511
+ completeness: completeness,
1512
+ service: UserWebhookService,
1513
+ query: {
1514
+ projectId: projectId,
1515
+ userId: new Includes(userIds),
1516
+ },
1517
+ /*
1518
+ * `name` ONLY. UserWebhook.webhookUrl is a bearer credential — anyone holding a
1519
+ * Slack/Discord/Teams hook URL can post as the integration — so it is never
1520
+ * selected here and never leaves the server on this path.
1521
+ * Common/UI/Utils/NotificationMethodUtil.ts documents the same rule for the rule
1522
+ * tables and reads only `name`; this follows it. The masked name is enough to
1523
+ * answer the only question readiness asks, which is whether a webhook exists.
1524
+ */
1525
+ select: {
1526
+ _id: true,
1527
+ userId: true,
1528
+ name: true,
1529
+ },
1530
+ consumePage: (rows) => {
1531
+ for (const row of rows) {
1532
+ /*
1533
+ * isVerified: true, with no isVerified column behind it. UserWebhook has no
1534
+ * verification concept at all — its presence IS the whole test, which is
1535
+ * exactly how the runtime fallback treats it. Reporting it as unverified would
1536
+ * paint the one channel that is guaranteed to work as the one channel that
1537
+ * will not.
1538
+ */
1539
+ addMethod(row, {
1540
+ methodType: ReadinessMethodType.Webhook,
1541
+ maskedIdentifier: maskIdentifier(row.name, MaskedIdentifierKind.Handle),
1542
+ isVerified: true,
1543
+ });
1544
+ }
1545
+ },
1546
+ });
1547
+ for (const methods of methodsByUserId.values()) {
1548
+ methods.sort((a, b) => {
1549
+ return (METHOD_DISPLAY_ORDER.indexOf(a.methodType) -
1550
+ METHOD_DISPLAY_ORDER.indexOf(b.methodType));
1551
+ });
1552
+ }
1553
+ return methodsByUserId;
1554
+ }
1555
+ /**
1556
+ * ONE paged read over UserNotificationRule for the whole responder set, folded into a
1557
+ * map keyed by (userId, ruleType, severityId).
1558
+ *
1559
+ * The folding happens per page rather than after the read for a reason that is not
1560
+ * about tidiness: this is by far the largest table this service touches — users x rule
1561
+ * types x severities x methods — so a project of a few thousand responders holds
1562
+ * hundreds of thousands of rows here, all of which collapse into at most a few cells
1563
+ * per user. Accumulating them first and folding second would hold the whole table in
1564
+ * memory to produce a map a thousand times smaller.
1565
+ *
1566
+ * Opt-out rows are read alongside real rules rather than filtered out in SQL, because
1567
+ * both halves are needed: a cell with an opt-out is Ready (deliberate silence) while a
1568
+ * cell with nothing at all is PartiallyReady (silence nobody chose), and telling those
1569
+ * apart is the entire reason the isOptOut column exists.
1570
+ *
1571
+ * The in-memory split is `isOptOut === true`, which is the exact dual of the
1572
+ * notOptOutRuleQuery predicate the paging path uses — deliberately NOT `isOptOut ===
1573
+ * false`. The column is nullable and was added long after these rows started existing,
1574
+ * so it is NULL on every rule in every existing install. Testing for false would
1575
+ * classify all of them as neither rules nor opt-outs, and this service would report a
1576
+ * fully-configured project as entirely unready.
1577
+ */
1578
+ static async loadCoverageIndex(projectId, userIds, completeness) {
1579
+ const coverageByKey = new Map();
1580
+ await this.readEveryPage({
1581
+ description: "user notification rules",
1582
+ projectId: projectId,
1583
+ completeness: completeness,
1584
+ service: UserNotificationRuleService,
1585
+ query: {
1586
+ projectId: projectId,
1587
+ userId: new Includes(userIds),
1588
+ },
1589
+ select: {
1590
+ _id: true,
1591
+ userId: true,
1592
+ ruleType: true,
1593
+ incidentSeverityId: true,
1594
+ alertSeverityId: true,
1595
+ isOptOut: true,
1596
+ },
1597
+ consumePage: (rows) => {
1598
+ for (const rule of rows) {
1599
+ this.foldRuleIntoCoverage(rule, coverageByKey);
1600
+ }
1601
+ },
1602
+ });
1603
+ return coverageByKey;
1604
+ }
1605
+ static foldRuleIntoCoverage(rule, coverageByKey) {
1606
+ var _a;
1607
+ const scope = RULE_TYPE_SCOPES.find((candidate) => {
1608
+ return candidate.ruleType === rule.ruleType;
1609
+ });
1610
+ /*
1611
+ * Not one of the four paging rule types — a handoff rule, or something added later.
1612
+ * It covers no page, so it covers no cell. This is defect 4 of
1613
+ * TeamComplianceService, which matched on severity alone and let a
1614
+ * WHEN_USER_GOES_OFF_CALL rule certify incident coverage.
1615
+ */
1616
+ if (!scope) {
1617
+ return;
1618
+ }
1619
+ /*
1620
+ * Take the severity from the column the RULE TYPE dictates, never from whichever one
1621
+ * happens to be populated. An alert rule carrying a stray incidentSeverityId matches
1622
+ * no page at runtime, so it must not be allowed to satisfy a cell here.
1623
+ */
1624
+ const severityId = scope.severityKind === SeverityKind.Incident
1625
+ ? rule.incidentSeverityId
1626
+ : rule.alertSeverityId;
1627
+ /*
1628
+ * A severity-scoped rule with a NULL severity is the Gap G corpse: the paging path
1629
+ * counts episode rules filtered by a concrete severity id, so NULL matches nothing
1630
+ * and the rule is unreachable. Counting it as coverage would report exactly the
1631
+ * users worst affected by that bug as fully ready.
1632
+ */
1633
+ if (!severityId) {
1634
+ return;
1635
+ }
1636
+ const key = buildCoverageKey(((_a = rule.userId) === null || _a === void 0 ? void 0 : _a.toString()) || "", scope.ruleType, severityId.toString());
1637
+ const state = coverageByKey.get(key) || {
1638
+ hasRule: false,
1639
+ isOptOut: false,
1640
+ };
1641
+ if (rule.isOptOut === true) {
1642
+ state.isOptOut = true;
1643
+ }
1644
+ else {
1645
+ state.hasRule = true;
1646
+ }
1647
+ coverageByKey.set(key, state);
1648
+ }
1649
+ static buildUserReadiness(user, attachment, userIdString, inputs, teamNamesById) {
1650
+ var _a, _b, _c;
1651
+ const sources = attachment.sources;
1652
+ const methods = inputs.methodsByUserId.get(userIdString) || [];
1653
+ const coverage = [];
1654
+ for (const scope of RULE_TYPE_SCOPES) {
1655
+ const severities = scope.severityKind === SeverityKind.Incident
1656
+ ? inputs.incidentSeverities
1657
+ : inputs.alertSeverities;
1658
+ for (const severity of severities) {
1659
+ const state = inputs.coverageByKey.get(buildCoverageKey(userIdString, scope.ruleType, severity.id.toString()));
1660
+ coverage.push({
1661
+ ruleType: scope.ruleType,
1662
+ severityId: severity.id,
1663
+ severityName: severity.name,
1664
+ hasRule: Boolean(state === null || state === void 0 ? void 0 : state.hasRule),
1665
+ isOptOut: Boolean(state === null || state === void 0 ? void 0 : state.isOptOut),
1666
+ });
1667
+ }
1668
+ }
1669
+ /*
1670
+ * Verified is necessary and not sufficient — webhooks are reported verified by
1671
+ * construction above, so this one predicate covers the "webhook counts without
1672
+ * verification" rule without a special case leaking into the status logic.
1673
+ */
1674
+ const verifiedMethods = methods.filter((method) => {
1675
+ return method.isVerified;
1676
+ });
1677
+ /*
1678
+ * USABLE is verified AND on a channel the project has switched on. Status used to be
1679
+ * computed from verification alone, which meant a responder whose only verified
1680
+ * methods were SMS and Call, in a project with SMS and Call switched off, rendered
1681
+ * Ready and green while being completely unpageable — the exact false green this
1682
+ * service exists to make impossible. The project switches are already loaded, sit
1683
+ * two fields away, and the runtime consults them on every send, so ignoring them here
1684
+ * was never a judgement call.
1685
+ */
1686
+ const usableMethods = verifiedMethods.filter((method) => {
1687
+ return this.isChannelEnabled(method.methodType, inputs.projectSettings);
1688
+ });
1689
+ const disabledChannels = this.distinctStrings(verifiedMethods
1690
+ .filter((method) => {
1691
+ return !this.isChannelEnabled(method.methodType, inputs.projectSettings);
1692
+ })
1693
+ .map((method) => {
1694
+ return method.methodType;
1695
+ }));
1696
+ const uncoveredCells = coverage.filter((cell) => {
1697
+ return !cell.hasRule && !cell.isOptOut;
1698
+ });
1699
+ let status = ReadinessStatus.Ready;
1700
+ if (usableMethods.length === 0) {
1701
+ status = ReadinessStatus.NotReachable;
1702
+ }
1703
+ else if (uncoveredCells.length > 0) {
1704
+ status = ReadinessStatus.PartiallyReady;
1705
+ }
1706
+ const reasons = this.buildReasons({
1707
+ status: status,
1708
+ methods: methods,
1709
+ verifiedMethods: verifiedMethods,
1710
+ usableMethods: usableMethods,
1711
+ disabledChannels: disabledChannels,
1712
+ uncoveredCells: uncoveredCells,
1713
+ projectSettings: inputs.projectSettings,
1714
+ });
1715
+ return {
1716
+ userId: user.id,
1717
+ userName: ((_a = user.name) === null || _a === void 0 ? void 0 : _a.toString()) || ((_b = user.email) === null || _b === void 0 ? void 0 : _b.toString()) || "Unknown User",
1718
+ userEmail: ((_c = user.email) === null || _c === void 0 ? void 0 : _c.toString()) || "",
1719
+ userProfilePictureId: user.profilePictureId,
1720
+ status: status,
1721
+ methods: methods,
1722
+ coverage: coverage,
1723
+ reasons: reasons,
1724
+ reachedVia: RESPONDER_SOURCE_ORDER.filter((source) => {
1725
+ return sources.has(source);
1726
+ }),
1727
+ /*
1728
+ * A fresh array per user, never a shared one. InMemoryTTLCache stores by reference
1729
+ * and hands the same object graph to every caller inside its TTL, so a list shared
1730
+ * between two responders would let a mutation anywhere downstream rewrite the
1731
+ * cached answer for both.
1732
+ *
1733
+ * Sorted by name so the column and the filter chip read the same way for every
1734
+ * responder, and so the order does not depend on which membership row the database
1735
+ * happened to return first.
1736
+ */
1737
+ teams: Array.from(attachment.teamIds)
1738
+ .map((teamId) => {
1739
+ const name = teamNamesById.get(teamId);
1740
+ return name ? { _id: new ObjectID(teamId), name: name } : null;
1741
+ })
1742
+ .filter((team) => {
1743
+ return team !== null;
1744
+ })
1745
+ .sort((a, b) => {
1746
+ return a.name.localeCompare(b.name);
1747
+ }),
1748
+ };
1749
+ }
1750
+ /**
1751
+ * Whether this project can send on this channel at all.
1752
+ *
1753
+ * Push, Email and Webhook have no project switch: the first two are zero-cost and the
1754
+ * third is somebody else's endpoint, so nothing gates them and they are always
1755
+ * available. The four paid channels do, and the runtime honours them — SmsService and
1756
+ * CallService refuse at send time, and the fallback checks all four before it picks a
1757
+ * channel to spend money on. Treating all four alike here is the conservative reading:
1758
+ * a responder marked unreachable because their project switched their only channel off
1759
+ * is an alarm somebody can act on, while the reverse mistake is a page nobody hears.
1760
+ */
1761
+ static isChannelEnabled(methodType, settings) {
1762
+ if (methodType === ReadinessMethodType.SMS) {
1763
+ return settings.enableSmsNotifications;
1764
+ }
1765
+ if (methodType === ReadinessMethodType.Call) {
1766
+ return settings.enableCallNotifications;
1767
+ }
1768
+ if (methodType === ReadinessMethodType.WhatsApp) {
1769
+ return settings.enableWhatsAppNotifications;
1770
+ }
1771
+ if (methodType === ReadinessMethodType.Telegram) {
1772
+ return settings.enableTelegramNotifications;
1773
+ }
1774
+ return true;
1775
+ }
1776
+ /**
1777
+ * Sentences an admin can act on, in the order they should act on them.
1778
+ *
1779
+ * Not status names, not rule-type enum values, not "non-compliant" — every line names
1780
+ * a specific missing thing and what happens because of it. The consequence clause is
1781
+ * the part that matters: "no rule for Sev4" is a shrug, "no rule for Sev4, pages are
1782
+ * dropped" is a ticket.
1783
+ */
1784
+ static buildReasons(data) {
1785
+ const reasons = [];
1786
+ if (data.status === ReadinessStatus.NotReachable) {
1787
+ /*
1788
+ * Three ways to be unreachable, and they need three different sentences because
1789
+ * they need three different people to fix them: the user adds a method, the user
1790
+ * verifies a method, or an admin turns a channel back on. A single "cannot be
1791
+ * paged" line sends all three to the wrong place.
1792
+ */
1793
+ if (data.verifiedMethods.length > 0) {
1794
+ reasons.push("No usable notification method - cannot be paged");
1795
+ reasons.push(`Every method they have verified is on ${data.disabledChannels.join(", ")}, and this project has ${data.disabledChannels.length === 1 ? "that channel" : "those channels"} switched off - that is a project setting, not something this user can fix`);
1796
+ return reasons;
1797
+ }
1798
+ reasons.push("No verified notification method - cannot be paged");
1799
+ /*
1800
+ * "They have nothing" and "they have something they never verified" look identical
1801
+ * in a status chip and could not be more different to fix: the second needs one
1802
+ * click from the user, not a conversation about how on-call works.
1803
+ */
1804
+ if (data.methods.length > 0) {
1805
+ const unverified = this.distinctStrings(data.methods.map((method) => {
1806
+ return method.methodType;
1807
+ }));
1808
+ reasons.push(`Added ${unverified.join(", ")} but never verified - unverified methods are never used`);
1809
+ }
1810
+ else {
1811
+ reasons.push("Ask this user to add and verify a notification method in User Settings > Notification Methods");
1812
+ }
1813
+ /*
1814
+ * Coverage is meaningless for someone nothing can reach. Listing their missing
1815
+ * rules underneath would bury the one sentence that matters under a dozen that do
1816
+ * not.
1817
+ */
1818
+ return reasons;
1819
+ }
1820
+ /*
1821
+ * Note what is deliberately NOT said here. A responder who still has one working
1822
+ * channel but also has, say, an SMS number the project has switched off is reachable,
1823
+ * and saying so on their row would put a warning sentence on every single user in a
1824
+ * project that has switched SMS off — a line nobody can act on, attached to people
1825
+ * who have nothing wrong with them, which is how a readiness surface teaches admins
1826
+ * to stop reading it. The stranded channel matters only when it is the reason nobody
1827
+ * can be reached, and that case is handled above.
1828
+ */
1829
+ if (data.uncoveredCells.length === 0) {
1830
+ return reasons;
1831
+ }
1832
+ const fallbackChannels = this.describeFallbackChannels(data.usableMethods, data.projectSettings);
1833
+ /*
1834
+ * One sentence per rule type, listing its missing severities, rather than one per
1835
+ * cell. A project with four severities and no rules at all would otherwise produce
1836
+ * sixteen near-identical lines that nobody reads to the end of.
1837
+ */
1838
+ for (const scope of RULE_TYPE_SCOPES) {
1839
+ const severityNames = this.distinctStrings(data.uncoveredCells
1840
+ .filter((cell) => {
1841
+ return cell.ruleType === scope.ruleType;
1842
+ })
1843
+ .map((cell) => {
1844
+ return cell.severityName || "this severity";
1845
+ }));
1846
+ if (severityNames.length === 0) {
1847
+ continue;
1848
+ }
1849
+ const subject = `No rules for ${severityNames.join(", ")} ${scope.subjectNoun}`;
1850
+ if (data.projectSettings.isFallbackDisabled) {
1851
+ reasons.push(`${subject} - pages are dropped because on-call fallback is disabled for this project`);
1852
+ }
1853
+ else if (fallbackChannels.length === 0) {
1854
+ /*
1855
+ * Not reachable from here today — a usable method always yields a fallback
1856
+ * channel — but kept so that a future channel with no fallback path degrades
1857
+ * into an honest sentence instead of "pages fall back to " with nothing after
1858
+ * it.
1859
+ */
1860
+ reasons.push(`${subject} - pages cannot be delivered`);
1861
+ }
1862
+ else {
1863
+ reasons.push(`${subject} - pages fall back to ${fallbackChannels.join(", ")}`);
1864
+ }
1865
+ }
1866
+ return reasons;
1867
+ }
1868
+ /**
1869
+ * The channels a fallback page would actually arrive on, so the reason sentences name
1870
+ * the real thing rather than a hopeful "some verified method".
1871
+ *
1872
+ * This mirrors UserNotificationRuleService.chooseFallbackChannels exactly, including
1873
+ * the project switches: zero-cost channels first and BOTH of them if present, then one
1874
+ * paid channel in escalating-intrusiveness order, then webhook. If that function's
1875
+ * order ever changes, this one has to change with it — a readiness surface promising
1876
+ * "falls back to SMS" for a project that has SMS switched off is worse than saying
1877
+ * nothing. The switch checks are redundant with the usable-method filter upstream and
1878
+ * are kept anyway, because this function's whole value is being a line-by-line mirror
1879
+ * of the one that does the real thing.
1880
+ */
1881
+ static describeFallbackChannels(usableMethods, settings) {
1882
+ const has = (methodType) => {
1883
+ return usableMethods.some((method) => {
1884
+ return method.methodType === methodType;
1885
+ });
1886
+ };
1887
+ const zeroCost = [];
1888
+ if (has(ReadinessMethodType.Push)) {
1889
+ zeroCost.push(ReadinessMethodType.Push);
1890
+ }
1891
+ if (has(ReadinessMethodType.Email)) {
1892
+ zeroCost.push(ReadinessMethodType.Email);
1893
+ }
1894
+ if (zeroCost.length > 0) {
1895
+ return zeroCost;
1896
+ }
1897
+ if (settings.enableSmsNotifications && has(ReadinessMethodType.SMS)) {
1898
+ return [ReadinessMethodType.SMS];
1899
+ }
1900
+ if (settings.enableCallNotifications && has(ReadinessMethodType.Call)) {
1901
+ return [ReadinessMethodType.Call];
1902
+ }
1903
+ if (settings.enableWhatsAppNotifications &&
1904
+ has(ReadinessMethodType.WhatsApp)) {
1905
+ return [ReadinessMethodType.WhatsApp];
1906
+ }
1907
+ if (settings.enableTelegramNotifications &&
1908
+ has(ReadinessMethodType.Telegram)) {
1909
+ return [ReadinessMethodType.Telegram];
1910
+ }
1911
+ if (has(ReadinessMethodType.Webhook)) {
1912
+ return [ReadinessMethodType.Webhook];
1913
+ }
1914
+ return [];
1915
+ }
1916
+ static toSeverityRefs(severities) {
1917
+ const refs = [];
1918
+ for (const severity of severities) {
1919
+ if (!severity.id) {
1920
+ continue;
1921
+ }
1922
+ refs.push({
1923
+ id: severity.id,
1924
+ name: severity.name || "Unnamed Severity",
1925
+ });
1926
+ }
1927
+ return refs;
1928
+ }
1929
+ static distinctIds(ids) {
1930
+ const seen = new Set();
1931
+ const distinct = [];
1932
+ for (const id of ids) {
1933
+ if (!id) {
1934
+ continue;
1935
+ }
1936
+ const key = id.toString();
1937
+ if (seen.has(key)) {
1938
+ continue;
1939
+ }
1940
+ seen.add(key);
1941
+ distinct.push(id);
1942
+ }
1943
+ return distinct;
1944
+ }
1945
+ static distinctStrings(values) {
1946
+ const seen = new Set();
1947
+ const distinct = [];
1948
+ for (const value of values) {
1949
+ if (seen.has(value)) {
1950
+ continue;
1951
+ }
1952
+ seen.add(value);
1953
+ distinct.push(value);
1954
+ }
1955
+ return distinct;
1956
+ }
1957
+ }
1958
+ OnCallReadinessService.summaryCache = new InMemoryTTLCache(10000);
1959
+ OnCallReadinessService.userCache = new InMemoryTTLCache(10000);
1960
+ export default OnCallReadinessService;
1961
+ //# sourceMappingURL=OnCallReadinessService.js.map