@oneuptime/common 11.3.28 → 11.4.1

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 (244) hide show
  1. package/Models/DatabaseModels/AIRun.ts +50 -0
  2. package/Models/DatabaseModels/Project.ts +60 -0
  3. package/Server/API/AIAgentTaskAPI.ts +10 -24
  4. package/Server/API/AIInvestigationAPI.ts +148 -0
  5. package/Server/API/TelemetryAPI.ts +4 -3
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  10. package/Server/Services/AIAgentTaskService.ts +62 -0
  11. package/Server/Services/AlertService.ts +25 -0
  12. package/Server/Services/AnalyticsDatabaseService.ts +27 -0
  13. package/Server/Services/IncidentService.ts +25 -0
  14. package/Server/Services/IncidentStateTimelineService.ts +19 -0
  15. package/Server/Services/LogAggregationService.ts +29 -11
  16. package/Server/Services/MetricService.ts +603 -13
  17. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
  18. package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
  19. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
  20. package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
  21. package/Server/Utils/AI/Sentinel/README.md +10 -0
  22. package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
  23. package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
  24. package/Server/Utils/AI/Toolbox/Index.ts +11 -0
  25. package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
  26. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +44 -0
  27. package/Server/Utils/Monitor/MonitorMetricUtil.ts +108 -2
  28. package/Tests/Server/API/BaseAPI.test.ts +1 -0
  29. package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +146 -0
  30. package/Tests/Server/Services/LogAggregationService.test.ts +22 -0
  31. package/Tests/Server/Services/MetricServiceAggregate.test.ts +194 -0
  32. package/Tests/Types/AI/AIAgentTaskStatus.test.ts +86 -0
  33. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +127 -0
  34. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.test.ts +152 -0
  35. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
  36. package/Tests/Types/Trace/TraceRecordingRuleDefinition.test.ts +308 -0
  37. package/Tests/UI/Components/Dropdown.test.tsx +17 -0
  38. package/Tests/UI/Components/EntityDropdown.test.tsx +130 -0
  39. package/Tests/UI/Components/Modal.test.tsx +144 -2
  40. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +90 -0
  41. package/Tests/UI/Utils/Theme.test.ts +161 -0
  42. package/Types/BaseDatabase/AggregateBy.ts +12 -0
  43. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  44. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  45. package/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.ts +19 -0
  46. package/Types/Dashboard/DashboardTemplates.ts +9 -1
  47. package/Types/Date.ts +46 -0
  48. package/Types/Monitor/CriteriaFilter.ts +4 -0
  49. package/Types/Monitor/HttpPhaseTimings.ts +16 -0
  50. package/Types/Monitor/MonitorMetricType.ts +17 -0
  51. package/Types/Monitor/PingMonitor/PingMonitorResponse.ts +20 -0
  52. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
  53. package/Types/Probe/ProbeMonitorResponse.ts +14 -0
  54. package/UI/Components/Calendar/Calendar.tsx +8 -2
  55. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
  56. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -5
  57. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +4 -1
  58. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +6 -3
  59. package/UI/Components/CodeBlock/CodeBlock.tsx +2 -1
  60. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  61. package/UI/Components/Divider/Divider.tsx +1 -1
  62. package/UI/Components/Dropdown/Dropdown.tsx +13 -4
  63. package/UI/Components/EntityDropdown/EntityDropdown.tsx +110 -2
  64. package/UI/Components/ErrorBoundary.tsx +2 -1
  65. package/UI/Components/EventItem/EventItem.tsx +2 -2
  66. package/UI/Components/GanttChart/Bar/Index.tsx +1 -1
  67. package/UI/Components/Graphs/UptimeBarTooltip.tsx +30 -23
  68. package/UI/Components/Header/Header.tsx +7 -0
  69. package/UI/Components/Header/HeaderIconDropdownButton.tsx +2 -0
  70. package/UI/Components/Label/Label.tsx +2 -2
  71. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +22 -10
  72. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +9 -3
  73. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +8 -8
  74. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +44 -20
  75. package/UI/Components/Modal/ConfirmModal.tsx +1 -1
  76. package/UI/Components/Modal/Modal.tsx +213 -121
  77. package/UI/Components/Modal/ModalFooter.tsx +33 -22
  78. package/UI/Components/ModelTable/TableView.tsx +1 -1
  79. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +20 -12
  80. package/UI/Components/Pill/Pill.tsx +21 -13
  81. package/UI/Components/StatusBubble/StatusBubble.tsx +3 -3
  82. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +7 -1
  83. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +9 -3
  84. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +62 -4
  85. package/UI/Components/Workflow/Component.tsx +53 -33
  86. package/UI/Components/Workflow/ComponentPortViewer.tsx +6 -6
  87. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +8 -8
  88. package/UI/Components/Workflow/ComponentsModal.tsx +16 -9
  89. package/UI/Components/Workflow/Workflow.tsx +13 -11
  90. package/UI/Utils/Theme.ts +140 -0
  91. package/Utils/Dashboard/Components/DashboardLogChartComponent.ts +104 -0
  92. package/Utils/Dashboard/Components/Index.ts +7 -0
  93. package/Utils/Monitor/MonitorMetricType.ts +70 -2
  94. package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
  95. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  97. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  98. package/build/dist/Server/API/AIAgentTaskAPI.js +8 -23
  99. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  100. package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
  101. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
  102. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  103. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
  104. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
  105. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js.map +1 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  111. package/build/dist/Server/Services/AIAgentTaskService.js +59 -0
  112. package/build/dist/Server/Services/AIAgentTaskService.js.map +1 -1
  113. package/build/dist/Server/Services/AlertService.js +24 -0
  114. package/build/dist/Server/Services/AlertService.js.map +1 -1
  115. package/build/dist/Server/Services/AnalyticsDatabaseService.js +19 -0
  116. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  117. package/build/dist/Server/Services/IncidentService.js +24 -0
  118. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  119. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  120. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  121. package/build/dist/Server/Services/LogAggregationService.js +21 -7
  122. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  123. package/build/dist/Server/Services/MetricService.js +449 -7
  124. package/build/dist/Server/Services/MetricService.js.map +1 -1
  125. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
  126. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  127. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
  128. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
  129. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
  130. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
  131. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
  132. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
  133. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
  134. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
  135. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
  136. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
  137. package/build/dist/Server/Utils/AI/Toolbox/Index.js +6 -0
  138. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
  140. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
  141. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +29 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  143. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +86 -2
  144. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  145. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  146. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  147. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  148. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  149. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js +2 -0
  150. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js.map +1 -0
  151. package/build/dist/Types/Dashboard/DashboardTemplates.js +9 -1
  152. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  153. package/build/dist/Types/Date.js +32 -0
  154. package/build/dist/Types/Date.js.map +1 -1
  155. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  156. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  157. package/build/dist/Types/Monitor/HttpPhaseTimings.js +2 -0
  158. package/build/dist/Types/Monitor/HttpPhaseTimings.js.map +1 -0
  159. package/build/dist/Types/Monitor/MonitorMetricType.js +15 -0
  160. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  161. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js +2 -0
  162. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js.map +1 -0
  163. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
  164. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
  165. package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
  166. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  167. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  168. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  169. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +7 -4
  170. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  171. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +4 -1
  172. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  173. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +6 -3
  174. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  175. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +1 -1
  176. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  177. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +3 -1
  178. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  179. package/build/dist/UI/Components/Divider/Divider.js +1 -1
  180. package/build/dist/UI/Components/Divider/Divider.js.map +1 -1
  181. package/build/dist/UI/Components/Dropdown/Dropdown.js +10 -4
  182. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  183. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +76 -3
  184. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  185. package/build/dist/UI/Components/ErrorBoundary.js +2 -1
  186. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  187. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  188. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  189. package/build/dist/UI/Components/GanttChart/Bar/Index.js +1 -1
  190. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  191. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +29 -23
  192. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  193. package/build/dist/UI/Components/Header/Header.js +1 -0
  194. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  195. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  196. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  197. package/build/dist/UI/Components/Label/Label.js +2 -2
  198. package/build/dist/UI/Components/Label/Label.js.map +1 -1
  199. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +20 -8
  200. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  201. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +8 -2
  202. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  203. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +8 -8
  204. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +40 -20
  205. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  206. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  207. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  208. package/build/dist/UI/Components/Modal/Modal.js +109 -51
  209. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  210. package/build/dist/UI/Components/Modal/ModalFooter.js +13 -10
  211. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  212. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  213. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  214. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +17 -12
  215. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  216. package/build/dist/UI/Components/Pill/Pill.js +12 -13
  217. package/build/dist/UI/Components/Pill/Pill.js.map +1 -1
  218. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +3 -3
  219. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  220. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  221. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  222. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -2
  223. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  224. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +36 -6
  225. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  226. package/build/dist/UI/Components/Workflow/Component.js +43 -33
  227. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  228. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js +6 -6
  229. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js.map +1 -1
  230. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +8 -8
  231. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  232. package/build/dist/UI/Components/Workflow/ComponentsModal.js +15 -9
  233. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  234. package/build/dist/UI/Components/Workflow/Workflow.js +13 -11
  235. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  236. package/build/dist/UI/Utils/Theme.js +106 -0
  237. package/build/dist/UI/Utils/Theme.js.map +1 -0
  238. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js +92 -0
  239. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js.map +1 -0
  240. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  241. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  242. package/build/dist/Utils/Monitor/MonitorMetricType.js +70 -3
  243. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  244. package/package.json +1 -1
