@oneuptime/common 13.0.6 → 13.0.8

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 (1030) hide show
  1. package/Models/DatabaseModels/AIAgentOwnerTeam.ts +6 -0
  2. package/Models/DatabaseModels/AIAgentOwnerUser.ts +6 -0
  3. package/Models/DatabaseModels/Alert.ts +12 -0
  4. package/Models/DatabaseModels/AlertEpisodeOwnerTeam.ts +6 -1
  5. package/Models/DatabaseModels/AlertEpisodeOwnerUser.ts +6 -1
  6. package/Models/DatabaseModels/AlertOwnerTeam.ts +6 -1
  7. package/Models/DatabaseModels/AlertOwnerUser.ts +6 -1
  8. package/Models/DatabaseModels/CephClusterOwnerTeam.ts +6 -0
  9. package/Models/DatabaseModels/CephClusterOwnerUser.ts +6 -0
  10. package/Models/DatabaseModels/CloudResourceOwnerTeam.ts +6 -0
  11. package/Models/DatabaseModels/CloudResourceOwnerUser.ts +6 -0
  12. package/Models/DatabaseModels/DashboardOwnerTeam.ts +6 -1
  13. package/Models/DatabaseModels/DashboardOwnerUser.ts +6 -1
  14. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +8 -0
  15. package/Models/DatabaseModels/DockerHostOwnerTeam.ts +6 -0
  16. package/Models/DatabaseModels/DockerHostOwnerUser.ts +6 -0
  17. package/Models/DatabaseModels/DockerSwarmClusterOwnerTeam.ts +6 -0
  18. package/Models/DatabaseModels/DockerSwarmClusterOwnerUser.ts +6 -0
  19. package/Models/DatabaseModels/HostOwnerTeam.ts +6 -0
  20. package/Models/DatabaseModels/HostOwnerUser.ts +6 -0
  21. package/Models/DatabaseModels/Incident.ts +12 -0
  22. package/Models/DatabaseModels/IncidentEpisodeOwnerTeam.ts +6 -1
  23. package/Models/DatabaseModels/IncidentEpisodeOwnerUser.ts +6 -1
  24. package/Models/DatabaseModels/IncidentOwnerTeam.ts +6 -1
  25. package/Models/DatabaseModels/IncidentOwnerUser.ts +6 -1
  26. package/Models/DatabaseModels/IncidentTemplateOwnerTeam.ts +6 -0
  27. package/Models/DatabaseModels/IncidentTemplateOwnerUser.ts +6 -0
  28. package/Models/DatabaseModels/IncomingCallPolicyOwnerTeam.ts +6 -1
  29. package/Models/DatabaseModels/IncomingCallPolicyOwnerUser.ts +6 -1
  30. package/Models/DatabaseModels/IoTFleetOwnerTeam.ts +6 -0
  31. package/Models/DatabaseModels/IoTFleetOwnerUser.ts +6 -0
  32. package/Models/DatabaseModels/KubernetesClusterOwnerTeam.ts +6 -0
  33. package/Models/DatabaseModels/KubernetesClusterOwnerUser.ts +6 -0
  34. package/Models/DatabaseModels/Monitor.ts +2 -3
  35. package/Models/DatabaseModels/MonitorGroupOwnerTeam.ts +6 -0
  36. package/Models/DatabaseModels/MonitorGroupOwnerUser.ts +6 -0
  37. package/Models/DatabaseModels/MonitorOwnerTeam.ts +6 -1
  38. package/Models/DatabaseModels/MonitorOwnerUser.ts +6 -1
  39. package/Models/DatabaseModels/MonitorTemplate.ts +2 -3
  40. package/Models/DatabaseModels/NetworkDeviceOwnerTeam.ts +6 -0
  41. package/Models/DatabaseModels/NetworkDeviceOwnerUser.ts +6 -0
  42. package/Models/DatabaseModels/OnCallDutyPolicyOwnerTeam.ts +6 -1
  43. package/Models/DatabaseModels/OnCallDutyPolicyOwnerUser.ts +6 -1
  44. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  45. package/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerTeam.ts +6 -1
  46. package/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerUser.ts +6 -1
  47. package/Models/DatabaseModels/PodmanHostOwnerTeam.ts +6 -0
  48. package/Models/DatabaseModels/PodmanHostOwnerUser.ts +6 -0
  49. package/Models/DatabaseModels/ProbeOwnerTeam.ts +6 -0
  50. package/Models/DatabaseModels/ProbeOwnerUser.ts +6 -0
  51. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  52. package/Models/DatabaseModels/ProxmoxClusterOwnerTeam.ts +6 -0
  53. package/Models/DatabaseModels/ProxmoxClusterOwnerUser.ts +6 -0
  54. package/Models/DatabaseModels/RumApplicationOwnerTeam.ts +6 -0
  55. package/Models/DatabaseModels/RumApplicationOwnerUser.ts +6 -0
  56. package/Models/DatabaseModels/RunbookOwnerTeam.ts +6 -1
  57. package/Models/DatabaseModels/RunbookOwnerUser.ts +6 -1
  58. package/Models/DatabaseModels/RunnerOwnerTeam.ts +6 -1
  59. package/Models/DatabaseModels/RunnerOwnerUser.ts +6 -1
  60. package/Models/DatabaseModels/ScheduledMaintenanceOwnerTeam.ts +6 -1
  61. package/Models/DatabaseModels/ScheduledMaintenanceOwnerUser.ts +6 -1
  62. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerTeam.ts +8 -0
  63. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +8 -0
  64. package/Models/DatabaseModels/ServerlessFunctionOwnerTeam.ts +6 -0
  65. package/Models/DatabaseModels/ServerlessFunctionOwnerUser.ts +6 -0
  66. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.ts +6 -1
  67. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.ts +6 -1
  68. package/Models/DatabaseModels/ServiceOwnerTeam.ts +6 -0
  69. package/Models/DatabaseModels/ServiceOwnerUser.ts +6 -0
  70. package/Models/DatabaseModels/StatusPageOwnerTeam.ts +6 -1
  71. package/Models/DatabaseModels/StatusPageOwnerUser.ts +6 -1
  72. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  73. package/Models/DatabaseModels/VMwareVCenterOwnerTeam.ts +6 -0
  74. package/Models/DatabaseModels/VMwareVCenterOwnerUser.ts +6 -0
  75. package/Models/DatabaseModels/WorkflowOwnerTeam.ts +6 -1
  76. package/Models/DatabaseModels/WorkflowOwnerUser.ts +6 -1
  77. package/Scripts/benchmark-fanin-capacity.js +2 -2
  78. package/Server/API/AIAgentDataAPI.ts +1 -1
  79. package/Server/API/AIBillingAPI.ts +1 -0
  80. package/Server/API/AIChatAPI.ts +6 -4
  81. package/Server/API/AIInsightAPI.ts +1 -4
  82. package/Server/API/AIInvestigationAPI.ts +8 -4
  83. package/Server/API/AIReadinessAPI.ts +4 -5
  84. package/Server/API/AlertAPI.ts +1 -0
  85. package/Server/API/AutoRemediationAPI.ts +1 -4
  86. package/Server/API/BaseAPI.ts +13 -0
  87. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  88. package/Server/API/BillingAPI.ts +2 -0
  89. package/Server/API/BillingInvoiceAPI.ts +1 -0
  90. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  91. package/Server/API/CodeFixRunAPI.ts +1 -3
  92. package/Server/API/CommonAPI.ts +68 -4
  93. package/Server/API/DashboardAPI.ts +12 -12
  94. package/Server/API/GlobalConfigAPI.ts +17 -0
  95. package/Server/API/IncidentAPI.ts +2 -0
  96. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  97. package/Server/API/NotificationAPI.ts +1 -0
  98. package/Server/API/OnCallCalendarAPI.ts +4 -5
  99. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  100. package/Server/API/OnCallReadinessAPI.ts +4 -1
  101. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  102. package/Server/API/ProjectAPI.ts +1 -0
  103. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  104. package/Server/API/StatusPageAPI.ts +174 -32
  105. package/Server/API/TeamMemberAPI.ts +3 -0
  106. package/Server/API/UserCallAPI.ts +2 -0
  107. package/Server/API/UserEmailAPI.ts +2 -0
  108. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  109. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  110. package/Server/API/UserSlackAPI.ts +1 -0
  111. package/Server/API/UserSmsAPI.ts +2 -0
  112. package/Server/API/UserTotpAuthAPI.ts +1 -0
  113. package/Server/API/UserWebAuthnAPI.ts +2 -0
  114. package/Server/API/UserWebhookAPI.ts +1 -0
  115. package/Server/API/UserWhatsAppAPI.ts +2 -0
  116. package/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.ts +1 -1
  117. package/Server/Infrastructure/Postgres/SchemaMigrations/1793500000000-MakeOwnerRowsUnique.ts +1057 -0
  118. package/Server/Infrastructure/Postgres/SchemaMigrations/1793700000000-AddAIInvestigationDecision.ts +25 -0
  119. package/Server/Infrastructure/Postgres/SchemaMigrations/1793800000000-RemoveMonitorDescriptionLengthLimit.ts +31 -0
  120. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  121. package/Server/Infrastructure/Postgres/SchemaMigrations/Readme.md +6 -4
  122. package/Server/Middleware/IdentityRateLimit.ts +34 -1
  123. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  124. package/Server/Middleware/PublicDashboardRateLimit.ts +169 -39
  125. package/Server/Middleware/UserAuthorization.ts +79 -5
  126. package/Server/Services/AlertEpisodeService.ts +13 -34
  127. package/Server/Services/AlertGroupingEngineService.ts +7 -4
  128. package/Server/Services/AlertService.ts +13 -32
  129. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  130. package/Server/Services/CephClusterOwnerRuleEngineService.ts +18 -10
  131. package/Server/Services/CloudResourceOwnerRuleEngineService.ts +18 -10
  132. package/Server/Services/DashboardOwnerRuleEngineService.ts +18 -10
  133. package/Server/Services/DatabaseService.ts +346 -1
  134. package/Server/Services/DetectionRuleService.ts +5 -4
  135. package/Server/Services/DockerHostOwnerRuleEngineService.ts +18 -10
  136. package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +18 -10
  137. package/Server/Services/HostOwnerRuleEngineService.ts +18 -10
  138. package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +6 -4
  139. package/Server/Services/IncidentEpisodeService.ts +15 -75
  140. package/Server/Services/IncidentGroupingEngineService.ts +7 -4
  141. package/Server/Services/IncidentService.ts +13 -32
  142. package/Server/Services/IncidentTemplateService.ts +13 -36
  143. package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +18 -10
  144. package/Server/Services/IoTFleetOwnerRuleEngineService.ts +18 -10
  145. package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +18 -10
  146. package/Server/Services/MonitorService.ts +13 -32
  147. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +18 -10
  148. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  149. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  150. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  151. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  152. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  153. package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +18 -10
  154. package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +18 -10
  155. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  156. package/Server/Services/PodmanHostOwnerRuleEngineService.ts +18 -10
  157. package/Server/Services/ProjectService.ts +18 -0
  158. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +18 -10
  159. package/Server/Services/RumApplicationOwnerRuleEngineService.ts +18 -10
  160. package/Server/Services/RumSessionPinService.ts +274 -22
  161. package/Server/Services/RunbookOwnerRuleEngineService.ts +18 -10
  162. package/Server/Services/ScheduledMaintenanceService.ts +13 -34
  163. package/Server/Services/ScheduledMaintenanceTemplateService.ts +12 -34
  164. package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +18 -10
  165. package/Server/Services/ServiceOwnerRuleEngineService.ts +18 -10
  166. package/Server/Services/StatusPagePrivateUserService.ts +38 -29
  167. package/Server/Services/StatusPagePrivateUserSessionService.ts +49 -1
  168. package/Server/Services/StatusPageService.ts +59 -36
  169. package/Server/Services/TeamPermissionService.ts +120 -26
  170. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  171. package/Server/Services/UserSessionService.ts +6 -0
  172. package/Server/Services/VMwareVCenterOwnerRuleEngineService.ts +18 -10
  173. package/Server/Services/WorkflowOwnerRuleEngineService.ts +18 -10
  174. package/Server/Static/Brand/README.md +7 -7
  175. package/Server/Static/Vendor/README.md +7 -7
  176. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  177. package/Server/Types/Database/Permissions/Index.ts +75 -11
  178. package/Server/Types/Database/Permissions/OwnedScopePermission.ts +18 -9
  179. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +82 -0
  180. package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
  181. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  182. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  183. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +21 -8
  184. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +41 -4
  185. package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +1 -1
  186. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +1 -1
  187. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +13 -5
  188. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +53 -13
  189. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +51 -14
  190. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +292 -0
  191. package/Server/Utils/AI/SRE/InvestigationQueue.ts +25 -0
  192. package/Server/Utils/AI/Toolbox/Index.ts +43 -13
  193. package/Server/Utils/AI/Toolbox/ResourceTools.ts +952 -0
  194. package/Server/Utils/AI/Toolbox/RumTools.ts +424 -0
  195. package/Server/Utils/AI/Toolbox/ToolTypes.ts +10 -0
  196. package/Server/Utils/AI/Toolbox/TraceTools.ts +2 -1
  197. package/Server/Utils/Database/PostgresErrorTranslator.ts +14 -0
  198. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  199. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +137 -126
  200. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +4 -1
  201. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  202. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  203. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  204. package/Server/Utils/Realtime.ts +163 -45
  205. package/Server/Utils/Rules/OwnerRuleAssignment.ts +157 -6
  206. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  207. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  208. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  209. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  210. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  211. package/Server/Utils/Telemetry/TelemetryEntity.ts +205 -6
  212. package/SslCertificates/GlobalSign.crt +26 -0
  213. package/SslCertificates/Readme.md +3 -0
  214. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +451 -0
  215. package/Tests/App/Dashboard/AIInsightsChatPanel.test.tsx +881 -0
  216. package/Tests/App/Dashboard/AIInsightsPageContext.test.ts +458 -0
  217. package/Tests/App/Dashboard/AIInsightsResourceCitations.test.ts +124 -0
  218. package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +798 -0
  219. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  220. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  221. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  222. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  223. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  224. package/Tests/App/Dashboard/AskAiSuggestedPrompts.test.ts +7 -0
  225. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +104 -0
  226. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +156 -64
  227. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  228. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  229. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  230. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  231. package/Tests/App/Dashboard/DiscoveryScanStatusMessage.test.tsx +970 -0
  232. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +406 -12
  233. package/Tests/App/Dashboard/EmbeddedMetricEventScope.test.tsx +358 -0
  234. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  235. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  236. package/Tests/App/Dashboard/ErrorPatternEventScope.test.tsx +148 -0
  237. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  238. package/Tests/App/Dashboard/EventOverlayAdditionalResourceScope.test.ts +255 -0
  239. package/Tests/App/Dashboard/EventOverlayScope.test.ts +845 -0
  240. package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +903 -0
  241. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +3 -3
  242. package/Tests/App/Dashboard/InvestigationDrawer.test.tsx +21 -0
  243. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +761 -0
  244. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +7 -4
  245. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +15 -7
  246. package/Tests/App/Dashboard/MetricsViewerEventScopeNavigation.test.tsx +264 -0
  247. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +342 -0
  248. package/Tests/App/Dashboard/MonitorTestForm.test.tsx +612 -0
  249. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +301 -0
  250. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  251. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  252. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  253. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  254. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  255. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  256. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  257. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
  258. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  259. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
  260. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
  261. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  262. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  263. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  264. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  265. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
  266. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +74 -2
  267. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  268. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  269. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  270. package/Tests/App/Dashboard/TextClampLayoutHarness.ts +224 -0
  271. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  272. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  273. package/Tests/Models/InventoryItemNaming.test.ts +54 -0
  274. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  275. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +299 -5
  276. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  277. package/Tests/Server/API/AIInvestigationAPI.test.ts +14 -0
  278. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +308 -0
  279. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  280. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  281. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  282. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  283. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  284. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  285. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +133 -0
  286. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  287. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  288. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  289. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  290. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  291. package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
  292. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  293. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  294. package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
  295. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  296. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  297. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  298. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  299. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  300. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  301. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +484 -0
  302. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  303. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  304. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  305. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  306. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  307. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  308. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  309. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  310. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  311. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  312. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  313. package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +1 -1
  314. package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +1 -1
  315. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +38 -16
  316. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +106 -31
  317. package/Tests/Server/Infrastructure/ExampleDockerfiles.test.ts +46 -12
  318. package/Tests/Server/Infrastructure/Postgres/MonitorDescriptionMigration.test.ts +422 -0
  319. package/Tests/Server/Infrastructure/TelemetryExporterDeploymentConfig.test.ts +1 -1
  320. package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +25 -4
  321. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  322. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +72 -0
  323. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  324. package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +12 -12
  325. package/Tests/Server/Middleware/PublicDashboardRateLimitStatusPage.test.ts +800 -0
  326. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  327. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  328. package/Tests/Server/Services/AIInvestigationDecisionPostgres.test.ts +346 -0
  329. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +471 -0
  330. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  331. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  332. package/Tests/Server/Services/DatabaseServiceAutoOwnerOnCreate.test.ts +250 -0
  333. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  334. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  335. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  336. package/Tests/Server/Services/DatabaseServiceUniqueColumnsTogether.test.ts +622 -0
  337. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  338. package/Tests/Server/Services/MonitorDescriptionValidation.test.ts +211 -0
  339. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  340. package/Tests/Server/Services/MonitorTemplateDescriptionPropagation.test.ts +269 -0
  341. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  342. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  343. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +504 -0
  344. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  345. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  346. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  347. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  348. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  349. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +43 -0
  350. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  351. package/Tests/Server/Services/SessionRetention.test.ts +334 -0
  352. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +172 -0
  353. package/Tests/Server/Services/StatusPagePrivateUserReadAccess.test.ts +178 -0
  354. package/Tests/Server/Services/StatusPagePrivateUserReadAccessPostgres.test.ts +311 -0
  355. package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +216 -1
  356. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1 -1
  357. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  358. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  359. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  360. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  361. package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1 -1
  362. package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +626 -0
  363. package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +668 -0
  364. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  365. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  366. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  367. package/Tests/Server/Types/Database/Permissions/SelectRejectionMessageContract.test.ts +1 -0
  368. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  369. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1 -1
  370. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +16 -9
  371. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  372. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  373. package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +137 -1
  374. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +697 -0
  375. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +105 -0
  376. package/Tests/Server/Utils/AI/RemediationHandoff.test.ts +16 -5
  377. package/Tests/Server/Utils/AI/ResourceTools.test.ts +1304 -0
  378. package/Tests/Server/Utils/AI/RumTools.test.ts +769 -0
  379. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  380. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  381. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  382. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  383. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  384. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +63 -0
  385. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  386. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +11 -9
  387. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +62 -45
  388. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +25 -24
  389. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaHumanMessages.test.ts +513 -0
  390. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +2 -2
  391. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +1 -1
  392. package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +9 -9
  393. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +7 -5
  394. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +2 -2
  395. package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +4 -3
  396. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +2 -2
  397. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  398. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +3 -2
  399. package/Tests/Server/Utils/Monitor/MonitorCriteriaHumanMessages.test.ts +529 -0
  400. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  401. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +412 -4
  402. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  403. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingClickhouseIntegration.test.ts +1 -1
  404. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  405. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  406. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  407. package/Tests/Server/Utils/TailwindBuildCopy.test.ts +14 -2
  408. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  409. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +235 -0
  410. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  411. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +385 -2
  412. package/Tests/Server/Utils/VendorAssets.test.ts +17 -3
  413. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  414. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  415. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  416. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  417. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  418. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  419. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  420. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  421. package/Tests/Types/AI/AIChatPageContext.test.ts +0 -0
  422. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  423. package/Tests/Types/AI/AIResourceContext.test.ts +312 -0
  424. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  425. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  426. package/Tests/Types/Database/UniqueColumnsTogether.test.ts +100 -0
  427. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  428. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +4 -4
  429. package/Tests/Types/Monitor/MonitorStepRetryPersistence.test.ts +125 -0
  430. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  431. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  432. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  433. package/Tests/Types/WebsiteRequest.test.ts +44 -0
  434. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  435. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  436. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  437. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
  438. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  439. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  440. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  441. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  442. package/Tests/UI/Components/Forms/MonitorDescriptionForm.test.tsx +410 -0
  443. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  444. package/Tests/UI/Components/ModelTable/BaseModelTableReadPermissions.test.tsx +439 -0
  445. package/Tests/UI/Components/NavBarMenuSearch.test.tsx +372 -0
  446. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  447. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  448. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  449. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  450. package/Tests/UI/EsbuildConfig.test.ts +1 -1
  451. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  452. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  453. package/Tests/UI/Monitor/MonitorStepRetrySettings.test.tsx +616 -0
  454. package/Tests/UI/ReactRouterSingletonBuild.test.ts +4 -2
  455. package/Tests/UI/Rum/ReplayEngine.test.ts +179 -0
  456. package/Tests/UI/Rum/ReplayHeader.test.tsx +23 -0
  457. package/Tests/UI/Rum/ReplayScrubber.test.tsx +88 -0
  458. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +83 -0
  459. package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +33 -0
  460. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  461. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  462. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  463. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  464. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  465. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  466. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  467. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  468. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  469. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  470. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  471. package/Tests/Utils/API.test.ts +96 -0
  472. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  473. package/Tests/Utils/HTTPResponseBodyReader.test.ts +188 -1
  474. package/Tests/Utils/Metrics/MetricEventScopeUrl.test.ts +101 -0
  475. package/Tests/Utils/Monitor/MonitorTestAvailabilityUtil.test.ts +256 -0
  476. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  477. package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +105 -0
  478. package/Types/AI/AIChatPageContext.ts +87 -2
  479. package/Types/AI/AIChatTypes.ts +4 -0
  480. package/Types/AI/AIResourceContext.ts +198 -0
  481. package/Types/AI/InvestigationNotStartedReason.ts +30 -0
  482. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  483. package/Types/Database/UniqueColumnsTogether.ts +55 -0
  484. package/Types/Metrics/MetricQueryConfigData.ts +8 -0
  485. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  486. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  487. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  488. package/Types/Realtime/EventName.ts +11 -0
  489. package/Types/Rum/WebVitals.ts +71 -0
  490. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  491. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  492. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  493. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  494. package/Types/WebsiteRequest.ts +2 -0
  495. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  496. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  497. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  498. package/UI/Components/EditionLabel/EditionLabel.tsx +19 -5
  499. package/UI/Components/EventItem/EventItem.tsx +12 -1
  500. package/UI/Components/Forms/BasicForm.tsx +151 -126
  501. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  502. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  503. package/UI/Components/Forms/FormSummary.tsx +7 -0
  504. package/UI/Components/Forms/Types/Field.ts +10 -0
  505. package/UI/Components/KeyboardShortcut/Screenshots/README.md +1 -1
  506. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  507. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  508. package/UI/Components/ModelTable/BaseModelTable.tsx +42 -5
  509. package/UI/Components/ModelTable/TableView.tsx +1 -1
  510. package/UI/Components/Navbar/NavBar.tsx +2 -0
  511. package/UI/Components/Navbar/NavBarMenuModal.tsx +6 -2
  512. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  513. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  514. package/UI/Utils/API/API.ts +173 -12
  515. package/UI/Utils/API/RequestOptions.ts +11 -0
  516. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  517. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  518. package/UI/Utils/Navigation.ts +9 -0
  519. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  520. package/UI/Utils/Realtime.ts +176 -30
  521. package/UI/esbuild-config.js +4 -4
  522. package/Utils/API.ts +24 -6
  523. package/Utils/Dashboard/HtmlWidgetDocument.ts +1 -1
  524. package/Utils/HTTPResponseBodyReader.ts +64 -8
  525. package/Utils/Metrics/MetricExplorerUrl.ts +19 -0
  526. package/Utils/Monitor/MonitorTestAvailabilityUtil.ts +92 -0
  527. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  528. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  529. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  530. package/Utils/Telemetry/HostIpAddresses.ts +46 -2
  531. package/build/dist/Models/DatabaseModels/AIAgentOwnerTeam.js +4 -1
  532. package/build/dist/Models/DatabaseModels/AIAgentOwnerTeam.js.map +1 -1
  533. package/build/dist/Models/DatabaseModels/AIAgentOwnerUser.js +4 -1
  534. package/build/dist/Models/DatabaseModels/AIAgentOwnerUser.js.map +1 -1
  535. package/build/dist/Models/DatabaseModels/Alert.js +12 -0
  536. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  537. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerTeam.js +3 -1
  538. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerTeam.js.map +1 -1
  539. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerUser.js +3 -1
  540. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerUser.js.map +1 -1
  541. package/build/dist/Models/DatabaseModels/AlertOwnerTeam.js +3 -1
  542. package/build/dist/Models/DatabaseModels/AlertOwnerTeam.js.map +1 -1
  543. package/build/dist/Models/DatabaseModels/AlertOwnerUser.js +3 -1
  544. package/build/dist/Models/DatabaseModels/AlertOwnerUser.js.map +1 -1
  545. package/build/dist/Models/DatabaseModels/CephClusterOwnerTeam.js +4 -1
  546. package/build/dist/Models/DatabaseModels/CephClusterOwnerTeam.js.map +1 -1
  547. package/build/dist/Models/DatabaseModels/CephClusterOwnerUser.js +4 -1
  548. package/build/dist/Models/DatabaseModels/CephClusterOwnerUser.js.map +1 -1
  549. package/build/dist/Models/DatabaseModels/CloudResourceOwnerTeam.js +4 -1
  550. package/build/dist/Models/DatabaseModels/CloudResourceOwnerTeam.js.map +1 -1
  551. package/build/dist/Models/DatabaseModels/CloudResourceOwnerUser.js +4 -1
  552. package/build/dist/Models/DatabaseModels/CloudResourceOwnerUser.js.map +1 -1
  553. package/build/dist/Models/DatabaseModels/DashboardOwnerTeam.js +3 -1
  554. package/build/dist/Models/DatabaseModels/DashboardOwnerTeam.js.map +1 -1
  555. package/build/dist/Models/DatabaseModels/DashboardOwnerUser.js +3 -1
  556. package/build/dist/Models/DatabaseModels/DashboardOwnerUser.js.map +1 -1
  557. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +3 -0
  558. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  559. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js +4 -1
  560. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js.map +1 -1
  561. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js +4 -1
  562. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js.map +1 -1
  563. package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerTeam.js +4 -1
  564. package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerTeam.js.map +1 -1
  565. package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerUser.js +4 -1
  566. package/build/dist/Models/DatabaseModels/DockerSwarmClusterOwnerUser.js.map +1 -1
  567. package/build/dist/Models/DatabaseModels/HostOwnerTeam.js +4 -1
  568. package/build/dist/Models/DatabaseModels/HostOwnerTeam.js.map +1 -1
  569. package/build/dist/Models/DatabaseModels/HostOwnerUser.js +4 -1
  570. package/build/dist/Models/DatabaseModels/HostOwnerUser.js.map +1 -1
  571. package/build/dist/Models/DatabaseModels/Incident.js +12 -0
  572. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  573. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerTeam.js +3 -1
  574. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerTeam.js.map +1 -1
  575. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerUser.js +3 -1
  576. package/build/dist/Models/DatabaseModels/IncidentEpisodeOwnerUser.js.map +1 -1
  577. package/build/dist/Models/DatabaseModels/IncidentOwnerTeam.js +3 -1
  578. package/build/dist/Models/DatabaseModels/IncidentOwnerTeam.js.map +1 -1
  579. package/build/dist/Models/DatabaseModels/IncidentOwnerUser.js +3 -1
  580. package/build/dist/Models/DatabaseModels/IncidentOwnerUser.js.map +1 -1
  581. package/build/dist/Models/DatabaseModels/IncidentTemplateOwnerTeam.js +4 -1
  582. package/build/dist/Models/DatabaseModels/IncidentTemplateOwnerTeam.js.map +1 -1
  583. package/build/dist/Models/DatabaseModels/IncidentTemplateOwnerUser.js +4 -1
  584. package/build/dist/Models/DatabaseModels/IncidentTemplateOwnerUser.js.map +1 -1
  585. package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerTeam.js +3 -1
  586. package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerTeam.js.map +1 -1
  587. package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerUser.js +3 -1
  588. package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerUser.js.map +1 -1
  589. package/build/dist/Models/DatabaseModels/IoTFleetOwnerTeam.js +4 -1
  590. package/build/dist/Models/DatabaseModels/IoTFleetOwnerTeam.js.map +1 -1
  591. package/build/dist/Models/DatabaseModels/IoTFleetOwnerUser.js +4 -1
  592. package/build/dist/Models/DatabaseModels/IoTFleetOwnerUser.js.map +1 -1
  593. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js +4 -1
  594. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js.map +1 -1
  595. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js +4 -1
  596. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js.map +1 -1
  597. package/build/dist/Models/DatabaseModels/Monitor.js +2 -3
  598. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  599. package/build/dist/Models/DatabaseModels/MonitorGroupOwnerTeam.js +4 -1
  600. package/build/dist/Models/DatabaseModels/MonitorGroupOwnerTeam.js.map +1 -1
  601. package/build/dist/Models/DatabaseModels/MonitorGroupOwnerUser.js +4 -1
  602. package/build/dist/Models/DatabaseModels/MonitorGroupOwnerUser.js.map +1 -1
  603. package/build/dist/Models/DatabaseModels/MonitorOwnerTeam.js +3 -1
  604. package/build/dist/Models/DatabaseModels/MonitorOwnerTeam.js.map +1 -1
  605. package/build/dist/Models/DatabaseModels/MonitorOwnerUser.js +3 -1
  606. package/build/dist/Models/DatabaseModels/MonitorOwnerUser.js.map +1 -1
  607. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -3
  608. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  609. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerTeam.js +4 -1
  610. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerTeam.js.map +1 -1
  611. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerUser.js +4 -1
  612. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerUser.js.map +1 -1
  613. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerTeam.js +3 -1
  614. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerTeam.js.map +1 -1
  615. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerUser.js +3 -1
  616. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerUser.js.map +1 -1
  617. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  618. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  619. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerTeam.js +3 -1
  620. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerTeam.js.map +1 -1
  621. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerUser.js +3 -1
  622. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerUser.js.map +1 -1
  623. package/build/dist/Models/DatabaseModels/PodmanHostOwnerTeam.js +4 -1
  624. package/build/dist/Models/DatabaseModels/PodmanHostOwnerTeam.js.map +1 -1
  625. package/build/dist/Models/DatabaseModels/PodmanHostOwnerUser.js +4 -1
  626. package/build/dist/Models/DatabaseModels/PodmanHostOwnerUser.js.map +1 -1
  627. package/build/dist/Models/DatabaseModels/ProbeOwnerTeam.js +4 -1
  628. package/build/dist/Models/DatabaseModels/ProbeOwnerTeam.js.map +1 -1
  629. package/build/dist/Models/DatabaseModels/ProbeOwnerUser.js +4 -1
  630. package/build/dist/Models/DatabaseModels/ProbeOwnerUser.js.map +1 -1
  631. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  632. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  633. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerTeam.js +4 -1
  634. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerTeam.js.map +1 -1
  635. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerUser.js +4 -1
  636. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerUser.js.map +1 -1
  637. package/build/dist/Models/DatabaseModels/RumApplicationOwnerTeam.js +4 -1
  638. package/build/dist/Models/DatabaseModels/RumApplicationOwnerTeam.js.map +1 -1
  639. package/build/dist/Models/DatabaseModels/RumApplicationOwnerUser.js +4 -1
  640. package/build/dist/Models/DatabaseModels/RumApplicationOwnerUser.js.map +1 -1
  641. package/build/dist/Models/DatabaseModels/RunbookOwnerTeam.js +3 -1
  642. package/build/dist/Models/DatabaseModels/RunbookOwnerTeam.js.map +1 -1
  643. package/build/dist/Models/DatabaseModels/RunbookOwnerUser.js +3 -1
  644. package/build/dist/Models/DatabaseModels/RunbookOwnerUser.js.map +1 -1
  645. package/build/dist/Models/DatabaseModels/RunnerOwnerTeam.js +3 -1
  646. package/build/dist/Models/DatabaseModels/RunnerOwnerTeam.js.map +1 -1
  647. package/build/dist/Models/DatabaseModels/RunnerOwnerUser.js +3 -1
  648. package/build/dist/Models/DatabaseModels/RunnerOwnerUser.js.map +1 -1
  649. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerTeam.js +3 -1
  650. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerTeam.js.map +1 -1
  651. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerUser.js +3 -1
  652. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceOwnerUser.js.map +1 -1
  653. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerTeam.js +6 -1
  654. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerTeam.js.map +1 -1
  655. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +6 -1
  656. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js.map +1 -1
  657. package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerTeam.js +4 -1
  658. package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerTeam.js.map +1 -1
  659. package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerUser.js +4 -1
  660. package/build/dist/Models/DatabaseModels/ServerlessFunctionOwnerUser.js.map +1 -1
  661. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.js +3 -1
  662. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.js.map +1 -1
  663. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.js +3 -1
  664. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.js.map +1 -1
  665. package/build/dist/Models/DatabaseModels/ServiceOwnerTeam.js +4 -1
  666. package/build/dist/Models/DatabaseModels/ServiceOwnerTeam.js.map +1 -1
  667. package/build/dist/Models/DatabaseModels/ServiceOwnerUser.js +4 -1
  668. package/build/dist/Models/DatabaseModels/ServiceOwnerUser.js.map +1 -1
  669. package/build/dist/Models/DatabaseModels/StatusPageOwnerTeam.js +3 -1
  670. package/build/dist/Models/DatabaseModels/StatusPageOwnerTeam.js.map +1 -1
  671. package/build/dist/Models/DatabaseModels/StatusPageOwnerUser.js +3 -1
  672. package/build/dist/Models/DatabaseModels/StatusPageOwnerUser.js.map +1 -1
  673. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  674. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  675. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerTeam.js +4 -1
  676. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerTeam.js.map +1 -1
  677. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerUser.js +4 -1
  678. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerUser.js.map +1 -1
  679. package/build/dist/Models/DatabaseModels/WorkflowOwnerTeam.js +3 -1
  680. package/build/dist/Models/DatabaseModels/WorkflowOwnerTeam.js.map +1 -1
  681. package/build/dist/Models/DatabaseModels/WorkflowOwnerUser.js +3 -1
  682. package/build/dist/Models/DatabaseModels/WorkflowOwnerUser.js.map +1 -1
  683. package/build/dist/Server/API/AIAgentDataAPI.js +1 -1
  684. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  685. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  686. package/build/dist/Server/API/AIChatAPI.js +3 -1
  687. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  688. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  689. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  690. package/build/dist/Server/API/AIInvestigationAPI.js +4 -4
  691. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  692. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  693. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  694. package/build/dist/Server/API/AlertAPI.js +1 -1
  695. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  696. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  697. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  698. package/build/dist/Server/API/BaseAPI.js +12 -0
  699. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  700. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  701. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  702. package/build/dist/Server/API/BillingAPI.js +2 -2
  703. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  704. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  705. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  706. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  707. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  708. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  709. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  710. package/build/dist/Server/API/CommonAPI.js +50 -5
  711. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  712. package/build/dist/Server/API/DashboardAPI.js +12 -12
  713. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  714. package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
  715. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  716. package/build/dist/Server/API/IncidentAPI.js +2 -2
  717. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  718. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  719. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  720. package/build/dist/Server/API/NotificationAPI.js +1 -1
  721. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  722. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  723. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  724. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  725. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  726. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  727. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  728. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  729. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  730. package/build/dist/Server/API/ProjectAPI.js +1 -1
  731. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  732. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  733. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  734. package/build/dist/Server/API/StatusPageAPI.js +133 -33
  735. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  736. package/build/dist/Server/API/TeamMemberAPI.js +3 -3
  737. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  738. package/build/dist/Server/API/UserCallAPI.js +2 -2
  739. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  740. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  741. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  742. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  743. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  744. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  745. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  746. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  747. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  748. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  749. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  750. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  751. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  752. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  753. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  754. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  755. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  756. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  757. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  758. package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js +1 -1
  759. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793500000000-MakeOwnerRowsUnique.js +602 -0
  760. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793500000000-MakeOwnerRowsUnique.js.map +1 -0
  761. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793700000000-AddAIInvestigationDecision.js +14 -0
  762. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793700000000-AddAIInvestigationDecision.js.map +1 -0
  763. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793800000000-RemoveMonitorDescriptionLengthLimit.js +20 -0
  764. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793800000000-RemoveMonitorDescriptionLengthLimit.js.map +1 -0
  765. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  766. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  767. package/build/dist/Server/Middleware/IdentityRateLimit.js +22 -1
  768. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  769. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  770. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  771. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +117 -36
  772. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -1
  773. package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
  774. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  775. package/build/dist/Server/Services/AlertEpisodeService.js +13 -31
  776. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  777. package/build/dist/Server/Services/AlertGroupingEngineService.js +7 -4
  778. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  779. package/build/dist/Server/Services/AlertService.js +13 -31
  780. package/build/dist/Server/Services/AlertService.js.map +1 -1
  781. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  782. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  783. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +12 -8
  784. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
  785. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +12 -8
  786. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
  787. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +12 -8
  788. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
  789. package/build/dist/Server/Services/DatabaseService.js +242 -1
  790. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  791. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  792. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  793. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +12 -8
  794. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
  795. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +12 -8
  796. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
  797. package/build/dist/Server/Services/HostOwnerRuleEngineService.js +12 -8
  798. package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
  799. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +6 -4
  800. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
  801. package/build/dist/Server/Services/IncidentEpisodeService.js +15 -68
  802. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  803. package/build/dist/Server/Services/IncidentGroupingEngineService.js +7 -4
  804. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  805. package/build/dist/Server/Services/IncidentService.js +13 -31
  806. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  807. package/build/dist/Server/Services/IncidentTemplateService.js +13 -31
  808. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  809. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +12 -8
  810. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
  811. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +12 -8
  812. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
  813. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +12 -8
  814. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
  815. package/build/dist/Server/Services/MonitorService.js +13 -31
  816. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  817. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +12 -8
  818. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  819. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  820. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  821. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  822. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  823. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  824. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  825. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  826. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  827. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  828. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  829. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +12 -8
  830. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
  831. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +12 -8
  832. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
  833. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  834. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  835. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +12 -8
  836. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
  837. package/build/dist/Server/Services/ProjectService.js +17 -0
  838. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  839. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +12 -8
  840. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
  841. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +12 -8
  842. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
  843. package/build/dist/Server/Services/RumSessionPinService.js +180 -16
  844. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  845. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +12 -8
  846. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
  847. package/build/dist/Server/Services/ScheduledMaintenanceService.js +13 -31
  848. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  849. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +12 -29
  850. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  851. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +12 -8
  852. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
  853. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +12 -8
  854. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
  855. package/build/dist/Server/Services/StatusPagePrivateUserService.js +33 -25
  856. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  857. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +36 -1
  858. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
  859. package/build/dist/Server/Services/StatusPageService.js +50 -35
  860. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  861. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  862. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  863. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  864. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  865. package/build/dist/Server/Services/UserSessionService.js +5 -0
  866. package/build/dist/Server/Services/UserSessionService.js.map +1 -1
  867. package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js +12 -8
  868. package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js.map +1 -1
  869. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +12 -8
  870. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
  871. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  872. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  873. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  874. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  875. package/build/dist/Server/Types/Database/Permissions/OwnedScopePermission.js +15 -10
  876. package/build/dist/Server/Types/Database/Permissions/OwnedScopePermission.js.map +1 -1
  877. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +82 -0
  878. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  879. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
  880. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  881. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  882. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  883. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  884. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  885. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +12 -3
  886. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  887. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +32 -4
  888. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  889. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +1 -1
  890. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +1 -1
  891. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +8 -5
  892. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  893. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +33 -13
  894. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  895. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +31 -10
  896. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  897. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +205 -0
  898. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -0
  899. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +13 -0
  900. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  901. package/build/dist/Server/Utils/AI/Toolbox/Index.js +30 -6
  902. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  903. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +721 -0
  904. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -0
  905. package/build/dist/Server/Utils/AI/Toolbox/RumTools.js +330 -0
  906. package/build/dist/Server/Utils/AI/Toolbox/RumTools.js.map +1 -0
  907. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js.map +1 -1
  908. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js +1 -1
  909. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js.map +1 -1
  910. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +11 -0
  911. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
  912. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  913. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  914. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +106 -101
  915. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  916. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +4 -2
  917. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  918. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  919. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  920. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  921. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  922. package/build/dist/Server/Utils/Realtime.js +126 -37
  923. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  924. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +94 -0
  925. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  926. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  927. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  928. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  929. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  930. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  931. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  932. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  933. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  934. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  935. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  936. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +171 -6
  937. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  938. package/build/dist/Types/AI/AIChatPageContext.js +68 -2
  939. package/build/dist/Types/AI/AIChatPageContext.js.map +1 -1
  940. package/build/dist/Types/AI/AIChatTypes.js +4 -0
  941. package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
  942. package/build/dist/Types/AI/AIResourceContext.js +162 -0
  943. package/build/dist/Types/AI/AIResourceContext.js.map +1 -0
  944. package/build/dist/Types/AI/InvestigationNotStartedReason.js +2 -0
  945. package/build/dist/Types/AI/InvestigationNotStartedReason.js.map +1 -0
  946. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  947. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  948. package/build/dist/Types/Database/UniqueColumnsTogether.js +19 -0
  949. package/build/dist/Types/Database/UniqueColumnsTogether.js.map +1 -0
  950. package/build/dist/Types/Metrics/MetricQueryConfigData.js.map +1 -1
  951. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  952. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  953. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  954. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  955. package/build/dist/Types/Realtime/EventName.js +10 -0
  956. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  957. package/build/dist/Types/Rum/WebVitals.js +64 -0
  958. package/build/dist/Types/Rum/WebVitals.js.map +1 -0
  959. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  960. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  961. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  962. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  963. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  964. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  965. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  966. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  967. package/build/dist/Types/WebsiteRequest.js +2 -0
  968. package/build/dist/Types/WebsiteRequest.js.map +1 -1
  969. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  970. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  971. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  972. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  973. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  974. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  975. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +18 -5
  976. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  977. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  978. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  979. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  980. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  981. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  982. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  983. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  984. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  985. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  986. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  987. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  988. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  989. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  990. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  991. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +21 -1
  992. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  993. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  994. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  995. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  996. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +4 -2
  997. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  998. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  999. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1000. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  1001. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  1002. package/build/dist/UI/Utils/API/API.js +143 -19
  1003. package/build/dist/UI/Utils/API/API.js.map +1 -1
  1004. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  1005. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  1006. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  1007. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  1008. package/build/dist/UI/Utils/Navigation.js +8 -0
  1009. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  1010. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  1011. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  1012. package/build/dist/UI/Utils/Realtime.js +120 -15
  1013. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  1014. package/build/dist/Utils/API.js +21 -9
  1015. package/build/dist/Utils/API.js.map +1 -1
  1016. package/build/dist/Utils/HTTPResponseBodyReader.js +50 -6
  1017. package/build/dist/Utils/HTTPResponseBodyReader.js.map +1 -1
  1018. package/build/dist/Utils/Metrics/MetricExplorerUrl.js +14 -3
  1019. package/build/dist/Utils/Metrics/MetricExplorerUrl.js.map +1 -1
  1020. package/build/dist/Utils/Monitor/MonitorTestAvailabilityUtil.js +68 -0
  1021. package/build/dist/Utils/Monitor/MonitorTestAvailabilityUtil.js.map +1 -0
  1022. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  1023. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  1024. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  1025. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  1026. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  1027. package/build/dist/Utils/Telemetry/HostIpAddresses.js +42 -3
  1028. package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -1
  1029. package/package.json +5 -5
  1030. package/test-setup.sh +2 -2
