@oneuptime/common 12.0.6 → 12.0.7

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 (424) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +1 -1
  2. package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
  3. package/Models/DatabaseModels/DeletedProject.ts +595 -0
  4. package/Models/DatabaseModels/Index.ts +2 -0
  5. package/Models/DatabaseModels/Monitor.ts +111 -0
  6. package/Models/DatabaseModels/Project.ts +3 -3
  7. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
  8. package/Server/API/FileAPI.ts +15 -2
  9. package/Server/API/MicrosoftTeamsAPI.ts +6 -1
  10. package/Server/API/ProjectAPI.ts +214 -0
  11. package/Server/API/StatusPageAPI.ts +39 -6
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIAgentService.ts +20 -0
  17. package/Server/Services/DashboardDomainService.ts +73 -28
  18. package/Server/Services/DatabaseService.ts +1 -0
  19. package/Server/Services/DeletedProjectService.ts +138 -0
  20. package/Server/Services/FileService.ts +61 -0
  21. package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
  22. package/Server/Services/IncidentPublicNoteService.ts +18 -0
  23. package/Server/Services/Index.ts +2 -0
  24. package/Server/Services/MonitorService.ts +311 -0
  25. package/Server/Services/ProbeService.ts +20 -0
  26. package/Server/Services/ProjectService.ts +297 -1
  27. package/Server/Services/PushNotificationService.ts +71 -0
  28. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
  29. package/Server/Services/StatusPageAnnouncementService.ts +53 -0
  30. package/Server/Services/StatusPageDomainService.ts +73 -28
  31. package/Server/Services/TeamMemberService.ts +117 -12
  32. package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
  33. package/Server/Services/UserWebhookService.ts +34 -109
  34. package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
  35. package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
  36. package/Server/Types/Database/DeleteById.ts +2 -0
  37. package/Server/Types/Database/DeleteOneBy.ts +6 -0
  38. package/Server/Types/Domain.ts +41 -0
  39. package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
  40. package/Server/Types/Workflow/Components/API/Get.ts +2 -1
  41. package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
  42. package/Server/Types/Workflow/Components/API/Post.ts +2 -1
  43. package/Server/Types/Workflow/Components/API/Put.ts +2 -1
  44. package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
  45. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
  46. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
  47. package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
  48. package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
  49. package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
  50. package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
  51. package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
  52. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
  53. package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
  54. package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
  55. package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
  56. package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
  57. package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
  58. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
  59. package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
  60. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  61. package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
  62. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
  63. package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
  64. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
  65. package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
  66. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
  67. package/Server/Utils/DataSource/EgressGuard.ts +176 -16
  68. package/Server/Utils/DataSource/HttpFetch.ts +6 -45
  69. package/Server/Utils/Execute.ts +8 -0
  70. package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
  71. package/Server/Utils/LLM/LLMService.ts +57 -12
  72. package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
  73. package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
  74. package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
  75. package/Server/Utils/Monitor/MonitorResource.ts +36 -0
  76. package/Server/Utils/Response.ts +142 -6
  77. package/Server/Utils/SSRFProtection.ts +273 -60
  78. package/Server/Utils/StartServer.ts +8 -0
  79. package/Server/Utils/VM/VMRunner.ts +88 -1
  80. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +555 -49
  81. package/Server/Utils/Workspace/Slack/Slack.ts +25 -2
  82. package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
  83. package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
  84. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
  85. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
  86. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
  87. package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
  88. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
  89. package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
  90. package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
  91. package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
  92. package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
  93. package/Tests/Server/API/ProjectAPI.test.ts +299 -1
  94. package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
  95. package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
  96. package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
  97. package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
  98. package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
  99. package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
  100. package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
  101. package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
  102. package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
  103. package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
  104. package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
  105. package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
  106. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
  107. package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
  108. package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
  109. package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
  110. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
  111. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
  112. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
  113. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
  114. package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
  115. package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
  116. package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
  117. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
  118. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
  119. package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
  120. package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
  121. package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
  122. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
  123. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
  124. package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
  125. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +62 -7
  126. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +69 -0
  127. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
  128. package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
  129. package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
  130. package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
  131. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
  132. package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
  133. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
  134. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
  135. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
  137. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
  138. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
  139. package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
  140. package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
  141. package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
  142. package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
  143. package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
  144. package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
  145. package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
  146. package/Tests/Types/JSONFunctions.test.ts +252 -1
  147. package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
  148. package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
  149. package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
  150. package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
  151. package/Tests/UI/Components/404.test.tsx +1 -1
  152. package/Tests/UI/Components/Alert.test.tsx +1 -1
  153. package/Tests/UI/Components/Badge.test.tsx +1 -1
  154. package/Tests/UI/Components/BasicForm.test.tsx +39 -10
  155. package/Tests/UI/Components/Button.test.tsx +1 -1
  156. package/Tests/UI/Components/Card.test.tsx +1 -1
  157. package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
  158. package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
  159. package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
  160. package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
  161. package/Tests/UI/Components/Dropdown.test.tsx +1 -1
  162. package/Tests/UI/Components/FilePicker.test.tsx +1 -1
  163. package/Tests/UI/Components/HiddenText.test.tsx +1 -1
  164. package/Tests/UI/Components/Input.test.tsx +1 -1
  165. package/Tests/UI/Components/Loader.test.tsx +1 -1
  166. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
  167. package/Tests/UI/Components/MasterPage.test.tsx +1 -1
  168. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
  169. package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
  170. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
  171. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
  172. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
  173. package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
  174. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
  175. package/Tests/UI/Components/NavBar.test.tsx +1 -1
  176. package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
  177. package/Tests/UI/Components/Pagination.test.tsx +1 -1
  178. package/Tests/UI/Components/Pill.test.tsx +1 -1
  179. package/Tests/UI/Components/Probe.test.tsx +1 -1
  180. package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
  181. package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
  182. package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
  183. package/Tests/UI/Components/SideOver.test.tsx +500 -6
  184. package/Tests/UI/Components/Tabs.test.tsx +1 -1
  185. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
  186. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
  187. package/Tests/UI/Components/TextArea.test.tsx +1 -1
  188. package/Tests/UI/Components/Toast.test.tsx +1 -1
  189. package/Tests/UI/Components/Toggle.test.tsx +1 -1
  190. package/Tests/UI/Components/TopSection.test.tsx +1 -1
  191. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
  192. package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
  193. package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
  194. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
  195. package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
  196. package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
  197. package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
  198. package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
  199. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
  200. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +27 -0
  201. package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
  202. package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
  203. package/Tests/Utils/Realtime.test.ts +78 -0
  204. package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
  205. package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
  206. package/Types/AI/FixPullRequestCiStatus.ts +1 -1
  207. package/Types/AI/FixVerificationStatus.ts +2 -2
  208. package/Types/API/Hostname.ts +92 -4
  209. package/Types/API/URL.ts +43 -25
  210. package/Types/Billing/BalanceAdjustmentType.ts +14 -0
  211. package/Types/Billing/ProjectBalanceType.ts +13 -0
  212. package/Types/Domain.ts +26 -0
  213. package/Types/File/MimeType.ts +11 -0
  214. package/Types/JSONFunctions.ts +15 -0
  215. package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
  216. package/Types/Workflow/Components/BaseModel.ts +92 -28
  217. package/Typings/Index.d.ts +12 -0
  218. package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
  219. package/UI/Components/Modal/ConfirmModal.tsx +10 -5
  220. package/UI/Components/Modal/Modal.tsx +27 -47
  221. package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
  222. package/UI/Components/SideOver/SideOver.tsx +195 -68
  223. package/UI/Utils/PageScrollLock.ts +66 -0
  224. package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
  225. package/Utils/Dashboard/VariableInterpolation.ts +19 -1
  226. package/Utils/LegacySerializedArray.ts +60 -0
  227. package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
  228. package/Utils/Telemetry/SavedViewFilters.ts +88 -0
  229. package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
  230. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  231. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
  232. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
  233. package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
  234. package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
  235. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  236. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  237. package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
  238. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  239. package/build/dist/Models/DatabaseModels/Project.js +3 -3
  240. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  241. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  242. package/build/dist/Server/API/FileAPI.js +14 -2
  243. package/build/dist/Server/API/FileAPI.js.map +1 -1
  244. package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
  245. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  246. package/build/dist/Server/API/ProjectAPI.js +157 -4
  247. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  248. package/build/dist/Server/API/StatusPageAPI.js +27 -3
  249. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
  253. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
  254. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
  255. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
  256. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  257. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  258. package/build/dist/Server/Services/AIAgentService.js +17 -0
  259. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  260. package/build/dist/Server/Services/DashboardDomainService.js +55 -18
  261. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  262. package/build/dist/Server/Services/DatabaseService.js +1 -0
  263. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  264. package/build/dist/Server/Services/DeletedProjectService.js +124 -0
  265. package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
  266. package/build/dist/Server/Services/FileService.js +52 -0
  267. package/build/dist/Server/Services/FileService.js.map +1 -1
  268. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
  269. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  270. package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
  271. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  272. package/build/dist/Server/Services/Index.js +2 -0
  273. package/build/dist/Server/Services/Index.js.map +1 -1
  274. package/build/dist/Server/Services/MonitorService.js +232 -0
  275. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  276. package/build/dist/Server/Services/ProbeService.js +17 -0
  277. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  278. package/build/dist/Server/Services/ProjectService.js +228 -6
  279. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  280. package/build/dist/Server/Services/PushNotificationService.js +56 -0
  281. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  282. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
  283. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  284. package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
  285. package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
  286. package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
  287. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  288. package/build/dist/Server/Services/TeamMemberService.js +97 -6
  289. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  290. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
  291. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  292. package/build/dist/Server/Services/UserWebhookService.js +31 -87
  293. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  294. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
  295. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  296. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
  297. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  298. package/build/dist/Server/Types/Domain.js +37 -0
  299. package/build/dist/Server/Types/Domain.js.map +1 -1
  300. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
  301. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  302. package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
  303. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  304. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
  305. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  306. package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
  307. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  308. package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
  309. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  310. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
  311. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  312. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
  313. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  314. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
  315. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  316. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
  317. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
  318. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
  319. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
  320. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
  321. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
  322. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
  323. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
  324. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
  325. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
  326. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
  327. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
  328. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
  329. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
  330. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
  331. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
  332. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
  333. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
  334. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
  335. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
  336. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
  337. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
  338. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
  339. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  340. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
  341. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
  342. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  343. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
  344. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
  345. package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
  346. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
  347. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
  348. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
  349. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
  350. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  351. package/build/dist/Server/Utils/DataSource/EgressGuard.js +94 -10
  352. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  353. package/build/dist/Server/Utils/DataSource/HttpFetch.js +1 -19
  354. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  355. package/build/dist/Server/Utils/Execute.js +2 -2
  356. package/build/dist/Server/Utils/Execute.js.map +1 -1
  357. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
  358. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
  359. package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
  360. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  361. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
  362. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  363. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
  364. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
  365. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
  366. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  367. package/build/dist/Server/Utils/Monitor/MonitorResource.js +32 -0
  368. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  369. package/build/dist/Server/Utils/Response.js +112 -5
  370. package/build/dist/Server/Utils/Response.js.map +1 -1
  371. package/build/dist/Server/Utils/SSRFProtection.js +219 -53
  372. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  373. package/build/dist/Server/Utils/StartServer.js +7 -0
  374. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  375. package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
  376. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  377. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +445 -43
  378. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  379. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +18 -1
  380. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  381. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  382. package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
  383. package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
  384. package/build/dist/Types/API/Hostname.js +73 -4
  385. package/build/dist/Types/API/Hostname.js.map +1 -1
  386. package/build/dist/Types/API/URL.js +42 -24
  387. package/build/dist/Types/API/URL.js.map +1 -1
  388. package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
  389. package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
  390. package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
  391. package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
  392. package/build/dist/Types/Domain.js +22 -0
  393. package/build/dist/Types/Domain.js.map +1 -1
  394. package/build/dist/Types/File/MimeType.js +11 -0
  395. package/build/dist/Types/File/MimeType.js.map +1 -1
  396. package/build/dist/Types/JSONFunctions.js +14 -0
  397. package/build/dist/Types/JSONFunctions.js.map +1 -1
  398. package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
  399. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  400. package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
  401. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
  402. package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
  403. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  404. package/build/dist/UI/Components/Modal/Modal.js +24 -34
  405. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  406. package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
  407. package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
  408. package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
  409. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  410. package/build/dist/UI/Utils/PageScrollLock.js +52 -0
  411. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
  412. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
  413. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
  414. package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
  415. package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
  416. package/build/dist/Utils/LegacySerializedArray.js +51 -0
  417. package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
  418. package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
  419. package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
  420. package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
  421. package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
  422. package/jest.config.json +3 -0
  423. package/package.json +1 -1
  424. package/tsconfig.json +3 -1