@@ -0,0 +1,257 @@
1
+ import CalendarEvent from "../Calendar/CalendarEvent";
2
+ import OneUptimeDate from "../Date";
3
+
4
+ /*
5
+ * Turns the low-level calendar events produced by LayerUtil into the
6
+ * human-readable "shifts" the on-call schedule summaries render — e.g.
7
+ * "Alice is on call from Mon 9:00 AM to Tue 9:00 AM".
8
+ *
9
+ * LayerUtil emits one CalendarEvent per contiguous coverage segment. A single
10
+ * rotation turn can therefore be split into several events (a daily 9-5
11
+ * restriction produces one event per day), and the merged multi-layer schedule
12
+ * interleaves events from different users/layers. These helpers collapse those
13
+ * raw segments back into the shifts a human thinks in, and surface the gaps
14
+ * where nobody is on call.
15
+ *
16
+ * Everything here is pure and timezone-agnostic: it operates on the absolute
17
+ * instants already resolved by LayerUtil (which itself resolves restriction
18
+ * wall-clock windows in the schedule's timezone). Rendering code decides which
19
+ * timezone to display those instants in.
20
+ */
21
+
22
+ /*
23
+ * The identity of the on-call person for an event. LayerUtil stores the user id
24
+ * in CalendarEvent.title before the UI relabels it, so callers pass the events
25
+ * straight through from getEvents/getMultiLayerEvents.
26
+ */
27
+ export interface OnCallShift {
28
+ userId: string;
29
+ start: Date;
30
+ end: Date;
31
+ /*
32
+ * Total ACTIVE on-call seconds inside this shift, i.e. the sum of the covered
33
+ * segments' durations. For a contiguous shift this equals end - start. For a
34
+ * turn merged across off-hours (mergeAcrossGaps), it is the real time on call,
35
+ * which is smaller than the wall-clock span — so the summary can show honest
36
+ * coverage ("40h across the week") instead of the misleading span ("4d 8h").
37
+ */
38
+ coverageSeconds: number;
39
+ }
40
+
41
+ export interface CoverageGap {
42
+ start: Date;
43
+ end: Date;
44
+ }
45
+
46
+ export interface CurrentAndNextShift {
47
+ current: OnCallShift | null;
48
+ next: OnCallShift | null;
49
+ }
50
+
51
+ /*
52
+ * Two coverage segments are considered part of the same continuous shift when
53
+ * the gap between them is below this threshold. LayerUtil starts each following
54
+ * segment exactly one second after the previous one ends, so anything within a
55
+ * couple of minutes is "touching"; anything larger is a genuine off-hours gap
56
+ * (a restriction window closing, a rotation with no fallback layer, etc.).
57
+ */
58
+ const CONTIGUITY_TOLERANCE_SECONDS: number = 90;
59
+
60
+ export default class ScheduleShiftUtil {
61
+ /*
62
+ * Collapse raw coverage events into shifts.
63
+ *
64
+ * - mergeAcrossGaps = false (default): only merges segments that actually
65
+ * touch, so genuine off-hours gaps remain visible. This is what the "final
66
+ * schedule" summary uses so it can honestly show when nobody is on call.
67
+ * - mergeAcrossGaps = true: merges every consecutive run of the same user
68
+ * into a single turn, absorbing the within-turn off-hours gaps. This is what
69
+ * the per-layer rotation summary uses so "Alice: Mon -> Fri" reads as one
70
+ * rotation turn instead of five separate day rows. The turn's coverageSeconds
71
+ * still reflects only the real active on-call time (e.g. 5x8h, not the full
72
+ * Mon->Fri wall-clock span), and the summary shows the layer's active-hours
73
+ * window as context.
74
+ */
75
+ public static groupEventsIntoShifts(
76
+ events: Array<CalendarEvent>,
77
+ options?: { mergeAcrossGaps?: boolean | undefined } | undefined,
78
+ ): Array<OnCallShift> {
79
+ if (!events || events.length === 0) {
80
+ return [];
81
+ }
82
+
83
+ const mergeAcrossGaps: boolean = Boolean(options?.mergeAcrossGaps);
84
+
85
+ // Work on a copy sorted by start time; never mutate the caller's array.
86
+ const sorted: Array<CalendarEvent> = [...events]
87
+ .filter((event: CalendarEvent) => {
88
+ return (
89
+ Boolean(event.title) && Boolean(event.start) && Boolean(event.end)
90
+ );
91
+ })
92
+ .sort((a: CalendarEvent, b: CalendarEvent) => {
93
+ if (OneUptimeDate.isBefore(a.start, b.start)) {
94
+ return -1;
95
+ }
96
+ if (OneUptimeDate.isAfter(a.start, b.start)) {
97
+ return 1;
98
+ }
99
+ return 0;
100
+ });
101
+
102
+ const shifts: Array<OnCallShift> = [];
103
+
104
+ for (const event of sorted) {
105
+ const userId: string = event.title;
106
+ const eventSeconds: number = OneUptimeDate.getDifferenceInSeconds(
107
+ event.end,
108
+ event.start,
109
+ );
110
+ const last: OnCallShift | undefined = shifts[shifts.length - 1];
111
+
112
+ const sameUser: boolean = Boolean(last) && last!.userId === userId;
113
+
114
+ const isContiguous: boolean =
115
+ Boolean(last) &&
116
+ OneUptimeDate.getDifferenceInSeconds(last!.end, event.start) <=
117
+ CONTIGUITY_TOLERANCE_SECONDS &&
118
+ OneUptimeDate.isOnOrBefore(last!.end, event.end);
119
+
120
+ /*
121
+ * Extend the previous shift when it belongs to the same user AND either we
122
+ * are merging whole turns, or the two segments physically touch. Only
123
+ * extend the end forward — never pull it backwards — so overlapping
124
+ * segments (possible after the multi-layer priority merge) can't shrink a
125
+ * shift. Accumulate each merged segment's own duration into coverageSeconds
126
+ * so a turn merged across off-hours reports its real active time on call.
127
+ */
128
+ if (last && sameUser && (mergeAcrossGaps || isContiguous)) {
129
+ if (OneUptimeDate.isAfter(event.end, last.end)) {
130
+ last.end = event.end;
131
+ }
132
+ last.coverageSeconds += eventSeconds;
133
+ continue;
134
+ }
135
+
136
+ shifts.push({
137
+ userId,
138
+ start: event.start,
139
+ end: event.end,
140
+ coverageSeconds: eventSeconds,
141
+ });
142
+ }
143
+
144
+ return shifts;
145
+ }
146
+
147
+ /*
148
+ * The shift that contains `now` (the person on call right now) and the first
149
+ * shift that starts after `now` (up next). Either can be null: `current` is
150
+ * null when nobody is on call at this instant (a coverage gap), and `next` is
151
+ * null when the computed window does not reach far enough to include another
152
+ * shift.
153
+ */
154
+ public static getCurrentAndNextShift(
155
+ shifts: Array<OnCallShift>,
156
+ now: Date,
157
+ ): CurrentAndNextShift {
158
+ let current: OnCallShift | null = null;
159
+ let next: OnCallShift | null = null;
160
+
161
+ for (const shift of shifts) {
162
+ const hasStarted: boolean = OneUptimeDate.isOnOrBefore(shift.start, now);
163
+ const hasNotEnded: boolean = OneUptimeDate.isAfter(shift.end, now);
164
+
165
+ if (hasStarted && hasNotEnded) {
166
+ current = shift;
167
+ continue;
168
+ }
169
+
170
+ if (OneUptimeDate.isAfter(shift.start, now)) {
171
+ if (!next || OneUptimeDate.isBefore(shift.start, next.start)) {
172
+ next = shift;
173
+ }
174
+ }
175
+ }
176
+
177
+ return { current, next };
178
+ }
179
+
180
+ /*
181
+ * Periods inside [windowStart, windowEnd] during which no shift provides
182
+ * coverage. Includes a leading gap (from windowStart until the first shift
183
+ * begins) and gaps between consecutive shifts. Trailing time after the last
184
+ * shift is intentionally NOT reported as a gap: the summary window is finite,
185
+ * so "nothing scheduled past the window" is an artifact of the window, not a
186
+ * real coverage hole.
187
+ *
188
+ * `shifts` are assumed sorted by start (as returned by groupEventsIntoShifts).
189
+ */
190
+ public static getCoverageGaps(
191
+ shifts: Array<OnCallShift>,
192
+ windowStart: Date,
193
+ windowEnd: Date,
194
+ ): Array<CoverageGap> {
195
+ const gaps: Array<CoverageGap> = [];
196
+
197
+ if (OneUptimeDate.isOnOrAfter(windowStart, windowEnd)) {
198
+ return gaps;
199
+ }
200
+
201
+ /*
202
+ * A gap only "counts" when it is longer than the contiguity tolerance, so
203
+ * the one-second boundaries LayerUtil leaves between segments never show up
204
+ * as spurious coverage holes.
205
+ */
206
+ const isRealGap: (start: Date, end: Date) => boolean = (
207
+ start: Date,
208
+ end: Date,
209
+ ): boolean => {
210
+ return (
211
+ OneUptimeDate.isAfter(end, start) &&
212
+ OneUptimeDate.getDifferenceInSeconds(end, start) >
213
+ CONTIGUITY_TOLERANCE_SECONDS
214
+ );
215
+ };
216
+
217
+ if (shifts.length === 0) {
218
+ if (isRealGap(windowStart, windowEnd)) {
219
+ gaps.push({ start: windowStart, end: windowEnd });
220
+ }
221
+ return gaps;
222
+ }
223
+
224
+ // Leading gap: nobody on call from the window start until the first shift.
225
+ const firstShift: OnCallShift = shifts[0]!;
226
+ if (
227
+ OneUptimeDate.isAfter(firstShift.start, windowStart) &&
228
+ isRealGap(windowStart, firstShift.start)
229
+ ) {
230
+ gaps.push({ start: windowStart, end: firstShift.start });
231
+ }
232
+
233
+ /*
234
+ * `coveredUntil` tracks the furthest point covered so far. Shifts can
235
+ * overlap (multi-layer merges), so we advance it monotonically and only
236
+ * record a gap when the next shift starts strictly after it.
237
+ */
238
+ let coveredUntil: Date = firstShift.end;
239
+
240
+ for (let i: number = 1; i < shifts.length; i++) {
241
+ const shift: OnCallShift = shifts[i]!;
242
+
243
+ if (
244
+ OneUptimeDate.isAfter(shift.start, coveredUntil) &&
245
+ isRealGap(coveredUntil, shift.start)
246
+ ) {
247
+ gaps.push({ start: coveredUntil, end: shift.start });
248
+ }
249
+
250
+ if (OneUptimeDate.isAfter(shift.end, coveredUntil)) {
251
+ coveredUntil = shift.end;
252
+ }
253
+ }
254
+
255
+ return gaps;
256
+ }
257
+ }
@@ -8,10 +8,13 @@ import SslMonitorResponse from "../Monitor/SSLMonitor/SslMonitorResponse";
8
8
  import SyntheticMonitorResponse from "../Monitor/SyntheticMonitors/SyntheticMonitorResponse";
