@oneuptime/common 12.0.4 → 12.0.5

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 (374) hide show
  1. package/Models/DatabaseModels/AIRun.ts +38 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +30 -0
  3. package/Models/DatabaseModels/Dashboard.ts +28 -0
  4. package/Models/DatabaseModels/Incident.ts +2 -0
  5. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  7. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  8. package/Models/DatabaseModels/IncidentFeed.ts +30 -0
  9. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  10. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  11. package/Models/DatabaseModels/LogSavedView.ts +33 -0
  12. package/Models/DatabaseModels/Project.ts +231 -9
  13. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  14. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  15. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
  17. package/Models/DatabaseModels/StatusPage.ts +28 -0
  18. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  19. package/Server/API/AIAgentDataAPI.ts +44 -55
  20. package/Server/API/AIInvestigationAPI.ts +175 -17
  21. package/Server/API/DashboardAPI.ts +10 -10
  22. package/Server/API/StatusPageAPI.ts +38 -19
  23. package/Server/EnvironmentConfig.ts +16 -0
  24. package/Server/Infrastructure/ClickhouseConfig.ts +15 -1
  25. package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Services/AIRunService.ts +78 -10
  33. package/Server/Services/AIService.ts +94 -22
  34. package/Server/Services/AlertFeedService.ts +5 -0
  35. package/Server/Services/AnalyticsDatabaseService.ts +29 -0
  36. package/Server/Services/DatabaseService.ts +43 -2
  37. package/Server/Services/IncidentFeedService.ts +5 -0
  38. package/Server/Services/LlmLogService.ts +46 -2
  39. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
  40. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
  41. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
  42. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
  43. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
  44. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
  45. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +223 -22
  46. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +25 -24
  47. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
  48. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
  49. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +25 -24
  50. package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
  51. package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
  52. package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
  53. package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
  54. package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
  55. package/Server/Utils/AI/SRE/README.md +1 -1
  56. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
  57. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
  58. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
  59. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
  60. package/Server/Utils/Monitor/MonitorMetricUtil.ts +46 -0
  61. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  62. package/Server/Utils/PasswordHash.ts +28 -4
  63. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
  64. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
  65. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
  66. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1184 -0
  67. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
  68. package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
  69. package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
  70. package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
  71. package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
  72. package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
  73. package/Tests/Server/API/AIInvestigationAPI.test.ts +594 -0
  74. package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
  75. package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
  76. package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
  77. package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
  78. package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
  79. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +396 -20
  80. package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
  81. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
  82. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
  83. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
  84. package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
  85. package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
  86. package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
  87. package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
  88. package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
  89. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
  90. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
  91. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
  92. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
  93. package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
  94. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
  95. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
  96. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
  97. package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
  98. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
  99. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +313 -20
  100. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  101. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
  102. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
  103. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
  104. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
  105. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
  106. package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
  107. package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
  108. package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
  109. package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
  110. package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
  111. package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
  112. package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
  113. package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
  114. package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
  115. package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
  116. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
  117. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
  118. package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
  119. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
  120. package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
  121. package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
  122. package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
  123. package/Tests/Types/Permission.test.ts +137 -0
  124. package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
  125. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
  126. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
  127. package/Tests/UI/Components/ErrorBoundary.test.tsx +158 -0
  128. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
  129. package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
  130. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
  131. package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
  132. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
  133. package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
  134. package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
  135. package/Tests/UI/Utils/Project.test.ts +31 -0
  136. package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
  137. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
  138. package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
  139. package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
  140. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
  141. package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
  142. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
  143. package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
  144. package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
  145. package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
  146. package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
  147. package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
  148. package/Types/AI/CodeFixTaskContext.ts +63 -3
  149. package/Types/AI/CodeFixTaskType.ts +3 -3
  150. package/Types/HashedString.ts +4 -3
  151. package/Types/Monitor/CriteriaFilter.ts +4 -0
  152. package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
  153. package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
  155. package/Types/Monitor/MonitorMetricType.ts +8 -0
  156. package/Types/Monitor/MonitorStep.ts +10 -1
  157. package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
  158. package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
  159. package/Types/Permission.ts +23 -175
  160. package/Types/Probe/ProbeMonitorResponse.ts +6 -0
  161. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
  162. package/UI/Components/ErrorBoundary.tsx +138 -11
  163. package/UI/Components/Feed/FeedItem.tsx +9 -2
  164. package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
  165. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  166. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
  167. package/UI/Components/MoreMenu/MoreMenu.tsx +285 -97
  168. package/UI/Components/MoreMenu/MoreMenuItem.tsx +8 -5
  169. package/UI/Utils/DownloadFile.ts +16 -8
  170. package/UI/Utils/ErrorSupportBundle.ts +805 -0
  171. package/UI/Utils/ModelAPI/ModelAPI.ts +19 -8
  172. package/UI/Utils/Project.ts +4 -0
  173. package/UI/Utils/StatusPage.ts +8 -2
  174. package/UI/Utils/UseDashboardGridDnd.ts +917 -0
  175. package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
  176. package/Utils/Dashboard/GridLayout.ts +523 -0
  177. package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
  178. package/Utils/Monitor/MonitorMetricType.ts +44 -2
  179. package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
  180. package/Utils/StatusPage/GroupTree.ts +157 -66
  181. package/Utils/StatusPage/ResourceUptime.ts +37 -2
  182. package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
  183. package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
  184. package/build/dist/Models/DatabaseModels/AIRun.js +39 -1
  185. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
  187. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
  189. package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  191. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  193. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  195. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  197. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
  199. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  201. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  203. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
  205. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/Project.js +241 -11
  207. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  209. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  211. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  212. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  213. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  214. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
  215. package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
  216. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  218. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/AIInvestigationAPI.js +111 -11
  222. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +8 -5
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/StatusPageAPI.js +29 -11
  226. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  227. package/build/dist/Server/EnvironmentConfig.js +14 -0
  228. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  229. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +12 -1
  230. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
  232. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  245. package/build/dist/Server/Services/AIRunService.js +58 -12
  246. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  247. package/build/dist/Server/Services/AIService.js +74 -21
  248. package/build/dist/Server/Services/AIService.js.map +1 -1
  249. package/build/dist/Server/Services/AlertFeedService.js +3 -0
  250. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +29 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/DatabaseService.js +21 -2
  254. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  255. package/build/dist/Server/Services/IncidentFeedService.js +3 -0
  256. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  257. package/build/dist/Server/Services/LlmLogService.js +22 -1
  258. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  259. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  260. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  261. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
  262. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
  263. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
  264. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  265. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
  266. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  267. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
  268. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  269. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
  270. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  271. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +156 -28
  272. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  273. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +21 -19
  274. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  275. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
  276. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  277. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
  278. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  279. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +21 -19
  280. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  281. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
  282. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
  283. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
  284. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  285. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
  286. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  287. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
  288. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
  289. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
  290. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
  291. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
  292. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  293. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
  294. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  295. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
  296. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  297. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
  298. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  299. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  300. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  301. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  302. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  303. package/build/dist/Server/Utils/PasswordHash.js +25 -4
  304. package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
  305. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
  306. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
  307. package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
  308. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  309. package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
  310. package/build/dist/Types/HashedString.js +4 -3
  311. package/build/dist/Types/HashedString.js.map +1 -1
  312. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  313. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  314. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
  315. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
  316. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
  317. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  318. package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
  319. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  320. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  321. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  322. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
  323. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
  324. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
  325. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
  326. package/build/dist/Types/Permission.js +23 -155
  327. package/build/dist/Types/Permission.js.map +1 -1
  328. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
  329. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  330. package/build/dist/UI/Components/ErrorBoundary.js +67 -8
  331. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  332. package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
  333. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  334. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
  335. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  336. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  338. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
  339. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  340. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +193 -65
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +1 -1
  343. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  344. package/build/dist/UI/Utils/DownloadFile.js +15 -8
  345. package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
  346. package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
  347. package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
  348. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -8
  349. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  350. package/build/dist/UI/Utils/Project.js +3 -0
  351. package/build/dist/UI/Utils/Project.js.map +1 -1
  352. package/build/dist/UI/Utils/StatusPage.js +4 -4
  353. package/build/dist/UI/Utils/StatusPage.js.map +1 -1
  354. package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
  355. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
  356. package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
  357. package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
  358. package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
  359. package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
  360. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
  361. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
  362. package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
  363. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  364. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
  365. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
  366. package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
  367. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
  368. package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
  369. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  370. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
  371. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
  372. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
  373. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
  374. package/package.json +1 -1