@@ -7,6 +7,7 @@ import ModalFooter from "./ModalFooter";
7
7
  import { VeryLightGray } from "../../../Types/BrandColors";
8
8
  import IconProp from "../../../Types/Icon/IconProp";
9
9
  import useTranslateValue from "../../Utils/Translation";
10
+ import { lockPageScroll, unlockPageScroll } from "../../Utils/PageScrollLock";
10
11
  import React, {
11
12
  FunctionComponent,
12
13
  ReactElement,
@@ -23,52 +24,6 @@ export enum ModalWidth {
23
24
  Large,
24
25
  }
25
26
 
26
- /*
27
- * The page behind a modal must not scroll, and nested modals share a single
28
- * lock: a counter, rather than a boolean, is what stops the inner modal from
29
- * handing scrolling back while its parent is still open.
30
- */
31
- let openModalCount: number = 0;
32
- let bodyOverflowBeforeLock: string = "";
33
- let bodyPaddingRightBeforeLock: string = "";
34
-
35
- type PageScrollLockFunction = () => void;
36
-
37
- const lockPageScroll: PageScrollLockFunction = (): void => {
38
- openModalCount++;
39
-
40
- if (openModalCount > 1 || typeof document === "undefined") {
41
- return;
42
- }
43
-
44
- bodyOverflowBeforeLock = document.body.style.overflow;
45
- bodyPaddingRightBeforeLock = document.body.style.paddingRight;
46
-
47
- /*
48
- * Hiding the scrollbar reclaims its width, so the page underneath would
49
- * jump sideways by exactly that much. Pad it back to hold everything still.
50
- */
51
- const scrollbarWidth: number =
52
- window.innerWidth - document.documentElement.clientWidth;
53
-
54
- if (scrollbarWidth > 0) {
55
- document.body.style.paddingRight = `${scrollbarWidth}px`;
56
- }
57
-
58
- document.body.style.overflow = "hidden";
59
- };
60
-
61
- const unlockPageScroll: PageScrollLockFunction = (): void => {
62
- openModalCount = Math.max(0, openModalCount - 1);
63
-
64
- if (openModalCount > 0 || typeof document === "undefined") {
65
- return;
66
- }
67
-
68
- document.body.style.overflow = bodyOverflowBeforeLock;
69
- document.body.style.paddingRight = bodyPaddingRightBeforeLock;
70
- };
71
-
72
27
  export interface ComponentProps {
73
28
  title: string;
74
29
  description?: string | undefined;
@@ -346,7 +301,32 @@ const Modal: FunctionComponent<ComponentProps> = (
346
301
  ): boolean => {
347
302
  const modal: HTMLDivElement | null = modalRef.current;
348
303
 
349
- return modal !== null && !modal.contains(event.target as Node);
304
+ if (modal === null) {
305
+ return false;
306
+ }
307
+
308
+ const target: Node = event.target as Node;
309
+
310
+ /*
311
+ * Dropdown menus inside the body (AutocompleteTextInput, OperatorSelector,
312
+ * ColorPicker, IconPicker, react-select) portal themselves to document.body
313
+ * so a scroll container cannot clip them. React dispatches events through
314
+ * the *React* tree rather than the DOM tree, so picking one of those
315
+ * options still bubbles into this handler even though the pointer never
316
+ * touched the backdrop — and because the option is not a DOM descendant of
317
+ * the panel, the containment check below read it as a click outside and
318
+ * dismissed the dialog, discarding whatever the user had filled in.
319
+ *
320
+ * A press only counts as a backdrop press when it physically landed inside
321
+ * the backdrop element this handler is attached to. That is a DOM question,
322
+ * and `contains` is true of the element itself, so a press directly on this
323
+ * layer or on the flex box it centres the panel in still dismisses.
324
+ */
325
+ if (!event.currentTarget.contains(target)) {
326
+ return false;
327
+ }
328
+
329
+ return !modal.contains(target);
350
330
  };
351
331
 
352
332
  const onBackdropMouseDown: BackdropPressHandler = (
@@ -14,6 +14,17 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
14
14
  modelId: ObjectID;
15
15
  modelAPI?: typeof ModelAPI | undefined;
16
16
  onDeleteSuccess: () => void;
17
+ /*
18
+ * Extra content for the confirmation modal - a field to fill in before
19
+ * confirming, for example. State for it belongs to the caller.
20
+ */
21
+ confirmationContent?: ReactElement | undefined;
22
+ /*
23
+ * Replaces the delete request itself, for models whose delete needs more
24
+ * than an id (see confirmationContent). Throwing surfaces the error in the
25
+ * same dialog the default request uses.
26
+ */
27
+ onDelete?: (() => Promise<void>) | undefined;
17
28
  }
18
29
 
19
30
  const ModelDelete: <TBaseModel extends BaseModel>(
@@ -30,12 +41,16 @@ const ModelDelete: <TBaseModel extends BaseModel>(
30
41
  const deleteItem: PromiseVoidFunction = async (): Promise<void> => {
31
42
  setIsLoading(true);
32
43
  try {
33
- const modelAPI: typeof ModelAPI = props.modelAPI || ModelAPI;
44
+ if (props.onDelete) {
45
+ await props.onDelete();
46
+ } else {
47
+ const modelAPI: typeof ModelAPI = props.modelAPI || ModelAPI;
34
48
 
35
- await modelAPI.deleteItem<TBaseModel>({
36
- modelType: props.modelType,
37
- id: props.modelId,
38
- });
49
+ await modelAPI.deleteItem<TBaseModel>({
50
+ modelType: props.modelType,
51
+ id: props.modelId,
52
+ });
53
+ }
39
54
  props.onDeleteSuccess?.();
40
55
  } catch (err) {
41
56
  setError(API.getFriendlyMessage(err));
@@ -76,7 +91,9 @@ const ModelDelete: <TBaseModel extends BaseModel>(
76
91
  }}
77
92
  submitButtonText={`Delete ${model.singularName}`}
78
93
  submitButtonType={ButtonStyleType.DANGER}
79
- />
94
+ >
95
+ {props.confirmationContent}
96
+ </ConfirmModal>
80
97
  ) : (
81
98
  <></>
82
99
  )}
@@ -1,7 +1,16 @@
1
1
  import Button, { ButtonStyleType } from "../Button/Button";
2
2
  import Icon from "../Icon/Icon";
3
3
  import IconProp from "../../../Types/Icon/IconProp";
4
- import React, { FunctionComponent, ReactElement } from "react";
4
+ import { lockPageScroll, unlockPageScroll } from "../../Utils/PageScrollLock";
5
+ import React, {
6
+ FunctionComponent,
7
+ ReactElement,
8
+ useEffect,
9
+ useId,
10
+ useLayoutEffect,
11
+ useState,
12
+ } from "react";
13
+ import { createPortal } from "react-dom";
5
14
 
6
15
  export enum SideOverSize {
7
16
  Small = "Small",
@@ -19,8 +28,42 @@ export interface ComponentProps {
19
28
  submitButtonText?: string | undefined;
20
29
  leftFooterElement?: ReactElement | undefined;
21
30
  size?: SideOverSize | undefined;
31
+ /*
32
+ * Opt out of hiding the page's scrollbar while the panel is open. Only worth
33
+ * setting for a panel that is a permanent part of its page rather than a
34
+ * transient drawer — every current caller wants the lock.
35
+ */
36
+ disablePageScrollLock?: boolean | undefined;
22
37
  }
23
38
 
39
+ type PortalTargetFunction = () => HTMLElement | null;
40
+
41
+ /*
42
+ * Where the panel is appended in the DOM, not where it appears on screen — it
43
+ * is `position: fixed` either way.
44
+ *
45
+ * Two reasons it cannot simply stay where the caller rendered it. First, any
46
+ * ancestor with a z-index traps the panel in that ancestor's stacking context,
47
+ * which is how a map's zoom toolbar (z-20) ended up painting on top of an open
48
+ * device panel. Second, and unfixable by z-index at all: only the fullscreen
49
+ * element's own subtree reaches the browser's top layer, so a panel appended to
50
+ * <body> while a chart or map is fullscreen is not painted anywhere. Following
51
+ * document.fullscreenElement keeps the panel visible in both modes.
52
+ */
53
+ const getPortalTarget: PortalTargetFunction = (): HTMLElement | null => {
54
+ if (typeof document === "undefined") {
55
+ return null;
56
+ }
57
+
58
+ const fullscreenElement: Element | null = document.fullscreenElement;
59
+
60
+ if (fullscreenElement instanceof HTMLElement) {
61
+ return fullscreenElement;
62
+ }
63
+
64
+ return document.body;
65
+ };
66
+
24
67
  const SideOver: FunctionComponent<ComponentProps> = (
25
68
  props: ComponentProps,
26
69
  ): ReactElement => {
@@ -34,85 +77,169 @@ const SideOver: FunctionComponent<ComponentProps> = (
34
77
  widthClass = "max-w-7xl";
35
78
  }
36
79
 
37
- return (
80
+ const titleId: string = useId();
81
+ const descriptionId: string = useId();
82
+
83
+ const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(
84
+ getPortalTarget,
85
+ );
86
+
87
+ useEffect(() => {
88
+ const syncPortalTarget: () => void = (): void => {
89
+ setPortalTarget(getPortalTarget());
90
+ };
91
+
92
+ /*
93
+ * Re-read once on mount as well: the first render happens before the
94
+ * browser has necessarily settled document.fullscreenElement.
95
+ */
96
+ syncPortalTarget();
97
+ document.addEventListener("fullscreenchange", syncPortalTarget);
98
+
99
+ return () => {
100
+ document.removeEventListener("fullscreenchange", syncPortalTarget);
101
+ };
102
+ }, []);
103
+
104
+ /*
105
+ * A layout effect, not an ordinary one. Hiding the page scrollbar widens the
106
+ * viewport that `right: 0` resolves against, so a panel that painted before
107
+ * the lock landed would visibly jump sideways by the scrollbar's width on
108
+ * every platform that draws a classic scrollbar. Locking before paint means
109
+ * the panel is only ever drawn in its final position.
110
+ */
111
+ useLayoutEffect(() => {
112
+ if (props.disablePageScrollLock) {
113
+ return undefined;
114
+ }
115
+
116
+ lockPageScroll();
117
+
118
+ return () => {
119
+ unlockPageScroll();
120
+ };
121
+ }, [props.disablePageScrollLock]);
122
+
123
+ const sideOver: ReactElement = (
124
+ /*
125
+ * z-30 clears every tier of page chrome (sticky headers and table headers
126
+ * at z-10/z-20, map and log-viewer toolbars at z-20) while staying under
127
+ * the app-wide overlays that must remain visible over a panel: Toast and
128
+ * the AI chat panel at z-40, Modal at z-50, portalled dropdown menus at
129
+ * z-60. Deliberately NOT z-40 — Toast and AIChatPanel are mounted ahead of
130
+ * the routed page in App.tsx, so an equal z-index would be broken in this
131
+ * panel's favour by document order and toasts would disappear behind it.
132
+ *
133
+ * role="dialog" without aria-modal, because the panel genuinely is not
134
+ * modal: everything outside it stays clickable by design (both topology
135
+ * maps let you click straight from one node's panel to the next). Claiming
136
+ * aria-modal also made this panel the last entry in Modal's dialog stack,
137
+ * which silently disabled Escape, backdrop-dismissal and initial focus on
138
+ * any Modal that was already open.
139
+ */
38
140
  <div
39
- className="relative z-10"
40
- aria-labelledby="slide-over-title"
141
+ className="relative z-30"
142
+ aria-labelledby={titleId}
143
+ aria-describedby={props.description ? descriptionId : undefined}
41
144
  role="dialog"
42
- aria-modal="true"
145
+ data-testid="side-over"
43
146
  >
44
- <div className="inset-0"></div>
45
-
46
- <div className="inset-0 overflow-hidden">
47
- <div className="inset-0 overflow-hidden">
48
- <div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full">
49
- <div className={`pointer-events-auto w-screen ${widthClass}`}>
50
- <div className="flex h-full flex-col bg-white shadow-xl">
51
- <div className="flex-shrink-0 flex flex-col bg-gray-50 px-4 py-6 sm:px-6">
52
- <div className="flex items-start justify-between space-x-3">
53
- <div className="space-y-1">
54
- <h2
55
- className="text-lg font-medium text-gray-900"
56
- id="slide-over-title"
57
- >
58
- {props.title}
59
- </h2>
60
- <p className="text-sm text-gray-500">
61
- {props.description}
62
- </p>
63
- </div>
64
- <div className="flex h-7 items-center">
65
- <button
66
- onClick={() => {
67
- props.onClose();
68
- }}
69
- type="button"
70
- className="text-gray-400 hover:text-gray-500"
71
- >
72
- <span className="sr-only">Close panel</span>
73
-
74
- <Icon className="h-6 w-6" icon={IconProp.Close} />
75
- </button>
76
- </div>
77
- </div>
78
- </div>
79
- <div className="h-full overflow-y-scroll">
80
- <div className="space-y-6 py-6 sm:space-y-0 sm:divide-y sm:divide-gray-200 sm:py-0 p-5">
81
- {props.children}
82
- </div>
147
+ <div
148
+ className="pointer-events-none fixed inset-0 flex justify-end"
149
+ data-testid="side-over-layer"
150
+ >
151
+ <div className={`pointer-events-auto w-full ${widthClass}`}>
152
+ <div className="flex h-full flex-col bg-white shadow-xl">
153
+ <div className="flex-shrink-0 flex flex-col bg-gray-50 px-4 py-6 sm:px-6">
154
+ <div className="flex items-start justify-between space-x-3">
155
+ <div className="space-y-1">
156
+ <h2
157
+ className="text-lg font-medium text-gray-900"
158
+ id={titleId}
159
+ data-testid="side-over-title"
160
+ >
161
+ {props.title}
162
+ </h2>
163
+ <p
164
+ className="text-sm text-gray-500"
165
+ id={descriptionId}
166
+ data-testid="side-over-description"
167
+ >
168
+ {props.description}
169
+ </p>
83
170
  </div>
84
- <div className="flex-shrink-0 border-t border-gray-200 px-4 py-5 sm:px-6 flex justify-between">
85
- <div className="flex justify-start space-x-3">
86
- {props.leftFooterElement}
87
- </div>
88
- <div className="flex justify-end space-x-3">
89
- <Button
90
- title="Close"
91
- onClick={() => {
92
- props.onClose();
93
- }}
94
- buttonStyle={ButtonStyleType.NORMAL}
95
- />
96
-
97
- {props.onSubmit && (
98
- <Button
99
- title={props.submitButtonText || "Save"}
100
- disabled={props.submitButtonDisabled}
101
- onClick={() => {
102
- props.onSubmit!();
103
- }}
104
- buttonStyle={ButtonStyleType.PRIMARY}
105
- />
106
- )}
107
- </div>
171
+ <div className="flex h-7 items-center">
172
+ <button
173
+ onClick={() => {
174
+ props.onClose();
175
+ }}
176
+ type="button"
177
+ title="Close panel"
178
+ data-testid="close-button"
179
+ className="text-gray-400 hover:text-gray-500"
180
+ >
181
+ <span className="sr-only">Close panel</span>
182
+
183
+ <Icon className="h-6 w-6" icon={IconProp.Close} />
184
+ </button>
108
185
  </div>
109
186
  </div>
110
187
  </div>
188
+ {/*
189
+ * overflow-y-auto, not overflow-y-scroll: `scroll` paints a dead
190
+ * track down the panel's right edge even when the content fits,
191
+ * immediately beside the page's own scrollbar, which reads as the
192
+ * page scrollbar bleeding through the panel. overscroll-contain
193
+ * stops a wheel past the end of the panel from scrolling the page
194
+ * out from under it.
195
+ */}
196
+ <div
197
+ className="min-h-0 flex-1 overflow-y-auto overscroll-contain"
198
+ data-testid="side-over-content"
199
+ >
200
+ <div className="space-y-6 py-6 sm:space-y-0 sm:divide-y sm:divide-gray-200 sm:py-0 p-5">
201
+ {props.children}
202
+ </div>
203
+ </div>
204
+ <div
205
+ className="flex-shrink-0 border-t border-gray-200 px-4 py-5 sm:px-6 flex justify-between"
206
+ data-testid="side-over-footer"
207
+ >
208
+ <div className="flex justify-start space-x-3">
209
+ {props.leftFooterElement}
210
+ </div>
211
+ <div className="flex justify-end space-x-3">
212
+ <Button
213
+ title="Close"
214
+ onClick={() => {
215
+ props.onClose();
216
+ }}
217
+ buttonStyle={ButtonStyleType.NORMAL}
218
+ />
219
+
220
+ {props.onSubmit && (
221
+ <Button
222
+ title={props.submitButtonText || "Save"}
223
+ disabled={props.submitButtonDisabled}
224
+ onClick={() => {
225
+ props.onSubmit!();
226
+ }}
227
+ buttonStyle={ButtonStyleType.PRIMARY}
228
+ />
229
+ )}
230
+ </div>
231
+ </div>
111
232
  </div>
112
233
  </div>
113
234
  </div>
114
235
  </div>
115
236
  );
237
+
238
+ if (!portalTarget) {
239
+ return sideOver;
240
+ }
241
+
242
+ return createPortal(sideOver, portalTarget);
116
243
  };
117
244
 
118
245
  export default SideOver;
@@ -0,0 +1,66 @@
1
+ /*
2
+ * The page behind an open dialog surface must not scroll, and nested surfaces
3
+ * share a single lock: a counter, rather than a boolean, is what stops an inner
4
+ * dialog from handing scrolling back while the surface that opened it is still
5
+ * up.
6
+ *
7
+ * This lives outside any one component on purpose. Modal and SideOver nest
8
+ * inside each other in shipped code — Workflow's "Run Workflow" panel is a
9
+ * SideOver that opens a ConfirmModal — and two private counters would race over
10
+ * document.body.style. Whichever surface unmounted first would restore the
11
+ * snapshot the other had already overwritten, leaving the page either scrolling
12
+ * under a dialog that is still open or locked forever with no dialog on screen.
13
+ */
14
+
15
+ let openDialogCount: number = 0;
16
+ let bodyOverflowBeforeLock: string = "";
17
+ let bodyPaddingRightBeforeLock: string = "";
18
+
19
+ export type PageScrollLockFunction = () => void;
20
+
21
+ export const lockPageScroll: PageScrollLockFunction = (): void => {
22
+ openDialogCount++;
23
+
24
+ if (openDialogCount > 1 || typeof document === "undefined") {
25
+ return;
26
+ }
27
+
28
+ bodyOverflowBeforeLock = document.body.style.overflow;
29
+ bodyPaddingRightBeforeLock = document.body.style.paddingRight;
30
+
31
+ /*
32
+ * Hiding the scrollbar reclaims its width, so the page underneath would
33
+ * jump sideways by exactly that much. Pad it back to hold everything still.
34
+ */
35
+ const scrollbarWidth: number =
36
+ window.innerWidth - document.documentElement.clientWidth;
37
+
38
+ if (scrollbarWidth > 0) {
39
+ document.body.style.paddingRight = `${scrollbarWidth}px`;
40
+ }
41
+
42
+ document.body.style.overflow = "hidden";
43
+ };
44
+
45
+ export const unlockPageScroll: PageScrollLockFunction = (): void => {
46
+ openDialogCount = Math.max(0, openDialogCount - 1);
47
+
48
+ if (openDialogCount > 0 || typeof document === "undefined") {
49
+ return;
50
+ }
51
+
52
+ document.body.style.overflow = bodyOverflowBeforeLock;
53
+ document.body.style.paddingRight = bodyPaddingRightBeforeLock;
54
+ };
55
+
56
+ /*
57
+ * Test-only escape hatch. The counter is module state that survives between
58
+ * cases in the same jest file, so a suite that asserts on the FIRST lock needs
59
+ * a way back to a known zero.
60
+ */
61
+ export const resetPageScrollLockForTesting: PageScrollLockFunction =
62
+ (): void => {
63
+ openDialogCount = 0;
64
+ bodyOverflowBeforeLock = "";
65
+ bodyPaddingRightBeforeLock = "";
66
+ };
@@ -128,8 +128,10 @@ export default class HtmlWidgetDocument {
128
128
  *
129
129
  * Resolution is delegated to DashboardVariableInterpolation so this widget
130
130
  * agrees with every query-driven widget about what a variable currently
131
- * means (selected value, else default, else nothing). Multi-select joins
132
- * with "," because that is the form an author would paste into a filter.
131
+ * means: a single-select is its selected value, else its default, else
132
+ * nothing; a multi-select is its picks, and no picks is "All" which
133
+ * lands here as "" rather than the default. Multi-select joins with ","
134
+ * because that is the form an author would paste into a filter.
133
135
  */
134
136
  public static resolveVariables(
135
137
  variables: Array<DashboardVariable> | undefined,
@@ -31,6 +31,23 @@ export interface ResolvedVariableValue {
31
31
  * key so the server's WHERE-builder produces `attributes[key] IN (...)`.
32
32
  */
33
33
  export default class DashboardVariableInterpolation {
34
+ /*
35
+ * The one place that decides what a variable currently *means*. Every
36
+ * widget and every selector defers to it, so the rules have to be
37
+ * stated rather than inferred:
38
+ *
39
+ * - A multi-select variable's `selectedValues` is its sole source of
40
+ * truth. Empty or unset is the "All" state and produces no
41
+ * predicate. `defaultValue` deliberately does NOT apply here: the
42
+ * selector shows "All" whenever the list is empty and cannot show
43
+ * anything else, so honouring a default would filter widgets behind
44
+ * a control that reads "unfiltered". A default that only holds one
45
+ * value also cannot express what a multi-select is for.
46
+ * - A single-select variable falls back to `defaultValue` only when
47
+ * `selectedValue` is absent. An empty string is a real choice — the
48
+ * "All" option — and must not be overwritten by the default, which
49
+ * is why this uses `??` rather than `||`.
50
+ */
34
51
  public static resolveValue(
35
52
  variable: DashboardVariable,
36
53
  ): ResolvedVariableValue | undefined {
@@ -43,7 +60,8 @@ export default class DashboardVariableInterpolation {
43
60
  if (values.length > 0) {
44
61
  return { multi: values };
45
62
  }
46
- // No multi-select picks. Fall through to default value handling.
63
+ // No picks: "All". Never falls back to the single-select default.
64
+ return undefined;
47
65
  }
48
66
 
49
67
  const v: string | undefined =
@@ -0,0 +1,60 @@
1
+ /*
2
+ * JSONFunctions.serializeValue had no Array.isArray branch. Arrays are
3
+ * typeof "object", so any array that reached it directly was walked by key and
4
+ * written out as { "0": ..., "1": ... }. Two shapes hit that path:
5
+ *
6
+ * - an array nested directly inside another array, e.g. the
7
+ * [facetKey, value] filter tuples of a telemetry saved view;
8
+ * - a top-level array handed to LocalStorage/SessionStorage/Cookie, which
9
+ * call serializeValue on the value itself rather than on a wrapper object.
10
+ *
11
+ * (An array under an object key was always safe, at any depth, because
12
+ * serialize() special-cases array-valued keys.)
13
+ *
14
+ * The serializer is fixed, but values written before the fix are still in
15
+ * databases and in browser storage. readLegacySerializedArray reads either
16
+ * shape, so those values load instead of being thrown away — or, worse,
17
+ * destructured and taking the page down with them.
18
+ */
19
+
20
+ export function readLegacySerializedArray(
21
+ value: unknown,
22
+ ): Array<unknown> | null {
23
+ if (Array.isArray(value)) {
24
+ return value;
25
+ }
26
+
27
+ if (!value || typeof value !== "object") {
28
+ return null;
29
+ }
30
+
31
+ const record: Record<string, unknown> = value as Record<string, unknown>;
32
+ const keys: Array<string> = Object.keys(record);
33
+
34
+ /*
35
+ * An empty object is what an empty array serialized to, and in every place
36
+ * this is used an empty collection is the sensible reading.
37
+ */
38
+ if (keys.length === 0) {
39
+ return [];
40
+ }
41
+
42
+ /*
43
+ * Only a complete run of indices 0..n-1 could have come from an array. A
44
+ * genuine object that merely happens to have a "0" key keeps its own shape
45
+ * and is rejected here.
46
+ */
47
+ for (let index: number = 0; index < keys.length; index++) {
48
+ if (!Object.prototype.hasOwnProperty.call(record, String(index))) {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ const restored: Array<unknown> = [];
54
+
55
+ for (let index: number = 0; index < keys.length; index++) {
56
+ restored.push(record[String(index)]);
57
+ }
58
+
59
+ return restored;
60
+ }