9
9
  import SnmpMonitorResponse from "../Monitor/SnmpMonitor/SnmpMonitorResponse";
10
10
  import DnsMonitorResponse from "../Monitor/DnsMonitor/DnsMonitorResponse";
11
+ import PingMonitorResponse from "../Monitor/PingMonitor/PingMonitorResponse";
11
12
  import DomainMonitorResponse from "../Monitor/DomainMonitor/DomainMonitorResponse";
12
13
  import DnssecMonitorResponse from "../Monitor/DnssecMonitor/DnssecMonitorResponse";
13
14
  import ExternalStatusPageMonitorResponse from "../Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
15
+ import HttpPhaseTimings from "../Monitor/HttpPhaseTimings";
14
16
  import MonitorEvaluationSummary from "../Monitor/MonitorEvaluationSummary";
17
+ import NetworkPathTrace from "../Monitor/NetworkMonitor/NetworkPathTrace";
15
18
  import ObjectID from "../ObjectID";
16
19
  import Port from "../Port";
17
20
  import ProbeAttempt from "./ProbeAttempt";
@@ -35,6 +38,17 @@ export default interface ProbeMonitorResponse {
35
38
  syntheticMonitorResponse?: Array<SyntheticMonitorResponse> | undefined;
36
39
  customCodeMonitorResponse?: CustomCodeMonitorResponse | undefined;
37
40
  snmpResponse?: SnmpMonitorResponse | undefined;
41
+ pingResponse?: PingMonitorResponse | undefined;
42
+ /*
43
+ * Traceroute + DNS lookup captured by the probe when a Ping/IP/Port check
44
+ * fails — path evidence from the moment of failure.
45
+ */
46
+ networkPathTrace?: NetworkPathTrace | undefined;
47
+ /*
48
+ * DNS / TCP / TLS / TTFB / download phase breakdown for Website and API
49
+ * checks. Absent when the request went through a proxy.
50
+ */
51
+ httpTimings?: HttpPhaseTimings | undefined;
38
52
  dnsResponse?: DnsMonitorResponse | undefined;
39
53
  domainResponse?: DomainMonitorResponse | undefined;
40
54
  dnssecResponse?: DnssecMonitorResponse | undefined;
@@ -21,6 +21,12 @@ export interface ComponentProps {
21
21
  id?: string | undefined;
22
22
  events: Array<CalendarEvent>;
23
23
  defaultCalendarView?: DefaultCalendarView;
24
+ /*
25
+ * Which date the calendar initially opens on. Defaults to "now". Callers that
26
+ * render events shifted into a display timezone pass the same-shifted "now" so
27
+ * the grid opens on that zone's current day rather than the browser's.
28
+ */
29
+ defaultDate?: Date | undefined;
24
30
  onRangeChange: (startAndEndTime: StartAndEndTime) => void;
25
31
  }
26
32
 
@@ -38,9 +44,9 @@ const CalendarElement: FunctionComponent<ComponentProps> = (
38
44
  ): ReactElement => {
39
45
  const { defaultDate } = useMemo(() => {
40
46
  return {
41
- defaultDate: OneUptimeDate.getCurrentDate(),
47
+ defaultDate: props.defaultDate || OneUptimeDate.getCurrentDate(),
42
48
  };
43
- }, []);
49
+ }, [props.defaultDate]);
44
50
 