@@ -0,0 +1,917 @@
1
+ import GridLayoutUtil, { GridRect } from "../../Utils/Dashboard/GridLayout";
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from "react";
9
+
10
+ /**
11
+ * Drag-and-drop + resize controller for the dashboard grid.
12
+ *
13
+ * Design notes, because the split of responsibilities is what makes this
14
+ * feel smooth:
15
+ *
16
+ * - The ACTIVE widget's DOM element is driven imperatively (transform /
17
+ * width / height updated in a requestAnimationFrame loop). React never
18
+ * repositions it mid-gesture, so charts and tables inside it are not
19
+ * re-rendered 60 times a second.
20
+ * - Everything else — the snapped drop placeholder and neighbours being
21
+ * pushed out of the way — renders through React state, but that state
22
+ * only changes when the SNAPPED grid cell changes (a few times per
23
+ * gesture), never per pointer event.
24
+ * - The preview layout is always recomputed from the layout as it was when
25
+ * the gesture started (`moveRect`/`resizeRect` are pure), so wiggling the
26
+ * pointer back and forth can never accumulate stray pushes.
27
+ * - On drop or cancel the element is "settled" imperatively to its final
28
+ * rect with transitions enabled, giving a snap-into-place animation, and
29
+ * React then renders the exact same values so there is nothing to fight
30
+ * over.
31
+ */
32
+
33
+ export type ResizeDirection = "n" | "s" | "e" | "w" | "ne" | "nw" | "se" | "sw";
34
+
35
+ export type GridDndMode = "move" | "resize";
36
+
37
+ export interface GridDndMetrics {
38
+ /** Width (= height) of one dashboard unit, in px. */
39
+ unitSizeInPx: number;
40
+ /** Gap between units, in px. */
41
+ gapInPx: number;
42
+ columns: number;
43
+ }
44
+
45
+ export interface DashboardGridDndProps {
46
+ /** Committed layout, one rect per widget. */
47
+ items: Array<GridRect>;
48
+ metrics: GridDndMetrics;
49
+ isEnabled: boolean;
50
+ /** The positioning container widgets are absolutely placed in. */
51
+ canvasRef: React.RefObject<HTMLElement>;
52
+ onCommit: (rects: Array<GridRect>) => void;
53
+ /** Pointer went down and came up without dragging — treat as a click. */
54
+ onItemClick?: ((id: string) => void) | undefined;
55
+ }
56
+
57
+ export interface DashboardGridDndResult {
58
+ /**
59
+ * Rects to render each widget at. While a gesture is active the active
60
+ * widget stays pinned at its origin (its DOM node is moved imperatively)
61
+ * and neighbours reflect the live push-preview.
62
+ */
63
+ rects: Array<GridRect>;
64
+ /** Snapped destination of the active widget — render the drop ghost here. */
65
+ placeholderRect: GridRect | null;
66
+ /** Rows needed to contain the live layout (including the placeholder). */
67
+ totalRows: number;
68
+ activeId: string | null;
69
+ activeMode: GridDndMode | null;
70
+ /** True once the pointer has actually moved past the drag threshold. */
71
+ isGestureActive: boolean;
72
+ registerItemElement: (id: string, element: HTMLElement | null) => void;
73
+ startMove: (id: string, event: React.PointerEvent) => void;
74
+ startResize: (
75
+ id: string,
76
+ direction: ResizeDirection,
77
+ event: React.PointerEvent,
78
+ ) => void;
79
+ }
80
+
81
+ /** Pointer must travel this many px before a press becomes a drag. */
82
+ const DRAG_THRESHOLD_IN_PX: number = 5;
83
+
84
+ /** Distance from the scroll-viewport edge where auto-scroll kicks in. */
85
+ const AUTO_SCROLL_EDGE_IN_PX: number = 72;
86
+
87
+ /** Fastest auto-scroll speed, px per frame. */
88
+ const AUTO_SCROLL_MAX_SPEED_IN_PX: number = 28;
89
+
90
+ /**
91
+ * How many rows past the current board bottom a single gesture may reach.
92
+ * Without a cap, dragging against the bottom edge auto-scrolls forever:
93
+ * the container grows to fit the candidate, which mints fresh scroll room,
94
+ * which moves the candidate further down. Committing near the cap raises
95
+ * the board bottom, so taller boards remain reachable across gestures.
96
+ */
97
+ const AUTO_GROW_SLACK_ROWS: number = 6;
98
+
99
+ /**
100
+ * Transition the canvas should put on every idle widget wrapper. The hook
101
+ * writes the exact same string when settling a dropped widget, so React's
102
+ * subsequent render is guaranteed to be a no-op style-wise.
103
+ */
104
+ export const GRID_ITEM_TRANSITION: string =
105
+ "transform 0.22s cubic-bezier(0.2, 0, 0, 1), width 0.22s cubic-bezier(0.2, 0, 0, 1), height 0.22s cubic-bezier(0.2, 0, 0, 1)";
106
+
107
+ interface PxRect {
108
+ x: number;
109
+ y: number;
110
+ width: number;
111
+ height: number;
112
+ }
113
+
114
+ interface GestureSession {
115
+ id: string;
116
+ mode: GridDndMode;
117
+ direction: ResizeDirection | null;
118
+ pointerId: number;
119
+ /** Normalized snapshot of the layout when the gesture began. */
120
+ originRects: Array<GridRect>;
121
+ originRect: GridRect;
122
+ /** Pointer position, canvas-relative, at gesture start. */
123
+ startCanvasX: number;
124
+ startCanvasY: number;
125
+ /** Pointer position, client coords, at gesture start (for threshold). */
126
+ startClientX: number;
127
+ startClientY: number;
128
+ latestClientX: number;
129
+ latestClientY: number;
130
+ /**
131
+ * Where inside the widget the pointer grabbed it, as a FRACTION of the
132
+ * widget's size — fractions survive mid-drag zoom/resize of the canvas,
133
+ * frozen pixel offsets do not.
134
+ */
135
+ grabFracX: number;
136
+ grabFracY: number;
137
+ /** Lowest row edge this gesture is allowed to reach. */
138
+ maxBottomUnits: number;
139
+ started: boolean;
140
+ /** Snapped candidate rect currently previewed. */
141
+ candidate: GridRect;
142
+ rafId: number | null;
143
+ /**
144
+ * Removes exactly the window listeners this session added. Stored on the
145
+ * session because later renders create new handler instances — any
146
+ * endGesture closure must detach the ORIGINAL ones.
147
+ */
148
+ detach: (() => void) | null;
149
+ }
150
+
151
+ interface PreviewState {
152
+ rects: Array<GridRect>;
153
+ activeRect: GridRect;
154
+ }
155
+
156
+ interface GestureState {
157
+ id: string;
158
+ mode: GridDndMode;
159
+ started: boolean;
160
+ }
161
+
162
+ type UseDashboardGridDndFunction = (
163
+ props: DashboardGridDndProps,
164
+ ) => DashboardGridDndResult;
165
+
166
+ const useDashboardGridDnd: UseDashboardGridDndFunction = (
167
+ props: DashboardGridDndProps,
168
+ ): DashboardGridDndResult => {
169
+ const [preview, setPreview] = useState<PreviewState | null>(null);
170
+ const [gesture, setGesture] = useState<GestureState | null>(null);
171
+
172
+ const sessionRef: React.MutableRefObject<GestureSession | null> =
173
+ useRef<GestureSession | null>(null);
174
+ const elementsRef: React.MutableRefObject<Map<string, HTMLElement>> = useRef<
175
+ Map<string, HTMLElement>
176
+ >(new Map<string, HTMLElement>());
177
+
178
+ // Live mirrors so the imperative handlers never see stale props.
179
+ const propsRef: React.MutableRefObject<DashboardGridDndProps> =
180
+ useRef<DashboardGridDndProps>(props);
181
+ propsRef.current = props;
182
+
183
+ const registerItemElement: (id: string, element: HTMLElement | null) => void =
184
+ useCallback((id: string, element: HTMLElement | null): void => {
185
+ if (element) {
186
+ elementsRef.current.set(id, element);
187
+ } else {
188
+ elementsRef.current.delete(id);
189
+ }
190
+ }, []);
191
+
192
+ // ── px helpers ────────────────────────────────────────────────────────
193
+
194
+ type CellSizeFunction = () => number;
195
+ const getCellSize: CellSizeFunction = (): number => {
196
+ const m: GridDndMetrics = propsRef.current.metrics;
197
+ return m.unitSizeInPx + m.gapInPx;
198
+ };
199
+
200
+ type UnitsToPxFunction = (units: number) => number;
201
+ const unitsToPositionPx: UnitsToPxFunction = (units: number): number => {
202
+ return units * getCellSize();
203
+ };
204
+ const unitsToSizePx: UnitsToPxFunction = (units: number): number => {
205
+ const m: GridDndMetrics = propsRef.current.metrics;
206
+ return units * m.unitSizeInPx + (units - 1) * m.gapInPx;
207
+ };
208
+
209
+ type RectToPxFunction = (rect: GridRect) => PxRect;
210
+ const rectToPx: RectToPxFunction = (rect: GridRect): PxRect => {
211
+ return {
212
+ x: unitsToPositionPx(rect.left),
213
+ y: unitsToPositionPx(rect.top),
214
+ width: unitsToSizePx(rect.width),
215
+ height: unitsToSizePx(rect.height),
216
+ };
217
+ };
218
+
219
+ // ── element styling ───────────────────────────────────────────────────
220
+
221
+ type ApplyPxFunction = (element: HTMLElement, px: PxRect) => void;
222
+ const applyPxRect: ApplyPxFunction = (
223
+ element: HTMLElement,
224
+ px: PxRect,
225
+ ): void => {
226
+ element.style.transform = `translate(${px.x}px, ${px.y}px)`;
227
+ element.style.width = `${px.width}px`;
228
+ element.style.height = `${px.height}px`;
229
+ };
230
+
231
+ type SettleFunction = (id: string, rect: GridRect) => void;
232
+ const settleElement: SettleFunction = (id: string, rect: GridRect): void => {
233
+ const element: HTMLElement | undefined = elementsRef.current.get(id);
234
+ if (!element) {
235
+ return;
236
+ }
237
+ element.style.transition = GRID_ITEM_TRANSITION;
238
+ element.style.willChange = "";
239
+ applyPxRect(element, rectToPx(rect));
240
+ /*
241
+ * Keep the card elevated while it glides into place; dropping the
242
+ * z-index immediately would paint it underneath the neighbours it is
243
+ * still crossing. Clear only after the settle transition, and only if
244
+ * no new gesture grabbed the same card in the meantime.
245
+ */
246
+ window.setTimeout(() => {
247
+ const settled: HTMLElement | undefined = elementsRef.current.get(id);
248
+ if (settled && sessionRef.current?.id !== id) {
249
+ settled.style.zIndex = "";
250
+ }
251
+ }, 260);
252
+ };
253
+
254
+ // ── auto-scroll ───────────────────────────────────────────────────────
255
+
256
+ type FindScrollParentFunction = (
257
+ element: HTMLElement | null,
258
+ ) => HTMLElement | null;
259
+ const findScrollParent: FindScrollParentFunction = (
260
+ element: HTMLElement | null,
261
+ ): HTMLElement | null => {
262
+ let current: HTMLElement | null = element?.parentElement || null;
263
+ while (current) {
264
+ const style: CSSStyleDeclaration = window.getComputedStyle(current);
265
+ const overflowY: string = style.overflowY;
266
+ const isScrollable: boolean =
267
+ (overflowY === "auto" || overflowY === "scroll") &&
268
+ current.scrollHeight > current.clientHeight;
269
+ if (isScrollable) {
270
+ return current;
271
+ }
272
+ current = current.parentElement;
273
+ }
274
+ return null;
275
+ };
276
+
277
+ type AutoScrollFunction = (clientY: number) => void;
278
+ const autoScrollIfNearEdge: AutoScrollFunction = (clientY: number): void => {
279
+ const canvas: HTMLElement | null = propsRef.current.canvasRef.current;
280
+ const scrollParent: HTMLElement | null = findScrollParent(canvas);
281
+
282
+ let viewportTop: number = 0;
283
+ let viewportBottom: number = window.innerHeight;
284
+
285
+ if (scrollParent) {
286
+ const rect: DOMRect = scrollParent.getBoundingClientRect();
287
+ viewportTop = Math.max(rect.top, 0);
288
+ viewportBottom = Math.min(rect.bottom, window.innerHeight);
289
+ }
290
+
291
+ let speed: number = 0;
292
+
293
+ const distanceFromTop: number = clientY - viewportTop;
294
+ const distanceFromBottom: number = viewportBottom - clientY;
295
+
296
+ if (distanceFromTop < AUTO_SCROLL_EDGE_IN_PX) {
297
+ const intensity: number =
298
+ 1 - Math.max(distanceFromTop, 0) / AUTO_SCROLL_EDGE_IN_PX;
299
+ speed = -Math.ceil(intensity * AUTO_SCROLL_MAX_SPEED_IN_PX);
300
+ } else if (distanceFromBottom < AUTO_SCROLL_EDGE_IN_PX) {
301
+ const intensity: number =
302
+ 1 - Math.max(distanceFromBottom, 0) / AUTO_SCROLL_EDGE_IN_PX;
303
+ speed = Math.ceil(intensity * AUTO_SCROLL_MAX_SPEED_IN_PX);
304
+ }
305
+
306
+ if (speed === 0) {
307
+ return;
308
+ }
309
+
310
+ if (scrollParent) {
311
+ scrollParent.scrollTop += speed;
312
+ } else {
313
+ window.scrollBy(0, speed);
314
+ }
315
+ };
316
+
317
+ // ── candidate computation ─────────────────────────────────────────────
318
+
319
+ /**
320
+ * Raw (unsnapped) px rect for the active widget, following the pointer.
321
+ * This is what the element visually tracks; the snapped candidate is
322
+ * derived from it.
323
+ */
324
+ type ComputeRawRectFunction = (session: GestureSession) => PxRect;
325
+ const computeRawRect: ComputeRawRectFunction = (
326
+ session: GestureSession,
327
+ ): PxRect => {
328
+ const canvas: HTMLElement | null = propsRef.current.canvasRef.current;
329
+ const metrics: GridDndMetrics = propsRef.current.metrics;
330
+ const canvasRect: DOMRect | null = canvas
331
+ ? canvas.getBoundingClientRect()
332
+ : null;
333
+
334
+ const pointerCanvasX: number = canvasRect
335
+ ? session.latestClientX - canvasRect.left
336
+ : session.latestClientX;
337
+ const pointerCanvasY: number = canvasRect
338
+ ? session.latestClientY - canvasRect.top
339
+ : session.latestClientY;
340
+
341
+ const origin: PxRect = rectToPx(session.originRect);
342
+ const boardWidth: number = unitsToSizePx(metrics.columns);
343
+
344
+ if (session.mode === "move") {
345
+ const grabOffsetX: number = session.grabFracX * origin.width;
346
+ const grabOffsetY: number = session.grabFracY * origin.height;
347
+ const maxTopUnits: number = Math.max(
348
+ session.maxBottomUnits - session.originRect.height,
349
+ 0,
350
+ );
351
+ let x: number = pointerCanvasX - grabOffsetX;
352
+ let y: number = pointerCanvasY - grabOffsetY;
353
+ x = Math.min(Math.max(x, 0), Math.max(boardWidth - origin.width, 0));
354
+ y = Math.min(Math.max(y, 0), unitsToPositionPx(maxTopUnits));
355
+ return { x, y, width: origin.width, height: origin.height };
356
+ }
357
+
358
+ // Resize: apply the pointer delta to the grabbed edge(s).
359
+ const dx: number = pointerCanvasX - session.startCanvasX;
360
+ const dy: number = pointerCanvasY - session.startCanvasY;
361
+
362
+ const direction: ResizeDirection = session.direction || "se";
363
+
364
+ const minWidthPx: number = unitsToSizePx(
365
+ Math.min(session.originRect.minWidth, metrics.columns),
366
+ );
367
+ const minHeightPx: number = unitsToSizePx(session.originRect.minHeight);
368
+
369
+ let x: number = origin.x;
370
+ let y: number = origin.y;
371
+ let width: number = origin.width;
372
+ let height: number = origin.height;
373
+
374
+ const right: number = origin.x + origin.width;
375
+ const bottom: number = origin.y + origin.height;
376
+
377
+ if (direction.includes("e")) {
378
+ width = Math.min(Math.max(origin.width + dx, minWidthPx), boardWidth - x);
379
+ }
380
+ if (direction.includes("s")) {
381
+ const maxHeightPx: number = unitsToSizePx(
382
+ Math.max(
383
+ session.maxBottomUnits - session.originRect.top,
384
+ session.originRect.minHeight,
385
+ ),
386
+ );
387
+ height = Math.min(Math.max(origin.height + dy, minHeightPx), maxHeightPx);
388
+ }
389
+ if (direction.includes("w")) {
390
+ x = Math.min(Math.max(origin.x + dx, 0), right - minWidthPx);
391
+ width = right - x;
392
+ }
393
+ if (direction.includes("n")) {
394
+ y = Math.min(Math.max(origin.y + dy, 0), bottom - minHeightPx);
395
+ height = bottom - y;
396
+ }
397
+
398
+ return { x, y, width, height };
399
+ };
400
+
401
+ /** Snap a raw px rect onto the grid, clamped into bounds. */
402
+ type SnapFunction = (session: GestureSession, raw: PxRect) => GridRect;
403
+ const snapToGrid: SnapFunction = (
404
+ session: GestureSession,
405
+ raw: PxRect,
406
+ ): GridRect => {
407
+ const metrics: GridDndMetrics = propsRef.current.metrics;
408
+ const cell: number = getCellSize();
409
+
410
+ const sizeToUnits: (px: number) => number = (px: number): number => {
411
+ return Math.max(1, Math.round((px + metrics.gapInPx) / cell));
412
+ };
413
+
414
+ if (session.mode === "move") {
415
+ const maxTopUnits: number = Math.max(
416
+ session.maxBottomUnits - session.originRect.height,
417
+ 0,
418
+ );
419
+ return GridLayoutUtil.clampRect(
420
+ {
421
+ ...session.originRect,
422
+ left: Math.round(raw.x / cell),
423
+ top: Math.min(Math.round(raw.y / cell), maxTopUnits),
424
+ },
425
+ metrics.columns,
426
+ );
427
+ }
428
+
429
+ const direction: ResizeDirection = session.direction || "se";
430
+
431
+ let left: number = session.originRect.left;
432
+ let top: number = session.originRect.top;
433
+ let width: number = session.originRect.width;
434
+ let height: number = session.originRect.height;
435
+
436
+ const rightEdge: number =
437
+ session.originRect.left + session.originRect.width;
438
+ const bottomEdge: number =
439
+ session.originRect.top + session.originRect.height;
440
+
441
+ if (direction.includes("e")) {
442
+ width = sizeToUnits(raw.width);
443
+ }
444
+ if (direction.includes("s")) {
445
+ height = sizeToUnits(raw.height);
446
+ }
447
+ if (direction.includes("w")) {
448
+ width = sizeToUnits(raw.width);
449
+ left = rightEdge - width;
450
+ }
451
+ if (direction.includes("n")) {
452
+ height = sizeToUnits(raw.height);
453
+ top = bottomEdge - height;
454
+ }
455
+
456
+ return GridLayoutUtil.clampRect(
457
+ { ...session.originRect, left, top, width, height },
458
+ metrics.columns,
459
+ );
460
+ };
461
+
462
+ // ── preview recompute (only when the snapped cell changes) ────────────
463
+
464
+ type RefreshPreviewFunction = (session: GestureSession) => void;
465
+ const refreshPreview: RefreshPreviewFunction = (
466
+ session: GestureSession,
467
+ ): void => {
468
+ const raw: PxRect = computeRawRect(session);
469
+ const candidate: GridRect = snapToGrid(session, raw);
470
+
471
+ if (GridLayoutUtil.areRectsAtSamePlace(candidate, session.candidate)) {
472
+ return;
473
+ }
474
+
475
+ session.candidate = candidate;
476
+
477
+ let nextRects: Array<GridRect>;
478
+
479
+ if (session.mode === "move") {
480
+ nextRects = GridLayoutUtil.moveRect({
481
+ rects: session.originRects,
482
+ id: session.id,
483
+ left: candidate.left,
484
+ top: candidate.top,
485
+ columns: propsRef.current.metrics.columns,
486
+ });
487
+ } else {
488
+ nextRects = GridLayoutUtil.resizeRect({
489
+ rects: session.originRects,
490
+ id: session.id,
491
+ left: candidate.left,
492
+ top: candidate.top,
493
+ width: candidate.width,
494
+ height: candidate.height,
495
+ columns: propsRef.current.metrics.columns,
496
+ });
497
+ }
498
+
499
+ setPreview({ rects: nextRects, activeRect: candidate });
500
+ };
501
+
502
+ // ── frame loop ────────────────────────────────────────────────────────
503
+
504
+ type FrameFunction = () => void;
505
+ const onFrame: FrameFunction = (): void => {
506
+ const session: GestureSession | null = sessionRef.current;
507
+ if (!session) {
508
+ return;
509
+ }
510
+
511
+ if (session.started) {
512
+ autoScrollIfNearEdge(session.latestClientY);
513
+
514
+ const raw: PxRect = computeRawRect(session);
515
+ const element: HTMLElement | undefined = elementsRef.current.get(
516
+ session.id,
517
+ );
518
+ if (element) {
519
+ applyPxRect(element, raw);
520
+ }
521
+
522
+ refreshPreview(session);
523
+ }
524
+
525
+ session.rafId = window.requestAnimationFrame(onFrame);
526
+ };
527
+
528
+ // ── gesture lifecycle ─────────────────────────────────────────────────
529
+
530
+ type ActivateFunction = (session: GestureSession) => void;
531
+ const activateGesture: ActivateFunction = (session: GestureSession): void => {
532
+ session.started = true;
533
+
534
+ const element: HTMLElement | undefined = elementsRef.current.get(
535
+ session.id,
536
+ );
537
+ if (element) {
538
+ element.style.transition = "none";
539
+ element.style.willChange = "transform, width, height";
540
+ element.style.zIndex = "60";
541
+ }
542
+
543
+ document.body.style.userSelect = "none";
544
+ document.body.style.cursor =
545
+ session.mode === "move"
546
+ ? "grabbing"
547
+ : getResizeCursor(session.direction || "se");
548
+
549
+ setGesture({ id: session.id, mode: session.mode, started: true });
550
+ // Show the placeholder at the origin right away.
551
+ setPreview({
552
+ rects: session.originRects,
553
+ activeRect: session.candidate,
554
+ });
555
+ };
556
+
557
+ type EndFunction = (commit: boolean) => void;
558
+ const endGesture: EndFunction = (commit: boolean): void => {
559
+ const session: GestureSession | null = sessionRef.current;
560
+ if (!session) {
561
+ return;
562
+ }
563
+
564
+ if (session.rafId !== null) {
565
+ window.cancelAnimationFrame(session.rafId);
566
+ }
567
+
568
+ if (session.detach) {
569
+ session.detach();
570
+ }
571
+
572
+ document.body.style.userSelect = "";
573
+ document.body.style.cursor = "";
574
+
575
+ sessionRef.current = null;
576
+
577
+ const wasStarted: boolean = session.started;
578
+
579
+ if (!wasStarted) {
580
+ // Press-and-release without movement: a click.
581
+ setGesture(null);
582
+ setPreview(null);
583
+ /*
584
+ * Only a press on the widget body counts as a click. A press on a
585
+ * resize handle that never moved must not open the settings modal —
586
+ * otherwise the same press behaves differently depending on whether
587
+ * the pointer jittered a single pixel.
588
+ */
589
+ if (commit && session.mode === "move" && propsRef.current.onItemClick) {
590
+ propsRef.current.onItemClick(session.id);
591
+ }
592
+ return;
593
+ }
594
+
595
+ if (commit) {
596
+ const finalRects: Array<GridRect> =
597
+ session.mode === "move"
598
+ ? GridLayoutUtil.moveRect({
599
+ rects: session.originRects,
600
+ id: session.id,
601
+ left: session.candidate.left,
602
+ top: session.candidate.top,
603
+ columns: propsRef.current.metrics.columns,
604
+ })
605
+ : GridLayoutUtil.resizeRect({
606
+ rects: session.originRects,
607
+ id: session.id,
608
+ left: session.candidate.left,
609
+ top: session.candidate.top,
610
+ width: session.candidate.width,
611
+ height: session.candidate.height,
612
+ columns: propsRef.current.metrics.columns,
613
+ });
614
+
615
+ const finalRect: GridRect =
616
+ finalRects.find((r: GridRect) => {
617
+ return r.id === session.id;
618
+ }) || session.originRect;
619
+
620
+ settleElement(session.id, finalRect);
621
+ setGesture(null);
622
+ setPreview(null);
623
+
624
+ if (!GridLayoutUtil.areLayoutsEqual(finalRects, propsRef.current.items)) {
625
+ propsRef.current.onCommit(finalRects);
626
+ }
627
+ return;
628
+ }
629
+
630
+ // Cancelled: glide back to where the widget started.
631
+ settleElement(session.id, session.originRect);
632
+ setGesture(null);
633
+ setPreview(null);
634
+ };
635
+
636
+ type PointerMoveHandler = (event: PointerEvent) => void;
637
+ const onPointerMove: PointerMoveHandler = (event: PointerEvent): void => {
638
+ const session: GestureSession | null = sessionRef.current;
639
+ if (!session || event.pointerId !== session.pointerId) {
640
+ return;
641
+ }
642
+
643
+ session.latestClientX = event.clientX;
644
+ session.latestClientY = event.clientY;
645
+
646
+ if (!session.started) {
647
+ const distance: number = Math.hypot(
648
+ event.clientX - session.startClientX,
649
+ event.clientY - session.startClientY,
650
+ );
651
+ // Resize gestures are always intentional — no threshold.
652
+ if (session.mode === "resize" || distance >= DRAG_THRESHOLD_IN_PX) {
653
+ activateGesture(session);
654
+ }
655
+ }
656
+
657
+ if (session.started) {
658
+ event.preventDefault();
659
+ }
660
+ };
661
+
662
+ type PointerUpHandler = (event: PointerEvent) => void;
663
+ const onPointerUp: PointerUpHandler = (event: PointerEvent): void => {
664
+ const session: GestureSession | null = sessionRef.current;
665
+ if (!session || event.pointerId !== session.pointerId) {
666
+ return;
667
+ }
668
+ // Take one final reading so fast flicks land where they were released.
669
+ session.latestClientX = event.clientX;
670
+ session.latestClientY = event.clientY;
671
+ if (session.started) {
672
+ const raw: PxRect = computeRawRect(session);
673
+ session.candidate = snapToGrid(session, raw);
674
+ }
675
+ endGesture(true);
676
+ };
677
+
678
+ type PointerCancelHandler = (event: PointerEvent) => void;
679
+ const onPointerCancel: PointerCancelHandler = (event: PointerEvent): void => {
680
+ const session: GestureSession | null = sessionRef.current;
681
+ if (!session || event.pointerId !== session.pointerId) {
682
+ return;
683
+ }
684
+ endGesture(false);
685
+ };
686
+
687
+ type KeyDownHandler = (event: KeyboardEvent) => void;
688
+ const onKeyDown: KeyDownHandler = (event: KeyboardEvent): void => {
689
+ if (event.key === "Escape" && sessionRef.current) {
690
+ event.preventDefault();
691
+ event.stopPropagation();
692
+ endGesture(false);
693
+ }
694
+ };
695
+
696
+ type WindowBlurHandler = () => void;
697
+ const onWindowBlur: WindowBlurHandler = (): void => {
698
+ endGesture(false);
699
+ };
700
+
701
+ type StartSessionFunction = (
702
+ id: string,
703
+ mode: GridDndMode,
704
+ direction: ResizeDirection | null,
705
+ event: React.PointerEvent,
706
+ ) => void;
707
+ const startSession: StartSessionFunction = (
708
+ id: string,
709
+ mode: GridDndMode,
710
+ direction: ResizeDirection | null,
711
+ event: React.PointerEvent,
712
+ ): void => {
713
+ if (!propsRef.current.isEnabled) {
714
+ return;
715
+ }
716
+ if (sessionRef.current) {
717
+ return;
718
+ }
719
+ // Primary button / primary touch only.
720
+ if (event.button !== 0 || !event.isPrimary) {
721
+ return;
722
+ }
723
+
724
+ const items: Array<GridRect> = propsRef.current.items;
725
+ const originRects: Array<GridRect> = GridLayoutUtil.normalize(
726
+ items,
727
+ propsRef.current.metrics.columns,
728
+ );
729
+ const originRect: GridRect | undefined = originRects.find((r: GridRect) => {
730
+ return r.id === id;
731
+ });
732
+
733
+ if (!originRect) {
734
+ return;
735
+ }
736
+
737
+ event.preventDefault();
738
+ event.stopPropagation();
739
+
740
+ try {
741
+ (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);
742
+ } catch {
743
+ // jsdom / older browsers: window listeners still cover us.
744
+ }
745
+
746
+ const canvas: HTMLElement | null = propsRef.current.canvasRef.current;
747
+ const canvasRect: DOMRect | null = canvas
748
+ ? canvas.getBoundingClientRect()
749
+ : null;
750
+
751
+ const startCanvasX: number = canvasRect
752
+ ? event.clientX - canvasRect.left
753
+ : event.clientX;
754
+ const startCanvasY: number = canvasRect
755
+ ? event.clientY - canvasRect.top
756
+ : event.clientY;
757
+
758
+ const originPx: PxRect = rectToPx(originRect);
759
+
760
+ const session: GestureSession = {
761
+ id,
762
+ mode,
763
+ direction,
764
+ pointerId: event.pointerId,
765
+ originRects,
766
+ originRect,
767
+ startCanvasX,
768
+ startCanvasY,
769
+ startClientX: event.clientX,
770
+ startClientY: event.clientY,
771
+ latestClientX: event.clientX,
772
+ latestClientY: event.clientY,
773
+ grabFracX: Math.min(
774
+ Math.max((startCanvasX - originPx.x) / Math.max(originPx.width, 1), 0),
775
+ 1,
776
+ ),
777
+ grabFracY: Math.min(
778
+ Math.max((startCanvasY - originPx.y) / Math.max(originPx.height, 1), 0),
779
+ 1,
780
+ ),
781
+ maxBottomUnits:
782
+ GridLayoutUtil.requiredRows(originRects) + AUTO_GROW_SLACK_ROWS,
783
+ started: false,
784
+ candidate: originRect,
785
+ rafId: null,
786
+ detach: null,
787
+ };
788
+
789
+ sessionRef.current = session;
790
+ setGesture({ id, mode, started: false });
791
+
792
+ window.addEventListener("pointermove", onPointerMove);
793
+ window.addEventListener("pointerup", onPointerUp);
794
+ window.addEventListener("pointercancel", onPointerCancel);
795
+ window.addEventListener("keydown", onKeyDown, true);
796
+ window.addEventListener("blur", onWindowBlur);
797
+
798
+ session.detach = (): void => {
799
+ window.removeEventListener("pointermove", onPointerMove);
800
+ window.removeEventListener("pointerup", onPointerUp);
801
+ window.removeEventListener("pointercancel", onPointerCancel);
802
+ window.removeEventListener("keydown", onKeyDown, true);
803
+ window.removeEventListener("blur", onWindowBlur);
804
+ };
805
+
806
+ session.rafId = window.requestAnimationFrame(onFrame);
807
+ };
808
+
809
+ const startMove: (id: string, event: React.PointerEvent) => void =
810
+ useCallback((id: string, event: React.PointerEvent): void => {
811
+ startSession(id, "move", null, event);
812
+ }, []);
813
+
814
+ const startResize: (
815
+ id: string,
816
+ direction: ResizeDirection,
817
+ event: React.PointerEvent,
818
+ ) => void = useCallback(
819
+ (id: string, direction: ResizeDirection, event: React.PointerEvent) => {
820
+ startSession(id, "resize", direction, event);
821
+ },
822
+ [],
823
+ );
824
+
825
+ /*
826
+ * Keep a live reference to endGesture so unmount/disable cleanup always
827
+ * runs the freshest closure (the session's own detach handles listener
828
+ * identity).
829
+ */
830
+ const endGestureRef: React.MutableRefObject<EndFunction> =
831
+ useRef<EndFunction>(endGesture);
832
+ endGestureRef.current = endGesture;
833
+
834
+ // Cancel any in-flight gesture when unmounting or when editing turns off.
835
+ useEffect(() => {
836
+ return () => {
837
+ endGestureRef.current(false);
838
+ };
839
+ }, []);
840
+
841
+ useEffect(() => {
842
+ if (!props.isEnabled && sessionRef.current) {
843
+ endGestureRef.current(false);
844
+ }
845
+ }, [props.isEnabled]);
846
+
847
+ // ── derived render data ───────────────────────────────────────────────
848
+
849
+ const activeId: string | null = gesture?.id || null;
850
+ const isGestureActive: boolean = Boolean(gesture?.started);
851
+
852
+ const rects: Array<GridRect> = useMemo(() => {
853
+ if (!preview || !activeId) {
854
+ return props.items;
855
+ }
856
+ /*
857
+ * Neighbours come from the preview; the active widget stays pinned at
858
+ * its origin because its DOM node is being moved imperatively.
859
+ */
860
+ const originRect: GridRect | undefined =
861
+ sessionRef.current?.originRect ||
862
+ props.items.find((r: GridRect) => {
863
+ return r.id === activeId;
864
+ });
865
+
866
+ return preview.rects.map((rect: GridRect) => {
867
+ return rect.id === activeId && originRect ? originRect : rect;
868
+ });
869
+ }, [preview, activeId, props.items]);
870
+
871
+ const placeholderRect: GridRect | null =
872
+ isGestureActive && preview ? preview.activeRect : null;
873
+
874
+ const totalRows: number = useMemo(() => {
875
+ const layoutRows: number = GridLayoutUtil.requiredRows(
876
+ preview ? preview.rects : props.items,
877
+ );
878
+ return layoutRows;
879
+ }, [preview, props.items]);
880
+
881
+ return {
882
+ rects,
883
+ placeholderRect,
884
+ totalRows,
885
+ activeId,
886
+ activeMode: gesture?.mode || null,
887
+ isGestureActive,
888
+ registerItemElement,
889
+ startMove,
890
+ startResize,
891
+ };
892
+ };
893
+
894
+ type GetResizeCursorFunction = (direction: ResizeDirection) => string;
895
+
896
+ export const getResizeCursor: GetResizeCursorFunction = (
897
+ direction: ResizeDirection,
898
+ ): string => {
899
+ switch (direction) {
900
+ case "n":
901
+ case "s":
902
+ return "ns-resize";
903
+ case "e":
904
+ case "w":
905
+ return "ew-resize";
906
+ case "ne":
907
+ case "sw":
908
+ return "nesw-resize";
909
+ case "nw":
910
+ case "se":
911
+ return "nwse-resize";
912
+ default:
913
+ return "default";
914
+ }
915
+ };
916
+
917
+ export default useDashboardGridDnd;