@@ -0,0 +1,1580 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ waitFor,
17
+ within,
18
+ } from "@testing-library/react";
19
+ import React, { ReactElement } from "react";
20
+ import { MemoryRouter } from "react-router-dom";
21
+ import getJestMockFunction, { MockFunction } from "../../MockType";
22
+
23
+ /*
24
+ * The schedule timeline page as a reader uses it: what loads, what each row
25
+ * says about right now, how the week / month / today controls move the
26
+ * window the server is asked for, what every filter and toggle does to the
27
+ * rows, and the empty, error and "answer arrived late" states.
28
+ *
29
+ * The API is stubbed at API.get and answers from a table keyed on the
30
+ * requested window, so each test states exactly what the server would send.
31
+ */
32
+
33
+ const getMock: MockFunction = getJestMockFunction();
34
+
35
+ jest.mock("../../../UI/Utils/API/API", () => {
36
+ return {
37
+ __esModule: true,
38
+ default: {
39
+ get: (...args: Array<any>) => {
40
+ return getMock(...args);
41
+ },
42
+ getFriendlyMessage: (error: unknown): string => {
43
+ if (error && typeof error === "object" && "message" in error) {
44
+ return String((error as { message: unknown }).message);
45
+ }
46
+
47
+ return "Something went wrong";
48
+ },
49
+ },
50
+ };
51
+ });
52
+
53
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
54
+ return {
55
+ __esModule: true,
56
+ default: {
57
+ getCommonHeaders: (): Record<string, string> => {
58
+ return {};
59
+ },
60
+ },
61
+ };
62
+ });
63
+
64
+ jest.mock("react-i18next", () => {
65
+ return {
66
+ useTranslation: () => {
67
+ return {
68
+ t: (key: string, options?: { defaultValue?: string }): string => {
69
+ return options?.defaultValue ?? key;
70
+ },
71
+ };
72
+ },
73
+ };
74
+ });
75
+
76
+ /*
77
+ * The real picker opens a modal with a searchable dropdown; the page only
78
+ * cares about the zone it hands back, so each test clicks straight to one.
79
+ */
80
+ jest.mock(
81
+ "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallScheduleLayer/TimezoneSelectButton",
82
+ () => {
83
+ return {
84
+ __esModule: true,
85
+ default: (props: {
86
+ value?: string;
87
+ onChange: (value: string | undefined) => void;
88
+ }) => {
89
+ return (
90
+ <span data-testid="timezone-picker" data-value={props.value}>
91
+ {["Asia/Tokyo", "America/New_York", "UTC"].map((zone: string) => {
92
+ return (
93
+ <button
94
+ key={zone}
95
+ type="button"
96
+ data-testid={`timezone-to-${zone}`}
97
+ onClick={() => {
98
+ props.onChange(zone);
99
+ }}
100
+ >
101
+ {zone}
102
+ </button>
103
+ );
104
+ })}
105
+ </span>
106
+ );
107
+ },
108
+ };
109
+ },
110
+ );
111
+
112
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
113
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
114
+ import URL from "../../../Types/API/URL";
115
+ import OneUptimeDate from "../../../Types/Date";
116
+ import { JSONObject } from "../../../Types/JSON";
117
+ import ObjectID from "../../../Types/ObjectID";
118
+ import {
119
+ ScheduleTimelineResponse,
120
+ ScheduleTimelineScheduleJson,
121
+ } from "../../../Types/OnCallDutyPolicy/ScheduleTimeline";
122
+ import ScheduleTimeline, {
123
+ GROUP_BY_TEAM_STORAGE_KEY,
124
+ VIEW_MODE_STORAGE_KEY,
125
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/ScheduleTimeline/ScheduleTimeline";
126
+ import TimelineRow from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/ScheduleTimeline/TimelineRow";
127
+ import TimelineGrid from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/ScheduleTimeline/TimelineGrid";
128
+ import TimelineModel, {
129
+ TimelineData,
130
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/ScheduleTimeline/TimelineModel";
131
+ import ScheduleTimelineLayout, {
132
+ TimelineRange,
133
+ TimelineViewMode,
134
+ } from "../../../Types/OnCallDutyPolicy/ScheduleTimelineLayout";
135
+
136
+ // Thursday. The week on screen is Mon 14 - Sun 20 September 2026 (UTC).
137
+ const NOW: Date = new Date("2026-09-17T12:00:00.000Z");
138
+ const WEEK_FROM: string = "2026-09-14T00:00:00.000Z";
139
+ const WEEK_TO: string = "2026-09-21T00:00:00.000Z";
140
+
141
+ const PAYMENTS: ScheduleTimelineScheduleJson = {
142
+ scheduleId: "s-pay",
143
+ scheduleName: "Payments primary",
144
+ scheduleTimezone: "Europe/Stockholm",
145
+ ownerTeamIds: ["t-pay"],
146
+ isCurrentUserOnRoster: false,
147
+ truncated: false,
148
+ shifts: [
149
+ {
150
+ shiftKey: "pay-1",
151
+ userId: "u-alice",
152
+ userName: "Alice Andersson",
153
+ start: "2026-09-10T09:00:00.000Z",
154
+ end: "2026-09-17T09:00:00.000Z",
155
+ layerId: "l-1",
156
+ layerName: "Weekly",
157
+ override: null,
158
+ },
159
+ {
160
+ shiftKey: "pay-2",
161
+ userId: "u-bob",
162
+ userName: "Bob Berg",
163
+ start: "2026-09-17T09:00:00.000Z",
164
+ end: "2026-09-24T09:00:00.000Z",
165
+ layerId: "l-1",
166
+ layerName: "Weekly",
167
+ override: null,
168
+ },
169
+ ],
170
+ };
171
+
172
+ // Uncovered from Wednesday 00:00 to Friday 00:00, so nobody is on call now.
173
+ const SRE: ScheduleTimelineScheduleJson = {
174
+ scheduleId: "s-sre",
175
+ scheduleName: "SRE primary",
176
+ scheduleTimezone: null,
177
+ ownerTeamIds: ["t-sre", "t-pay"],
178
+ isCurrentUserOnRoster: true,
179
+ truncated: false,
180
+ shifts: [
181
+ {
182
+ shiftKey: "sre-1",
183
+ userId: "u-carol",
184
+ userName: "Carol Chen",
185
+ start: "2026-09-14T00:00:00.000Z",
186
+ end: "2026-09-16T00:00:00.000Z",
187
+ layerId: null,
188
+ layerName: null,
189
+ override: null,
190
+ },
191
+ {
192
+ shiftKey: "sre-2",
193
+ userId: "u-dan",
194
+ userName: "Dan Diaz",
195
+ start: "2026-09-18T00:00:00.000Z",
196
+ end: "2026-09-21T00:00:00.000Z",
197
+ layerId: null,
198
+ layerName: null,
199
+ override: {
200
+ originalUserId: "u-carol",
201
+ originalUserName: "Carol Chen",
202
+ overrideStartsAt: "2026-09-18T00:00:00.000Z",
203
+ overrideEndsAt: "2026-09-21T00:00:00.000Z",
204
+ onCallDutyPolicyId: null,
205
+ },
206
+ },
207
+ ],
208
+ };
209
+
210
+ const LEGACY: ScheduleTimelineScheduleJson = {
211
+ scheduleId: "s-legacy",
212
+ scheduleName: "Legacy pager",
213
+ scheduleTimezone: null,
214
+ ownerTeamIds: [],
215
+ isCurrentUserOnRoster: false,
216
+ truncated: false,
217
+ shifts: [],
218
+ };
219
+
220
+ function timeline(
221
+ overrides?: Partial<ScheduleTimelineResponse>,
222
+ ): ScheduleTimelineResponse {
223
+ return {
224
+ from: WEEK_FROM,
225
+ to: WEEK_TO,
226
+ generatedAt: NOW.toISOString(),
227
+ truncated: false,
228
+ totalScheduleCount: 3,
229
+ schedulesTruncated: false,
230
+ schedules: [PAYMENTS, SRE, LEGACY],
231
+ teams: [
232
+ { teamId: "t-pay", teamName: "Payments", isCurrentUserMember: false },
233
+ { teamId: "t-sre", teamName: "SRE", isCurrentUserMember: true },
234
+ ],
235
+ ...(overrides || {}),
236
+ };
237
+ }
238
+
239
+ function ok(body: ScheduleTimelineResponse): HTTPResponse<JSONObject> {
240
+ return new HTTPResponse<JSONObject>(
241
+ 200,
242
+ JSON.parse(JSON.stringify(body)) as JSONObject,
243
+ {},
244
+ );
245
+ }
246
+
247
+ interface RequestedWindow {
248
+ from: string;
249
+ to: string;
250
+ teamId: string | null;
251
+ }
252
+
253
+ function requested(call: number): RequestedWindow {
254
+ const options: { url: URL } = getMock.mock.calls[call]?.[0] as { url: URL };
255
+ const parsed: globalThis.URL = new globalThis.URL(options.url.toString());
256
+
257
+ return {
258
+ from: parsed.searchParams.get("from") || "",
259
+ to: parsed.searchParams.get("to") || "",
260
+ teamId: parsed.searchParams.get("teamId"),
261
+ };
262
+ }
263
+
264
+ function lastRequested(): RequestedWindow {
265
+ return requested(getMock.mock.calls.length - 1);
266
+ }
267
+
268
+ /*
269
+ * Answers every request with `body`, but with `from` / `to` set to the
270
+ * window that was asked for, as the server would.
271
+ */
272
+ function answerWith(body: ScheduleTimelineResponse): void {
273
+ getMock.mockImplementation(async (options: { url: URL }) => {
274
+ const parsed: globalThis.URL = new globalThis.URL(options.url.toString());
275
+
276
+ return ok({
277
+ ...body,
278
+ from: parsed.searchParams.get("from") || body.from,
279
+ to: parsed.searchParams.get("to") || body.to,
280
+ });
281
+ });
282
+ }
283
+
284
+ function renderTimeline(
285
+ props?: React.ComponentProps<typeof ScheduleTimeline>,
286
+ ): void {
287
+ render(
288
+ <MemoryRouter>
289
+ <ScheduleTimeline {...(props || {})} />
290
+ </MemoryRouter>,
291
+ );
292
+ }
293
+
294
+ function rowIds(): Array<string> {
295
+ return screen
296
+ .queryAllByTestId("timeline-schedule-row")
297
+ .map((row: HTMLElement) => {
298
+ return row.getAttribute("data-schedule-id") || "";
299
+ });
300
+ }
301
+
302
+ function row(scheduleId: string, index: number = 0): HTMLElement {
303
+ const rows: Array<HTMLElement> = screen
304
+ .queryAllByTestId("timeline-schedule-row")
305
+ .filter((element: HTMLElement) => {
306
+ return element.getAttribute("data-schedule-id") === scheduleId;
307
+ });
308
+
309
+ const found: HTMLElement | undefined = rows[index];
310
+
311
+ if (!found) {
312
+ throw new Error(`No row for ${scheduleId}`);
313
+ }
314
+
315
+ return found;
316
+ }
317
+
318
+ async function loaded(): Promise<void> {
319
+ await waitFor(() => {
320
+ expect(screen.queryAllByTestId("timeline-schedule-row").length).toBe(4);
321
+ });
322
+ }
323
+
324
+ beforeEach(() => {
325
+ getMock.mockReset();
326
+ window.localStorage.clear();
327
+
328
+ jest.spyOn(OneUptimeDate, "getCurrentDate").mockReturnValue(NOW);
329
+ jest
330
+ .spyOn(OneUptimeDate, "getCurrentTimezone")
331
+ .mockReturnValue(
332
+ "UTC" as ReturnType<typeof OneUptimeDate.getCurrentTimezone>,
333
+ );
334
+ jest
335
+ .spyOn(OneUptimeDate, "getUserPrefers12HourFormat")
336
+ .mockReturnValue(false);
337
+
338
+ answerWith(timeline());
339
+ });
340
+
341
+ afterEach(() => {
342
+ cleanup();
343
+ jest.restoreAllMocks();
344
+ });
345
+
346
+ describe("first load", () => {
347
+ test("asks for the current ISO week in the viewer's zone", async () => {
348
+ renderTimeline();
349
+ await loaded();
350
+
351
+ expect(getMock).toHaveBeenCalledTimes(1);
352
+ expect(requested(0)).toEqual({
353
+ from: WEEK_FROM,
354
+ to: WEEK_TO,
355
+ teamId: null,
356
+ });
357
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
358
+ "September 14 – 20, 2026",
359
+ );
360
+ });
361
+
362
+ test("shows skeleton rows until the answer arrives", async () => {
363
+ let resolve: (value: HTTPResponse<JSONObject>) => void = () => {};
364
+
365
+ getMock.mockImplementation(() => {
366
+ return new Promise<HTTPResponse<JSONObject>>(
367
+ (done: (value: HTTPResponse<JSONObject>) => void) => {
368
+ resolve = done;
369
+ },
370
+ );
371
+ });
372
+
373
+ renderTimeline();
374
+
375
+ expect(screen.getByTestId("timeline-skeleton")).toBeInTheDocument();
376
+ expect(screen.queryByTestId("timeline-summary")).not.toBeInTheDocument();
377
+
378
+ await act(async () => {
379
+ resolve(ok(timeline()));
380
+ });
381
+
382
+ await loaded();
383
+ expect(screen.queryByTestId("timeline-skeleton")).not.toBeInTheDocument();
384
+ });
385
+
386
+ test("seven day columns, today marked", async () => {
387
+ renderTimeline();
388
+ await loaded();
389
+
390
+ const headers: Array<HTMLElement> = screen.getAllByTestId(
391
+ "timeline-day-header",
392
+ );
393
+
394
+ expect(headers).toHaveLength(7);
395
+ expect(
396
+ headers.map((header: HTMLElement) => {
397
+ return header.getAttribute("data-today");
398
+ }),
399
+ ).toEqual(["false", "false", "false", "true", "false", "false", "false"]);
400
+ expect(screen.getByTestId("timeline-now-line")).toBeInTheDocument();
401
+ });
402
+
403
+ test("rows are grouped by owner team; a shared schedule appears under each team", async () => {
404
+ renderTimeline();
405
+ await loaded();
406
+
407
+ expect(
408
+ screen
409
+ .getAllByTestId("timeline-group-header")
410
+ .map((header: HTMLElement) => {
411
+ return header.textContent;
412
+ }),
413
+ ).toEqual(["Payments2", "SRE1Your team", "No owner team1"]);
414
+
415
+ expect(rowIds()).toEqual(["s-pay", "s-sre", "s-sre", "s-legacy"]);
416
+ });
417
+
418
+ test("schedule names link to the schedule's page", async () => {
419
+ renderTimeline();
420
+ await loaded();
421
+
422
+ const link: HTMLElement = within(row("s-pay")).getByRole("link", {
423
+ name: "Payments primary",
424
+ });
425
+
426
+ expect(link.getAttribute("href")).toContain(
427
+ "/on-call-duty/schedules/s-pay",
428
+ );
429
+ });
430
+
431
+ test("each row says who is on call now, or that nobody is", async () => {
432
+ renderTimeline();
433
+ await loaded();
434
+
435
+ expect(
436
+ within(row("s-pay")).getByTestId("timeline-on-call-now"),
437
+ ).toHaveTextContent("Bob Bergon call now");
438
+ expect(
439
+ within(row("s-sre")).getByTestId("timeline-uncovered-now"),
440
+ ).toHaveTextContent("No one on call now");
441
+ expect(
442
+ within(row("s-legacy")).getByTestId("timeline-uncovered-now"),
443
+ ).toBeInTheDocument();
444
+ });
445
+
446
+ test("the caller's own schedules are tagged", async () => {
447
+ renderTimeline();
448
+ await loaded();
449
+
450
+ expect(within(row("s-sre")).getByText("You")).toBeInTheDocument();
451
+ expect(within(row("s-pay")).queryByText("You")).not.toBeInTheDocument();
452
+ });
453
+
454
+ test("bars for shifts, hatched gaps, and the overridden person underneath", async () => {
455
+ renderTimeline();
456
+ await loaded();
457
+
458
+ const payBars: Array<HTMLElement> = within(row("s-pay")).getAllByTestId(
459
+ "timeline-shift-bar",
460
+ );
461
+
462
+ expect(
463
+ payBars.map((bar: HTMLElement) => {
464
+ return [
465
+ bar.getAttribute("data-user-id"),
466
+ bar.getAttribute("data-active"),
467
+ ];
468
+ }),
469
+ ).toEqual([
470
+ ["u-alice", "false"],
471
+ ["u-bob", "true"],
472
+ ]);
473
+ expect(within(row("s-pay")).queryAllByTestId("timeline-gap")).toHaveLength(
474
+ 0,
475
+ );
476
+
477
+ const sreRow: HTMLElement = row("s-sre");
478
+
479
+ expect(within(sreRow).getAllByTestId("timeline-gap")).toHaveLength(1);
480
+ expect(
481
+ within(sreRow)
482
+ .getAllByTestId("timeline-gap")[0]
483
+ ?.getAttribute("aria-label"),
484
+ ).toBe("No one on call, Wed, Sep 16, 00:00 → Fri, Sep 18, 00:00");
485
+
486
+ const overrideBar: HTMLElement | undefined = within(sreRow)
487
+ .getAllByTestId("timeline-shift-bar")
488
+ .find((bar: HTMLElement) => {
489
+ return bar.getAttribute("data-override") === "true";
490
+ });
491
+
492
+ expect(overrideBar).toHaveTextContent("⇄ Dan Diaz");
493
+ expect(overrideBar?.getAttribute("aria-label")).toContain(
494
+ "covering for Carol Chen",
495
+ );
496
+
497
+ const overridden: HTMLElement = within(sreRow).getByTestId(
498
+ "timeline-overridden-segment",
499
+ );
500
+
501
+ expect(overridden).toHaveTextContent("Carol Chen (overridden)");
502
+
503
+ // Nobody at all: the whole week is one gap.
504
+ expect(within(row("s-legacy")).getAllByTestId("timeline-gap")).toHaveLength(
505
+ 1,
506
+ );
507
+ });
508
+
509
+ test("the summary counts schedules, coverage now, and gaps", async () => {
510
+ renderTimeline();
511
+ await loaded();
512
+
513
+ const summary: HTMLElement = screen.getByTestId("timeline-summary");
514
+
515
+ expect(summary).toHaveTextContent("3schedules");
516
+ expect(
517
+ screen.getByTestId("timeline-summary-covered-now"),
518
+ ).toHaveTextContent("1covered now");
519
+ expect(
520
+ screen.getByTestId("timeline-summary-uncovered-now"),
521
+ ).toHaveTextContent("2with no one on call now");
522
+ expect(screen.getByTestId("timeline-summary-gaps")).toHaveTextContent(
523
+ "2with coverage gaps this week",
524
+ );
525
+ });
526
+
527
+ test("the legend lists everyone on call this week, heaviest load first", async () => {
528
+ renderTimeline();
529
+ await loaded();
530
+
531
+ const people: Array<string> = screen
532
+ .getAllByTestId("timeline-person")
533
+ .map((chip: HTMLElement) => {
534
+ return chip.getAttribute("data-user-id") || "";
535
+ });
536
+
537
+ // Bob 3d15h, Alice 3d9h, Dan 3d, Carol 2d.
538
+ expect(people).toEqual(["u-bob", "u-alice", "u-dan", "u-carol"]);
539
+ expect(screen.getByTestId("timeline-legend")).toHaveTextContent(
540
+ "Times in UTC.",
541
+ );
542
+ });
543
+ });
544
+
545
+ describe("navigation", () => {
546
+ test("next, previous and today move the requested window a week at a time", async () => {
547
+ renderTimeline();
548
+ await loaded();
549
+
550
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
551
+
552
+ await waitFor(() => {
553
+ expect(lastRequested().from).toBe("2026-09-21T00:00:00.000Z");
554
+ });
555
+ expect(lastRequested().to).toBe("2026-09-28T00:00:00.000Z");
556
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
557
+ "September 21 – 27, 2026",
558
+ );
559
+
560
+ // Not this week any more: no red line, no "now" statements.
561
+ await waitFor(() => {
562
+ expect(screen.queryByTestId("timeline-now-line")).not.toBeInTheDocument();
563
+ });
564
+ expect(
565
+ screen.queryByTestId("timeline-summary-covered-now"),
566
+ ).not.toBeInTheDocument();
567
+
568
+ fireEvent.click(screen.getByTestId("timeline-previous-button"));
569
+ fireEvent.click(screen.getByTestId("timeline-previous-button"));
570
+
571
+ await waitFor(() => {
572
+ expect(lastRequested().from).toBe("2026-09-07T00:00:00.000Z");
573
+ });
574
+
575
+ fireEvent.click(screen.getByTestId("timeline-today-button"));
576
+
577
+ await waitFor(() => {
578
+ expect(lastRequested().from).toBe(WEEK_FROM);
579
+ });
580
+ });
581
+
582
+ test("outside the current week the rows describe the period instead of 'now'", async () => {
583
+ renderTimeline();
584
+ await loaded();
585
+
586
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
587
+
588
+ await waitFor(() => {
589
+ expect(
590
+ within(row("s-legacy")).getByText("No one on call this week"),
591
+ ).toBeInTheDocument();
592
+ });
593
+ });
594
+
595
+ test("the month view asks for the calendar month and is remembered", async () => {
596
+ renderTimeline();
597
+ await loaded();
598
+
599
+ fireEvent.click(screen.getByTestId("timeline-mode-month"));
600
+
601
+ await waitFor(() => {
602
+ expect(lastRequested()).toEqual({
603
+ from: "2026-09-01T00:00:00.000Z",
604
+ to: "2026-10-01T00:00:00.000Z",
605
+ teamId: null,
606
+ });
607
+ });
608
+
609
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
610
+ "September 2026",
611
+ );
612
+ expect(screen.getByTestId("timeline-mode-month")).toHaveAttribute(
613
+ "aria-checked",
614
+ "true",
615
+ );
616
+
617
+ await waitFor(() => {
618
+ expect(screen.getAllByTestId("timeline-day-header")).toHaveLength(30);
619
+ });
620
+
621
+ expect(window.localStorage.getItem(VIEW_MODE_STORAGE_KEY)).toBe("month");
622
+ });
623
+
624
+ test("a remembered month view is used on the next visit", async () => {
625
+ window.localStorage.setItem(VIEW_MODE_STORAGE_KEY, "month");
626
+
627
+ renderTimeline();
628
+
629
+ await waitFor(() => {
630
+ expect(requested(0).from).toBe("2026-09-01T00:00:00.000Z");
631
+ });
632
+ });
633
+
634
+ test("the back button stops where the server stops computing", async () => {
635
+ renderTimeline();
636
+ await loaded();
637
+
638
+ fireEvent.click(screen.getByTestId("timeline-mode-month"));
639
+
640
+ const back: HTMLElement = screen.getByTestId("timeline-previous-button");
641
+
642
+ /*
643
+ * 180 days before 17 Sep 2026 is 21 Mar 2026: March is reachable,
644
+ * February is not.
645
+ */
646
+ for (let step: number = 0; step < 6; step++) {
647
+ expect(back).not.toBeDisabled();
648
+ fireEvent.click(back);
649
+ }
650
+
651
+ await waitFor(() => {
652
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
653
+ "March 2026",
654
+ );
655
+ });
656
+ expect(back).toBeDisabled();
657
+ expect(screen.getByTestId("timeline-next-button")).not.toBeDisabled();
658
+ });
659
+
660
+ test("a slower answer for a window the reader already left is ignored", async () => {
661
+ const pending: Array<(value: HTTPResponse<JSONObject>) => void> = [];
662
+
663
+ getMock.mockImplementation(() => {
664
+ return new Promise<HTTPResponse<JSONObject>>(
665
+ (done: (value: HTTPResponse<JSONObject>) => void) => {
666
+ pending.push(done);
667
+ },
668
+ );
669
+ });
670
+
671
+ renderTimeline();
672
+
673
+ await waitFor(() => {
674
+ expect(pending).toHaveLength(1);
675
+ });
676
+
677
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
678
+
679
+ await waitFor(() => {
680
+ expect(pending).toHaveLength(2);
681
+ });
682
+
683
+ // The next week answers first...
684
+ await act(async () => {
685
+ pending[1]!(
686
+ ok(
687
+ timeline({
688
+ from: "2026-09-21T00:00:00.000Z",
689
+ to: "2026-09-28T00:00:00.000Z",
690
+ schedules: [LEGACY],
691
+ teams: [],
692
+ }),
693
+ ),
694
+ );
695
+ });
696
+
697
+ // ...then the stale current week.
698
+ await act(async () => {
699
+ pending[0]!(ok(timeline()));
700
+ });
701
+
702
+ await waitFor(() => {
703
+ expect(rowIds()).toEqual(["s-legacy"]);
704
+ });
705
+ });
706
+ });
707
+
708
+ describe("filters", () => {
709
+ test("search matches schedules, teams and people", async () => {
710
+ renderTimeline();
711
+ await loaded();
712
+
713
+ fireEvent.change(screen.getByTestId("timeline-search"), {
714
+ target: { value: "dan" },
715
+ });
716
+
717
+ expect(rowIds()).toEqual(["s-sre", "s-sre"]);
718
+
719
+ fireEvent.change(screen.getByTestId("timeline-search"), {
720
+ target: { value: "legacy" },
721
+ });
722
+
723
+ expect(rowIds()).toEqual(["s-legacy"]);
724
+ });
725
+
726
+ test("the team picker offers My teams and every owner team", async () => {
727
+ renderTimeline();
728
+ await loaded();
729
+
730
+ const select: HTMLSelectElement = screen.getByTestId(
731
+ "timeline-team-filter",
732
+ ) as HTMLSelectElement;
733
+
734
+ expect(
735
+ Array.from(select.options).map((option: HTMLOptionElement) => {
736
+ return option.textContent;
737
+ }),
738
+ ).toEqual(["All teams", "My teams", "Payments", "SRE"]);
739
+
740
+ fireEvent.change(select, { target: { value: "t-sre" } });
741
+
742
+ // Only the chosen team's group, even though SRE primary is co-owned.
743
+ expect(rowIds()).toEqual(["s-sre"]);
744
+ expect(
745
+ screen
746
+ .getAllByTestId("timeline-group-header")
747
+ .map((header: HTMLElement) => {
748
+ return header.textContent;
749
+ }),
750
+ ).toEqual(["SRE1Your team"]);
751
+
752
+ fireEvent.change(select, { target: { value: "t-pay" } });
753
+
754
+ expect(rowIds()).toEqual(["s-pay", "s-sre"]);
755
+ });
756
+
757
+ test("My teams keeps the schedules a team the caller is in owns", async () => {
758
+ renderTimeline();
759
+ await loaded();
760
+
761
+ fireEvent.change(screen.getByTestId("timeline-team-filter"), {
762
+ target: { value: "__my_teams__" },
763
+ });
764
+
765
+ expect(rowIds()).toEqual(["s-sre"]);
766
+ expect(
767
+ screen
768
+ .getAllByTestId("timeline-group-header")
769
+ .map((header: HTMLElement) => {
770
+ return header.textContent;
771
+ }),
772
+ ).toEqual(["SRE1Your team"]);
773
+ });
774
+
775
+ test("Schedules I'm on", async () => {
776
+ renderTimeline();
777
+ await loaded();
778
+
779
+ const toggle: HTMLElement = screen.getByTestId("timeline-only-mine");
780
+
781
+ fireEvent.click(toggle);
782
+
783
+ expect(toggle).toHaveAttribute("aria-pressed", "true");
784
+ expect(new Set(rowIds())).toEqual(new Set(["s-sre"]));
785
+
786
+ fireEvent.click(toggle);
787
+
788
+ expect(rowIds()).toHaveLength(4);
789
+ });
790
+
791
+ test("the 'no one on call now' count filters to those schedules and back", async () => {
792
+ renderTimeline();
793
+ await loaded();
794
+
795
+ const chip: HTMLElement = screen.getByTestId(
796
+ "timeline-summary-uncovered-now",
797
+ );
798
+
799
+ fireEvent.click(chip);
800
+
801
+ expect(chip).toHaveAttribute("aria-pressed", "true");
802
+ expect(rowIds()).toEqual(["s-sre", "s-sre", "s-legacy"]);
803
+
804
+ // The counts do not move under the reader's feet.
805
+ expect(
806
+ screen.getByTestId("timeline-summary-covered-now"),
807
+ ).toHaveTextContent("1covered now");
808
+
809
+ fireEvent.click(chip);
810
+
811
+ expect(rowIds()).toHaveLength(4);
812
+ });
813
+
814
+ test("the 'coverage gaps' count filters too", async () => {
815
+ renderTimeline();
816
+ await loaded();
817
+
818
+ fireEvent.click(screen.getByTestId("timeline-summary-gaps"));
819
+
820
+ expect(new Set(rowIds())).toEqual(new Set(["s-sre", "s-legacy"]));
821
+ });
822
+
823
+ test("no match: a message and a way back", async () => {
824
+ renderTimeline();
825
+ await loaded();
826
+
827
+ fireEvent.change(screen.getByTestId("timeline-search"), {
828
+ target: { value: "zzz" },
829
+ });
830
+
831
+ expect(screen.getByTestId("timeline-no-matches")).toHaveTextContent(
832
+ "No schedules match these filters",
833
+ );
834
+
835
+ fireEvent.click(
836
+ within(screen.getByTestId("timeline-no-matches")).getByText(
837
+ "Clear filters",
838
+ ),
839
+ );
840
+
841
+ expect(rowIds()).toHaveLength(4);
842
+ expect(screen.getByTestId("timeline-search")).toHaveValue("");
843
+ });
844
+
845
+ test("Clear filters resets every filter", async () => {
846
+ renderTimeline();
847
+ await loaded();
848
+
849
+ expect(
850
+ screen.queryByTestId("timeline-clear-filters"),
851
+ ).not.toBeInTheDocument();
852
+
853
+ fireEvent.click(screen.getByTestId("timeline-only-mine"));
854
+ fireEvent.change(screen.getByTestId("timeline-search"), {
855
+ target: { value: "sre" },
856
+ });
857
+
858
+ fireEvent.click(screen.getByTestId("timeline-clear-filters"));
859
+
860
+ expect(rowIds()).toHaveLength(4);
861
+ expect(screen.getByTestId("timeline-only-mine")).toHaveAttribute(
862
+ "aria-pressed",
863
+ "false",
864
+ );
865
+ });
866
+
867
+ test("grouping can be turned off, and that is remembered", async () => {
868
+ renderTimeline();
869
+ await loaded();
870
+
871
+ fireEvent.click(screen.getByTestId("timeline-group-by-team"));
872
+
873
+ expect(screen.queryAllByTestId("timeline-group-header")).toHaveLength(0);
874
+ expect(rowIds()).toEqual(["s-pay", "s-sre", "s-legacy"]);
875
+ expect(
876
+ JSON.parse(
877
+ window.localStorage.getItem(GROUP_BY_TEAM_STORAGE_KEY) || "null",
878
+ ),
879
+ ).toBe(false);
880
+ });
881
+
882
+ test("a group collapses and expands", async () => {
883
+ renderTimeline();
884
+ await loaded();
885
+
886
+ const header: HTMLElement = screen.getAllByTestId(
887
+ "timeline-group-header",
888
+ )[0] as HTMLElement;
889
+ const toggle: HTMLElement = within(header).getByRole("button");
890
+
891
+ fireEvent.click(toggle);
892
+
893
+ expect(toggle).toHaveAttribute("aria-expanded", "false");
894
+ expect(rowIds()).toEqual(["s-sre", "s-legacy"]);
895
+
896
+ fireEvent.click(toggle);
897
+
898
+ expect(rowIds()).toHaveLength(4);
899
+ });
900
+ });
901
+
902
+ describe("highlighting a person", () => {
903
+ test("from the legend: their bars are marked everywhere, and it toggles off", async () => {
904
+ renderTimeline();
905
+ await loaded();
906
+
907
+ const chip: HTMLElement = screen
908
+ .getAllByTestId("timeline-person")
909
+ .find((element: HTMLElement) => {
910
+ return element.getAttribute("data-user-id") === "u-dan";
911
+ }) as HTMLElement;
912
+
913
+ fireEvent.click(chip);
914
+
915
+ expect(chip).toHaveAttribute("aria-pressed", "true");
916
+
917
+ const pressed: Array<string> = screen
918
+ .getAllByTestId("timeline-shift-bar")
919
+ .filter((bar: HTMLElement) => {
920
+ return bar.getAttribute("aria-pressed") === "true";
921
+ })
922
+ .map((bar: HTMLElement) => {
923
+ return bar.getAttribute("data-user-id") || "";
924
+ });
925
+
926
+ // Dan's one shift, drawn under both groups SRE primary appears in.
927
+ expect(pressed).toEqual(["u-dan", "u-dan"]);
928
+
929
+ fireEvent.click(screen.getByTestId("timeline-clear-highlight"));
930
+
931
+ expect(chip).toHaveAttribute("aria-pressed", "false");
932
+ });
933
+
934
+ test("from a bar", async () => {
935
+ renderTimeline();
936
+ await loaded();
937
+
938
+ const bar: HTMLElement = within(row("s-pay"))
939
+ .getAllByTestId("timeline-shift-bar")
940
+ .find((element: HTMLElement) => {
941
+ return element.getAttribute("data-user-id") === "u-bob";
942
+ }) as HTMLElement;
943
+
944
+ fireEvent.click(bar);
945
+
946
+ expect(bar).toHaveAttribute("aria-pressed", "true");
947
+ expect(
948
+ screen.getAllByTestId("timeline-person").find((element: HTMLElement) => {
949
+ return element.getAttribute("data-user-id") === "u-bob";
950
+ }),
951
+ ).toHaveAttribute("aria-pressed", "true");
952
+
953
+ fireEvent.click(bar);
954
+
955
+ expect(bar).toHaveAttribute("aria-pressed", "false");
956
+ });
957
+ });
958
+
959
+ describe("locked to a team", () => {
960
+ test("asks for that team, and hides the team picker and grouping", async () => {
961
+ const teamId: ObjectID = ObjectID.generate();
962
+
963
+ renderTimeline({ teamId });
964
+
965
+ // Ungrouped: each schedule once.
966
+ await waitFor(() => {
967
+ expect(rowIds()).toEqual(["s-pay", "s-sre", "s-legacy"]);
968
+ });
969
+
970
+ expect(requested(0).teamId).toBe(teamId.toString());
971
+ expect(
972
+ screen.queryByTestId("timeline-team-filter"),
973
+ ).not.toBeInTheDocument();
974
+ expect(
975
+ screen.queryByTestId("timeline-group-by-team"),
976
+ ).not.toBeInTheDocument();
977
+ expect(screen.queryAllByTestId("timeline-group-header")).toHaveLength(0);
978
+ expect(screen.getByText("On-Call Schedules")).toBeInTheDocument();
979
+ });
980
+
981
+ test("a team with no schedules is told how to add one", async () => {
982
+ answerWith(timeline({ schedules: [], teams: [], totalScheduleCount: 0 }));
983
+
984
+ renderTimeline({ teamId: ObjectID.generate() });
985
+
986
+ await waitFor(() => {
987
+ expect(
988
+ screen.getByText("This team does not own any on-call schedules yet"),
989
+ ).toBeInTheDocument();
990
+ });
991
+ });
992
+ });
993
+
994
+ describe("empty, error and partial answers", () => {
995
+ test("a project without schedules gets an empty state with a way forward", async () => {
996
+ answerWith(timeline({ schedules: [], teams: [], totalScheduleCount: 0 }));
997
+
998
+ renderTimeline();
999
+
1000
+ await waitFor(() => {
1001
+ expect(screen.getByText("No on-call schedules yet")).toBeInTheDocument();
1002
+ });
1003
+ expect(screen.getByText("Go to On-Call Schedules")).toBeInTheDocument();
1004
+ expect(screen.queryByTestId("timeline-summary")).not.toBeInTheDocument();
1005
+ });
1006
+
1007
+ test("an error shows the server's message, and retry asks again", async () => {
1008
+ getMock.mockResolvedValueOnce(
1009
+ new HTTPErrorResponse(
1010
+ 422,
1011
+ { message: "You do not have permission to read schedules." },
1012
+ {},
1013
+ ),
1014
+ );
1015
+
1016
+ renderTimeline();
1017
+
1018
+ await waitFor(() => {
1019
+ expect(
1020
+ screen.getByText("You do not have permission to read schedules."),
1021
+ ).toBeInTheDocument();
1022
+ });
1023
+
1024
+ fireEvent.click(screen.getByTestId("refresh-button"));
1025
+
1026
+ await loaded();
1027
+ expect(getMock).toHaveBeenCalledTimes(2);
1028
+ });
1029
+
1030
+ test("a capped schedule list says so", async () => {
1031
+ answerWith(timeline({ totalScheduleCount: 412, schedulesTruncated: true }));
1032
+
1033
+ renderTimeline();
1034
+ await loaded();
1035
+
1036
+ expect(
1037
+ screen.getByTestId("timeline-schedules-truncated"),
1038
+ ).toHaveTextContent("Showing the first 3 of 412 schedules");
1039
+ });
1040
+
1041
+ test("an incomplete rotation says so", async () => {
1042
+ answerWith(timeline({ truncated: true }));
1043
+
1044
+ renderTimeline();
1045
+ await loaded();
1046
+
1047
+ expect(screen.getByTestId("timeline-engine-truncated")).toBeInTheDocument();
1048
+ });
1049
+
1050
+ test("a partly served window is shaded and explained, not drawn as 'nobody on call'", async () => {
1051
+ getMock.mockResolvedValue(
1052
+ ok(timeline({ from: "2026-09-16T00:00:00.000Z", to: WEEK_TO })),
1053
+ );
1054
+
1055
+ renderTimeline();
1056
+ await loaded();
1057
+
1058
+ expect(screen.getByTestId("timeline-outside-range")).toBeInTheDocument();
1059
+ expect(screen.getAllByTestId("timeline-unavailable")).toHaveLength(1);
1060
+
1061
+ // Legacy has no shifts: its gap starts where the served window starts.
1062
+ expect(
1063
+ within(row("s-legacy"))
1064
+ .getByTestId("timeline-gap")
1065
+ .getAttribute("aria-label"),
1066
+ ).toBe("No one on call, Wed, Sep 16, 00:00 → Mon, Sep 21, 00:00");
1067
+ });
1068
+ });
1069
+
1070
+ // -- Behaviour found in review -------------------------------------------------
1071
+
1072
+ function pendingAnswers(): Array<(value: HTTPResponse<JSONObject>) => void> {
1073
+ const pending: Array<(value: HTTPResponse<JSONObject>) => void> = [];
1074
+
1075
+ getMock.mockImplementation(() => {
1076
+ return new Promise<HTTPResponse<JSONObject>>(
1077
+ (done: (value: HTTPResponse<JSONObject>) => void) => {
1078
+ pending.push(done);
1079
+ },
1080
+ );
1081
+ });
1082
+
1083
+ return pending;
1084
+ }
1085
+
1086
+ function dayKeys(): Array<string> {
1087
+ return screen
1088
+ .getAllByTestId("timeline-day-header")
1089
+ .map((header: HTMLElement) => {
1090
+ return header.getAttribute("title") || "";
1091
+ });
1092
+ }
1093
+
1094
+ describe("while the next range loads", () => {
1095
+ test("the previous week stays drawn, dimmed, against its own days", async () => {
1096
+ const pending: Array<(value: HTTPResponse<JSONObject>) => void> =
1097
+ pendingAnswers();
1098
+
1099
+ renderTimeline();
1100
+
1101
+ await waitFor(() => {
1102
+ expect(pending).toHaveLength(1);
1103
+ });
1104
+
1105
+ await act(async () => {
1106
+ pending[0]!(ok(timeline()));
1107
+ });
1108
+
1109
+ await loaded();
1110
+
1111
+ const rowsBefore: Array<string> = rowIds();
1112
+ const gapsBefore: number = screen.getAllByTestId("timeline-gap").length;
1113
+
1114
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
1115
+
1116
+ await waitFor(() => {
1117
+ expect(pending).toHaveLength(2);
1118
+ });
1119
+
1120
+ // The label follows the request...
1121
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
1122
+ "September 21 – 27, 2026",
1123
+ );
1124
+ expect(screen.getByTestId("timeline-refreshing")).toBeInTheDocument();
1125
+
1126
+ // ...the grid stays on the week its data belongs to, gaps and all.
1127
+ expect(dayKeys()[0]).toBe("2026-09-14");
1128
+ expect(rowIds()).toEqual(rowsBefore);
1129
+ expect(screen.getAllByTestId("timeline-gap")).toHaveLength(gapsBefore);
1130
+ expect(screen.getByTestId("timeline-now-line")).toBeInTheDocument();
1131
+ expect(
1132
+ screen.queryByTestId("timeline-unavailable"),
1133
+ ).not.toBeInTheDocument();
1134
+ expect(
1135
+ screen.queryByTestId("timeline-outside-range"),
1136
+ ).not.toBeInTheDocument();
1137
+
1138
+ await act(async () => {
1139
+ pending[1]!(
1140
+ ok(
1141
+ timeline({
1142
+ from: "2026-09-21T00:00:00.000Z",
1143
+ to: "2026-09-28T00:00:00.000Z",
1144
+ }),
1145
+ ),
1146
+ );
1147
+ });
1148
+
1149
+ await waitFor(() => {
1150
+ expect(dayKeys()[0]).toBe("2026-09-21");
1151
+ });
1152
+ expect(screen.queryByTestId("timeline-refreshing")).not.toBeInTheDocument();
1153
+ expect(screen.queryByTestId("timeline-now-line")).not.toBeInTheDocument();
1154
+ });
1155
+
1156
+ test("an empty team page stays on its empty state", async () => {
1157
+ const pending: Array<(value: HTTPResponse<JSONObject>) => void> =
1158
+ pendingAnswers();
1159
+
1160
+ renderTimeline({ teamId: ObjectID.generate() });
1161
+
1162
+ await waitFor(() => {
1163
+ expect(pending).toHaveLength(1);
1164
+ });
1165
+
1166
+ await act(async () => {
1167
+ pending[0]!(ok(timeline({ schedules: [], teams: [] })));
1168
+ });
1169
+
1170
+ await waitFor(() => {
1171
+ expect(
1172
+ screen.getByText("This team does not own any on-call schedules yet"),
1173
+ ).toBeInTheDocument();
1174
+ });
1175
+
1176
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
1177
+
1178
+ await waitFor(() => {
1179
+ expect(pending).toHaveLength(2);
1180
+ });
1181
+
1182
+ expect(
1183
+ screen.getByText("This team does not own any on-call schedules yet"),
1184
+ ).toBeInTheDocument();
1185
+ expect(screen.queryByTestId("timeline-summary")).not.toBeInTheDocument();
1186
+ expect(screen.queryByTestId("timeline-no-matches")).not.toBeInTheDocument();
1187
+ });
1188
+
1189
+ test("leaving the page stops a request that is waiting to retry", async () => {
1190
+ getMock.mockResolvedValue(
1191
+ new HTTPErrorResponse(503, { message: "busy" }, { "retry-after": "1" }),
1192
+ );
1193
+
1194
+ jest.useFakeTimers();
1195
+
1196
+ try {
1197
+ renderTimeline();
1198
+
1199
+ await act(async () => {
1200
+ await Promise.resolve();
1201
+ });
1202
+
1203
+ expect(getMock).toHaveBeenCalledTimes(1);
1204
+
1205
+ cleanup();
1206
+
1207
+ await act(async () => {
1208
+ jest.advanceTimersByTime(5000);
1209
+ await Promise.resolve();
1210
+ });
1211
+
1212
+ expect(getMock).toHaveBeenCalledTimes(1);
1213
+ } finally {
1214
+ jest.useRealTimers();
1215
+ }
1216
+ });
1217
+ });
1218
+
1219
+ describe("changing the time zone", () => {
1220
+ test("keeps the same dates on screen, even across the date line", async () => {
1221
+ // Sunday 22:00 UTC: already Monday in Tokyo.
1222
+ jest
1223
+ .spyOn(OneUptimeDate, "getCurrentDate")
1224
+ .mockReturnValue(new Date("2026-09-20T22:00:00.000Z"));
1225
+
1226
+ renderTimeline();
1227
+
1228
+ await loaded();
1229
+
1230
+ expect(dayKeys()[0]).toBe("2026-09-14");
1231
+
1232
+ fireEvent.click(screen.getByTestId("timezone-to-Asia/Tokyo"));
1233
+
1234
+ // The week of 14 September, now laid out in Tokyo.
1235
+ await waitFor(() => {
1236
+ expect(lastRequested().from).toBe("2026-09-13T15:00:00.000Z");
1237
+ });
1238
+ expect(lastRequested().to).toBe("2026-09-20T15:00:00.000Z");
1239
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
1240
+ "September 14 – 20, 2026",
1241
+ );
1242
+ });
1243
+
1244
+ test("after paging forward, a zone to the west does not page back", async () => {
1245
+ renderTimeline();
1246
+
1247
+ await loaded();
1248
+
1249
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
1250
+
1251
+ await waitFor(() => {
1252
+ expect(lastRequested().from).toBe("2026-09-21T00:00:00.000Z");
1253
+ });
1254
+
1255
+ fireEvent.click(screen.getByTestId("timezone-to-America/New_York"));
1256
+
1257
+ await waitFor(() => {
1258
+ expect(lastRequested().from).toBe("2026-09-21T04:00:00.000Z");
1259
+ });
1260
+ expect(screen.getByTestId("timeline-range-label")).toHaveTextContent(
1261
+ "September 21 – 27, 2026",
1262
+ );
1263
+ });
1264
+ });
1265
+
1266
+ describe("the Week / Month switch", () => {
1267
+ test("is one tab stop, and the arrow keys move the selection", async () => {
1268
+ renderTimeline();
1269
+
1270
+ await loaded();
1271
+
1272
+ const weekButton: HTMLElement = screen.getByTestId("timeline-mode-week");
1273
+ const monthButton: HTMLElement = screen.getByTestId("timeline-mode-month");
1274
+
1275
+ expect(weekButton).toHaveAttribute("tabindex", "0");
1276
+ expect(monthButton).toHaveAttribute("tabindex", "-1");
1277
+
1278
+ weekButton.focus();
1279
+ fireEvent.keyDown(weekButton, { key: "ArrowRight" });
1280
+
1281
+ await waitFor(() => {
1282
+ expect(lastRequested().from).toBe("2026-09-01T00:00:00.000Z");
1283
+ });
1284
+
1285
+ expect(monthButton).toHaveAttribute("aria-checked", "true");
1286
+ expect(monthButton).toHaveAttribute("tabindex", "0");
1287
+ expect(document.activeElement).toBe(monthButton);
1288
+
1289
+ fireEvent.keyDown(monthButton, { key: "Home" });
1290
+
1291
+ await waitFor(() => {
1292
+ expect(weekButton).toHaveAttribute("aria-checked", "true");
1293
+ });
1294
+ expect(document.activeElement).toBe(weekButton);
1295
+
1296
+ // Other keys are left alone.
1297
+ fireEvent.keyDown(weekButton, { key: "a" });
1298
+ expect(weekButton).toHaveAttribute("aria-checked", "true");
1299
+ });
1300
+ });
1301
+
1302
+ describe("attention filters", () => {
1303
+ test("'no one on call now' is dropped when the reader pages away from now", async () => {
1304
+ renderTimeline();
1305
+
1306
+ await loaded();
1307
+
1308
+ fireEvent.click(screen.getByTestId("timeline-summary-uncovered-now"));
1309
+
1310
+ expect(rowIds()).toEqual(["s-sre", "s-sre", "s-legacy"]);
1311
+
1312
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
1313
+
1314
+ // Next week cannot say who is uncovered "now": every schedule is back.
1315
+ await waitFor(() => {
1316
+ expect(dayKeys()[0]).toBe("2026-09-21");
1317
+ });
1318
+ await waitFor(() => {
1319
+ expect(rowIds()).toHaveLength(4);
1320
+ });
1321
+ expect(screen.queryByTestId("timeline-no-matches")).not.toBeInTheDocument();
1322
+ });
1323
+
1324
+ test("an active 'coverage gaps' chip stays visible even at zero, so it can be turned off", async () => {
1325
+ const covered: ScheduleTimelineScheduleJson = {
1326
+ ...PAYMENTS,
1327
+ shifts: [
1328
+ {
1329
+ ...PAYMENTS.shifts[0]!,
1330
+ start: "2026-09-01T00:00:00.000Z",
1331
+ end: "2026-10-15T00:00:00.000Z",
1332
+ },
1333
+ ],
1334
+ };
1335
+
1336
+ getMock.mockImplementation(async (options: { url: URL }) => {
1337
+ const parsed: globalThis.URL = new globalThis.URL(options.url.toString());
1338
+ const from: string = parsed.searchParams.get("from") || WEEK_FROM;
1339
+ const to: string = parsed.searchParams.get("to") || WEEK_TO;
1340
+
1341
+ return ok(
1342
+ from === WEEK_FROM
1343
+ ? timeline()
1344
+ : timeline({ from, to, schedules: [covered] }),
1345
+ );
1346
+ });
1347
+
1348
+ renderTimeline();
1349
+
1350
+ await loaded();
1351
+
1352
+ fireEvent.click(screen.getByTestId("timeline-summary-gaps"));
1353
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
1354
+
1355
+ // Next week has no gaps at all; the chip must stay, at zero and pressed.
1356
+ await waitFor(() => {
1357
+ expect(screen.getByTestId("timeline-summary-gaps")).toHaveTextContent(
1358
+ "0with coverage gaps this week",
1359
+ );
1360
+ });
1361
+
1362
+ const chip: HTMLElement = screen.getByTestId("timeline-summary-gaps");
1363
+
1364
+ expect(chip).toHaveTextContent("0with coverage gaps this week");
1365
+ expect(chip).toHaveAttribute("aria-pressed", "true");
1366
+ expect(screen.getByTestId("timeline-no-matches")).toBeInTheDocument();
1367
+
1368
+ fireEvent.click(chip);
1369
+
1370
+ expect(rowIds()).toEqual(["s-pay"]);
1371
+ expect(
1372
+ screen.queryByTestId("timeline-summary-gaps"),
1373
+ ).not.toBeInTheDocument();
1374
+ });
1375
+ });
1376
+
1377
+ describe("layout details", () => {
1378
+ test("'no schedules match' is drawn outside the scrolling grid", async () => {
1379
+ renderTimeline();
1380
+
1381
+ await loaded();
1382
+
1383
+ fireEvent.change(screen.getByTestId("timeline-search"), {
1384
+ target: { value: "zzz" },
1385
+ });
1386
+
1387
+ expect(screen.getByTestId("timeline-no-matches")).toBeInTheDocument();
1388
+ expect(
1389
+ screen.queryByTestId("schedule-timeline-grid"),
1390
+ ).not.toBeInTheDocument();
1391
+ });
1392
+
1393
+ test("a row reads in time order, each override lane right after its shift", async () => {
1394
+ renderTimeline();
1395
+
1396
+ await loaded();
1397
+
1398
+ const order: Array<string> = within(row("s-sre"))
1399
+ .getAllByTestId(/timeline-(shift-bar|gap|overridden-segment)/)
1400
+ .map((element: HTMLElement) => {
1401
+ const kind: string = element.getAttribute("data-testid") || "";
1402
+ return kind === "timeline-shift-bar"
1403
+ ? `shift:${element.getAttribute("data-user-id")}`
1404
+ : kind.replace("timeline-", "");
1405
+ });
1406
+
1407
+ expect(order).toEqual([
1408
+ "shift:u-carol",
1409
+ "gap",
1410
+ "shift:u-dan",
1411
+ "overridden-segment",
1412
+ ]);
1413
+ });
1414
+
1415
+ test("a served window wholly outside the range shades the whole range", async () => {
1416
+ getMock.mockResolvedValue(
1417
+ ok(
1418
+ timeline({
1419
+ from: "2027-09-17T12:00:00.000Z",
1420
+ to: "2027-09-18T12:00:00.000Z",
1421
+ }),
1422
+ ),
1423
+ );
1424
+
1425
+ renderTimeline();
1426
+
1427
+ await loaded();
1428
+
1429
+ const bands: Array<HTMLElement> = screen.getAllByTestId(
1430
+ "timeline-unavailable",
1431
+ );
1432
+
1433
+ expect(bands).toHaveLength(1);
1434
+ expect(bands[0]?.style.left).toBe("0%");
1435
+ expect(bands[0]?.style.width).toBe("100%");
1436
+ expect(screen.getByTestId("timeline-outside-range")).toBeInTheDocument();
1437
+ expect(screen.queryAllByTestId("timeline-gap")).toHaveLength(0);
1438
+ });
1439
+
1440
+ test("keyboard focus uses an outline, which inline shadows and opacity cannot hide", async () => {
1441
+ renderTimeline();
1442
+
1443
+ await loaded();
1444
+
1445
+ for (const element of [
1446
+ ...screen.getAllByTestId("timeline-shift-bar"),
1447
+ ...screen.getAllByTestId("timeline-gap"),
1448
+ ...screen.getAllByTestId("timeline-overridden-segment"),
1449
+ ]) {
1450
+ expect(element.className).toContain("focus-visible:outline-2");
1451
+ expect(element.className).toContain("focus-visible:!opacity-100");
1452
+ expect(element.className).not.toContain("focus-visible:ring");
1453
+ }
1454
+ });
1455
+
1456
+ test("rows are memoized", () => {
1457
+ expect((TimelineRow as unknown as { $$typeof: symbol }).$$typeof).toBe(
1458
+ Symbol.for("react.memo"),
1459
+ );
1460
+ });
1461
+ });
1462
+
1463
+ describe("review round two", () => {
1464
+ test("paging away with 'no one on call now' on never flashes 'No schedules match'", async () => {
1465
+ const pending: Array<(value: HTTPResponse<JSONObject>) => void> =
1466
+ pendingAnswers();
1467
+
1468
+ renderTimeline();
1469
+
1470
+ await waitFor(() => {
1471
+ expect(pending).toHaveLength(1);
1472
+ });
1473
+ await act(async () => {
1474
+ pending[0]!(ok(timeline()));
1475
+ });
1476
+ await loaded();
1477
+
1478
+ fireEvent.click(screen.getByTestId("timeline-summary-uncovered-now"));
1479
+ fireEvent.click(screen.getByTestId("timeline-next-button"));
1480
+
1481
+ await waitFor(() => {
1482
+ expect(pending).toHaveLength(2);
1483
+ });
1484
+
1485
+ await act(async () => {
1486
+ pending[1]!(
1487
+ ok(
1488
+ timeline({
1489
+ from: "2026-09-21T00:00:00.000Z",
1490
+ to: "2026-09-28T00:00:00.000Z",
1491
+ }),
1492
+ ),
1493
+ );
1494
+ });
1495
+
1496
+ // The very first render of next week already shows every schedule.
1497
+ expect(screen.queryByTestId("timeline-no-matches")).not.toBeInTheDocument();
1498
+ expect(dayKeys()[0]).toBe("2026-09-21");
1499
+ expect(rowIds()).toHaveLength(4);
1500
+ });
1501
+
1502
+ test("a 'now' frozen on an earlier visit never marks a week the clock has left", () => {
1503
+ const data: TimelineData = TimelineModel.fromResponse(timeline());
1504
+ const groups: ReturnType<typeof TimelineModel.groupSchedules> =
1505
+ TimelineModel.groupSchedules({
1506
+ schedules: data.schedules,
1507
+ teams: data.teams,
1508
+ groupByTeam: false,
1509
+ });
1510
+
1511
+ const weekOf: (anchor: string) => TimelineRange = (
1512
+ anchor: string,
1513
+ ): TimelineRange => {
1514
+ return ScheduleTimelineLayout.getRange({
1515
+ mode: TimelineViewMode.Week,
1516
+ anchor: new Date(anchor),
1517
+ timezone: "UTC",
1518
+ });
1519
+ };
1520
+
1521
+ const thisWeek: TimelineRange = weekOf("2026-09-17T12:00:00.000Z");
1522
+ const lastWeek: TimelineRange = weekOf("2026-09-10T12:00:00.000Z");
1523
+
1524
+ const renderGrid: (range: TimelineRange, now: Date) => ReactElement = (
1525
+ range: TimelineRange,
1526
+ now: Date,
1527
+ ): ReactElement => {
1528
+ return (
1529
+ <MemoryRouter>
1530
+ <TimelineGrid
1531
+ range={range}
1532
+ groups={groups}
1533
+ now={now}
1534
+ computedWindow={{ start: range.start, end: range.end }}
1535
+ servedWindow={{ start: range.start, end: range.end }}
1536
+ showGroupHeaders={false}
1537
+ collapsedGroupKeys={new Set<string>()}
1538
+ onToggleGroup={() => {}}
1539
+ highlightedUserId={null}
1540
+ onToggleHighlight={() => {}}
1541
+ />
1542
+ </MemoryRouter>
1543
+ );
1544
+ };
1545
+
1546
+ // Thursday of this week: someone is on call now.
1547
+ const { rerender } = render(
1548
+ renderGrid(thisWeek, new Date("2026-09-17T12:00:00.000Z")),
1549
+ );
1550
+
1551
+ expect(
1552
+ screen.getAllByTestId("timeline-on-call-now").length,
1553
+ ).toBeGreaterThan(0);
1554
+
1555
+ // Page back; the clock is still Thursday.
1556
+ rerender(renderGrid(lastWeek, new Date("2026-09-17T12:00:00.000Z")));
1557
+
1558
+ // Come back to "this" week after the clock has moved on to the next one.
1559
+ rerender(renderGrid(thisWeek, new Date("2026-09-22T09:00:00.000Z")));
1560
+
1561
+ // The rows now describe the week, not a "now" inside it...
1562
+ expect(screen.queryAllByTestId("timeline-on-call-now")).toHaveLength(0);
1563
+ expect(screen.queryAllByTestId("timeline-uncovered-now")).toHaveLength(0);
1564
+
1565
+ /*
1566
+ * ...and the only active bar is the one that really contains the new
1567
+ * now: Bob's shift runs from Thursday into next week.
1568
+ */
1569
+ expect(
1570
+ screen
1571
+ .getAllByTestId("timeline-shift-bar")
1572
+ .filter((bar: HTMLElement) => {
1573
+ return bar.getAttribute("data-active") === "true";
1574
+ })
1575
+ .map((bar: HTMLElement) => {
1576
+ return bar.getAttribute("data-user-id");
1577
+ }),
1578
+ ).toEqual(["u-bob"]);
1579
+ });
1580
+ });