45
51
  const eventStyleGetter: EventPropGetter<any> = (
46
52
  event: CalendarEvent,
@@ -38,10 +38,10 @@ export interface Chart {
38
38
  exemplarPoints?: Array<ExemplarPoint> | undefined;
39
39
  onExemplarClick?: ((exemplar: ExemplarPoint) => void) | undefined;
40
40
  /**
41
- * Optional control panel rendered between the chart title and the
42
- * chart body. Used by per-series-grouped metric charts to surface a
43
- * search box, per-series toggles, and a "show all" escape hatch so
44
- * the chart stays usable at thousands of unique label combinations.
41
+ * Optional control panel rendered directly below the chart body. Used by
42
+ * per-series-grouped metric charts to surface a search box, a sort
43
+ * control, per-series toggles, and a "show all" escape hatch so the chart
44
+ * stays usable at thousands of unique label combinations.
45
45
  */
46
46
  seriesControls?: ReactElement | undefined;
47
47
  }
@@ -308,10 +308,10 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
308
308
  {chart.description}
309
309
  </p>
310
310
  )}
311
- {chart.seriesControls ? chart.seriesControls : null}
312
311
  <div className="flex-1 flex flex-col min-h-80">
313
312
  {getChartContent(chart, index)}
314
313
  </div>
314
+ {chart.seriesControls ? chart.seriesControls : null}
315
315
  </div>
316
316
  );
317
317
  })}
@@ -815,7 +815,10 @@ const AreaChart: React.ForwardRefExoticComponent<
815
815
  wrapperStyle={{ outline: "none" }}
816
816
  isAnimationActive={true}
817
817
  animationDuration={100}
818
- cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
818
+ cursor={{
819
+ stroke: "var(--ou-chart-cursor, #d1d5db)",
820
+ strokeWidth: 1,
821
+ }}
819
822
  offset={20}
820
823
  position={{ y: 0 }}
821
824
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -903,11 +906,11 @@ const AreaChart: React.ForwardRefExoticComponent<
903
906
  }
904
907
  return [low, high];
905
908
  }}
906
- stroke="#94a3b8"
909
+ stroke="var(--ou-chart-tick, #94a3b8)"
907
910
  strokeOpacity={0.3}
908
911
  strokeWidth={1}
909
912
  strokeDasharray="3 3"
910
- fill="#94a3b8"
913
+ fill="var(--ou-chart-tick, #94a3b8)"
911
914
  fillOpacity={0.12}
912
915
  isAnimationActive={false}
913
916
  connectNulls={false}
@@ -1064,7 +1067,7 @@ const AreaChart: React.ForwardRefExoticComponent<
1064
1067
  y={exemplar.y}
1065
1068
  r={5}
1066
1069
  fill="#7c3aed"
1067
- stroke="#ffffff"
1070
+ stroke="var(--ou-chart-marker-ring, #ffffff)"
1068
1071
  strokeWidth={2}
1069
1072
  style={{ cursor: "pointer" }}
1070
1073
  onClick={() => {
@@ -1074,7 +1077,7 @@ const AreaChart: React.ForwardRefExoticComponent<
1074
1077
  <Label
1075
1078
  value="E"
1076
1079
  position="center"
1077
- fill="#ffffff"
1080
+ fill="var(--ou-chart-marker-ring, #ffffff)"
1078
1081
  fontSize={8}
1079
1082
  fontWeight={700}
1080
1083
  />
@@ -925,7 +925,10 @@ const BarChart: React.ForwardRefExoticComponent<
925
925
  wrapperStyle={{ outline: "none" }}
926
926
  isAnimationActive={true}
927
927
  animationDuration={100}
928
- cursor={{ fill: "#d1d5db", opacity: "0.15" }}
928
+ cursor={{
929
+ fill: "var(--ou-chart-cursor, #d1d5db)",
930
+ opacity: "0.15",
931
+ }}
929
932
  offset={20}
930
933
  {...(layout === "horizontal"
931
934
  ? { position: { y: 0 } }
@@ -813,7 +813,10 @@ const LineChart: React.ForwardRefExoticComponent<
813
813
  wrapperStyle={{ outline: "none" }}
814
814
  isAnimationActive={true}
815
815
  animationDuration={100}
816
- cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
816
+ cursor={{
817
+ stroke: "var(--ou-chart-cursor, #d1d5db)",
818
+ strokeWidth: 1,
819
+ }}
817
820
  offset={20}
818
821
  position={{ y: 0 }}
819
822
  content={({ active, payload, label }: any) => {
@@ -1052,7 +1055,7 @@ const LineChart: React.ForwardRefExoticComponent<
1052
1055
  y={exemplar.y}
1053
1056
  r={5}
1054
1057
  fill="#7c3aed"
1055
- stroke="#ffffff"
1058
+ stroke="var(--ou-chart-marker-ring, #ffffff)"
1056
1059
  strokeWidth={2}
1057
1060
  style={{ cursor: "pointer" }}
1058
1061
  onClick={() => {
@@ -1062,7 +1065,7 @@ const LineChart: React.ForwardRefExoticComponent<
1062
1065
  <Label
1063
1066
  value="E"
1064
1067
  position="center"
1065
- fill="#ffffff"
1068
+ fill="var(--ou-chart-marker-ring, #ffffff)"
1066
1069
  fontSize={8}
1067
1070
  fontWeight={700}
1068
1071
  />
@@ -36,8 +36,9 @@ const CodeBlock: FunctionComponent<ComponentProps> = (
36
36
  {showCopyButton && typeof props.code === "string" && (
37
37
  <button
38
38
  onClick={handleCopy}
39
- className="absolute top-2 right-2 p-2 rounded-md bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white transition-all opacity-0 group-hover:opacity-100 z-10"
39
+ className="absolute top-2 right-2 p-2 rounded-md bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white transition-all opacity-70 hover:opacity-100 focus-visible:opacity-100 group-hover:opacity-100 z-10"
40
40
  title={copied ? "Copied!" : "Copy to clipboard"}
41
+ aria-label={copied ? "Copied" : "Copy to clipboard"}
41
42
  type="button"
42
43
  >
43
44
  <Icon
@@ -1,6 +1,7 @@
1
1
  import Editor from "@monaco-editor/react";
2
2
  import CodeType from "../../../Types/Code/CodeType";
3
3
  import MarkdownUtil from "../../Utils/Markdown";
4
+ import { Theme, useTheme } from "../../Utils/Theme";
4
5
  import React, {
5
6
  FunctionComponent,
6
7
  ReactElement,
@@ -36,6 +37,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
36
37
  const [placeholder, setPlaceholder] = useState<string>("");
37
38
  const [helpText, setHelpText] = useState<string | ReactElement>("");
38
39
  const editorRef: React.MutableRefObject<any> = useRef<any>(null);
40
+ const theme: Theme = useTheme();
39
41
 
40
42
  useEffect(() => {
41
43
  let value: string | undefined = props.value;
@@ -138,6 +140,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
138
140
  )}
139
141
 
140
142
  <Editor
143
+ theme={theme === Theme.Dark ? "vs-dark" : "light"}
141
144
  defaultLanguage={props.type}
142
145
  height="30vh"
143
146
  value={value}
@@ -4,7 +4,7 @@ const Divider: () => JSX.Element = (): ReactElement => {
4
4
  return (
5
5
  <div
6
6
  className="w-full border-t border-gray-100"
7
- style={{ borderColor: "#f9fafb" }}
7
+ style={{ borderColor: "var(--ou-border-subtle, #f9fafb)" }}
8
8
  ></div>
9
9
  );
10
10
  };
@@ -41,6 +41,12 @@ export interface DropdownOptionGroup {
41
41
  options: Array<DropdownOption>;
42
42
  }
43
43
 
44
+ /*
45
+ * Modal surfaces use z-50. The portalled menu must sit above that stacking
46
+ * context so options are not painted underneath an anchored modal footer.
47
+ */
48
+ export const DROPDOWN_MENU_Z_INDEX: number = 60;
49
+
44
50
  export interface ComponentProps {
45
51
  options: Array<DropdownOption | DropdownOptionGroup>;
46
52
  initialValue?: undefined | DropdownOption | Array<DropdownOption>;
@@ -334,8 +340,8 @@ const Dropdown: FunctionComponent<ComponentProps> = (
334
340
  } = (color?: string): { backgroundColor: string; color: string } => {
335
341
  if (!color) {
336
342
  return {
337
- backgroundColor: "#e5e7eb", // gray-200
338
- color: "#374151", // gray-700
343
+ backgroundColor: "var(--ou-surface-tertiary, #e5e7eb)",
344
+ color: "var(--ou-text-secondary, #374151)",
339
345
  };
340
346
  }
341
347
 
@@ -511,6 +517,9 @@ const Dropdown: FunctionComponent<ComponentProps> = (
511
517
  setValue(value);
512
518
  }, [props.value]);
513
519
 
520
+ const menuPortalTarget: HTMLElement | null =
521
+ typeof document === "undefined" ? null : document.body;
522
+
514
523
  return (
515
524
  <div
516
525
  id={props.id}
@@ -710,11 +719,11 @@ const Dropdown: FunctionComponent<ComponentProps> = (
710
719
  menuPortal: (base: CSSObjectWithLabel): CSSObjectWithLabel => {
711
720
  return {
712
721
  ...base,
713
- zIndex: 50,
722
+ zIndex: DROPDOWN_MENU_Z_INDEX,
714
723
  };
715
724
  },
716
725
  }}
717
- menuPortalTarget={document.body}
726
+ menuPortalTarget={menuPortalTarget}
718
727
  menuPosition="fixed"
719
728
  isClearable={true}
720
729
  isSearchable={true}