@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
@@ -0,0 +1,393 @@
1
+ /*
2
+ * ---------------------------------------------------------------------------
3
+ * GitHubUtil.createPullRequestWithToken — the App-authenticated create-PR
4
+ * path used by the chat toolbox (open_code_pull_request) and by the
5
+ * server-side fix triggers.
6
+ *
7
+ * The invariant pinned here: the pull request opens READY FOR REVIEW. A draft
8
+ * reaches no reviewer, no CODEOWNERS rule and no review automation, so an
9
+ * agent that drafted by default was burying its own output where nobody was
10
+ * asked to look.
11
+ *
12
+ * This is a VISIBILITY change and not a safety one. The safety boundary lives
13
+ * in the branch policy (never the default branch, never a protected branch,
14
+ * asserted in CodeWriteTools.test.ts) and in the fact that nothing is ever
15
+ * merged automatically. Ready for review still means unreviewed.
16
+ *
17
+ * The isDraft option and its 422 fallback survive for callers that still want
18
+ * a draft — but the fallback must fire ONLY on a 422 that names drafts.
19
+ * GitHub answers 422 for "a pull request already exists for this branch" too,
20
+ * and blindly retrying that produced a second identical failure whose message
21
+ * no longer described the real problem.
22
+ * ---------------------------------------------------------------------------
23
+ */
24
+
25
+ import GitHubUtil from "../../../Server/Utils/CodeRepository/GitHub/GitHub";
26
+ import API from "../../../Utils/API";
27
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
28
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
29
+ import { JSONObject } from "../../../Types/JSON";
30
+ import PullRequest from "../../../Types/CodeRepository/PullRequest";
31
+ import PullRequestState from "../../../Types/CodeRepository/PullRequestState";
32
+ import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
33
+
34
+ const CREATE_ARGS: {
35
+ installationId: string;
36
+ organizationName: string;
37
+ repositoryName: string;
38
+ baseBranchName: string;
39
+ headBranchName: string;
40
+ title: string;
41
+ body: string;
42
+ } = {
43
+ installationId: "installation-1",
44
+ organizationName: "acme",
45
+ repositoryName: "checkout",
46
+ baseBranchName: "main",
47
+ headBranchName: "oneuptime-ai/fix-charge",
48
+ title: "fix: guard the undefined cart",
49
+ body: "why this change",
50
+ };
51
+
52
+ // GitHub's create-PR 201 body, trimmed to the fields the mapper reads.
53
+ function prCreatedResponse(): HTTPResponse<JSONObject> {
54
+ return new HTTPResponse<JSONObject>(
55
+ 201,
56
+ {
57
+ id: 4242,
58
+ number: 7,
59
+ title: "fix: guard the undefined cart",
60
+ body: "why this change",
61
+ url: "https://api.github.com/repos/acme/checkout/pulls/7",
62
+ state: "open",
63
+ created_at: "2026-08-11T10:00:00Z",
64
+ updated_at: "2026-08-11T10:00:00Z",
65
+ head: { ref: "oneuptime-ai/fix-charge" },
66
+ base: { ref: "main" },
67
+ },
68
+ {},
69
+ );
70
+ }
71
+
72
+ function errorResponse(
73
+ statusCode: number,
74
+ data: JSONObject,
75
+ ): HTTPErrorResponse {
76
+ return new HTTPErrorResponse(statusCode, data, {});
77
+ }
78
+
79
+ function draftsNotSupported(): HTTPErrorResponse {
80
+ return errorResponse(422, {
81
+ message: "Draft pull requests are not supported in this repository.",
82
+ });
83
+ }
84
+
85
+ function pullRequestAlreadyExists(): HTTPErrorResponse {
86
+ return errorResponse(422, {
87
+ message: "Validation Failed",
88
+ errors: [
89
+ {
90
+ resource: "PullRequest",
91
+ code: "custom",
92
+ message:
93
+ "A pull request already exists for acme:oneuptime-ai/fix-charge.",
94
+ },
95
+ ],
96
+ });
97
+ }
98
+
99
+ let postSpy: jest.SpiedFunction<typeof API.post>;
100
+
101
+ // The body of the Nth API.post call, 1-indexed to match how retries read.
102
+ function requestBody(callNumber: number): JSONObject {
103
+ const args: unknown = postSpy.mock.calls[callNumber - 1];
104
+
105
+ if (!args) {
106
+ throw new Error(`API.post was not called ${callNumber} time(s)`);
107
+ }
108
+
109
+ return (args as Array<{ data: JSONObject }>)[0]!.data;
110
+ }
111
+
112
+ describe("GitHubUtil.createPullRequestWithToken", () => {
113
+ beforeEach(() => {
114
+ jest
115
+ .spyOn(GitHubUtil, "getInstallationAccessToken")
116
+ .mockResolvedValue({ token: "gh-token", expiresAt: new Date() });
117
+
118
+ postSpy = jest.spyOn(API, "post");
119
+ });
120
+
121
+ afterEach(() => {
122
+ jest.restoreAllMocks();
123
+ });
124
+
125
+ describe("the default", () => {
126
+ test("opens the pull request READY FOR REVIEW, not as a draft", async () => {
127
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
128
+
129
+ await GitHubUtil.createPullRequestWithToken(CREATE_ARGS);
130
+
131
+ expect(postSpy).toHaveBeenCalledTimes(1);
132
+ expect(requestBody(1)["draft"]).toBe(false);
133
+ });
134
+
135
+ /*
136
+ * Sent explicitly rather than omitted: GitHub's own default is non-draft
137
+ * today, but leaving the field out makes the intent unassertable and
138
+ * dependent on their default staying put.
139
+ */
140
+ test("sends draft explicitly rather than relying on GitHub's default", async () => {
141
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
142
+
143
+ await GitHubUtil.createPullRequestWithToken(CREATE_ARGS);
144
+
145
+ expect(Object.keys(requestBody(1))).toContain("draft");
146
+ });
147
+
148
+ test("sends the branches, title and body alongside it", async () => {
149
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
150
+
151
+ await GitHubUtil.createPullRequestWithToken(CREATE_ARGS);
152
+
153
+ expect(requestBody(1)).toEqual({
154
+ base: "main",
155
+ head: "oneuptime-ai/fix-charge",
156
+ title: "fix: guard the undefined cart",
157
+ body: "why this change",
158
+ draft: false,
159
+ });
160
+ });
161
+
162
+ test("returns the mapped pull request", async () => {
163
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
164
+
165
+ const pullRequest: PullRequest =
166
+ await GitHubUtil.createPullRequestWithToken(CREATE_ARGS);
167
+
168
+ expect(pullRequest.pullRequestId).toBe(4242);
169
+ expect(pullRequest.pullRequestNumber).toBe(7);
170
+ expect(pullRequest.state).toBe(PullRequestState.Open);
171
+ expect(pullRequest.repoOrganizationName).toBe("acme");
172
+ expect(pullRequest.repoName).toBe("checkout");
173
+ });
174
+
175
+ test("requests a token scoped to pull requests and contents", async () => {
176
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
177
+
178
+ await GitHubUtil.createPullRequestWithToken(CREATE_ARGS);
179
+
180
+ expect(GitHubUtil.getInstallationAccessToken).toHaveBeenCalledWith(
181
+ "installation-1",
182
+ { permissions: { pull_requests: "write", contents: "write" } },
183
+ );
184
+ });
185
+ });
186
+
187
+ describe("the explicit option", () => {
188
+ test("isDraft: false opens ready for review", async () => {
189
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
190
+
191
+ await GitHubUtil.createPullRequestWithToken({
192
+ ...CREATE_ARGS,
193
+ isDraft: false,
194
+ });
195
+
196
+ expect(requestBody(1)["draft"]).toBe(false);
197
+ });
198
+
199
+ // The option is kept, not removed — only the DEFAULT changed.
200
+ test("isDraft: true still opens a draft", async () => {
201
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
202
+
203
+ await GitHubUtil.createPullRequestWithToken({
204
+ ...CREATE_ARGS,
205
+ isDraft: true,
206
+ });
207
+
208
+ expect(requestBody(1)["draft"]).toBe(true);
209
+ });
210
+
211
+ /*
212
+ * A caller threading an optional through ("isDraft: settings.draft") must
213
+ * land on ready-for-review. The old default read `!== false`, where
214
+ * undefined meant draft.
215
+ */
216
+ test("isDraft: undefined is ready for review, not a draft", async () => {
217
+ postSpy.mockResolvedValueOnce(prCreatedResponse());
218
+
219
+ await GitHubUtil.createPullRequestWithToken({
220
+ ...CREATE_ARGS,
221
+ isDraft: undefined,
222
+ });
223
+
224
+ expect(requestBody(1)["draft"]).toBe(false);
225
+ });
226
+ });
227
+
228
+ describe("the draft fallback for repositories without the feature", () => {
229
+ test("retries a rejected draft as ready for review", async () => {
230
+ postSpy
231
+ .mockResolvedValueOnce(draftsNotSupported())
232
+ .mockResolvedValueOnce(prCreatedResponse());
233
+
234
+ const pullRequest: PullRequest =
235
+ await GitHubUtil.createPullRequestWithToken({
236
+ ...CREATE_ARGS,
237
+ isDraft: true,
238
+ });
239
+
240
+ expect(postSpy).toHaveBeenCalledTimes(2);
241
+ expect(requestBody(1)["draft"]).toBe(true);
242
+ expect(requestBody(2)["draft"]).toBe(false);
243
+ expect(pullRequest.pullRequestNumber).toBe(7);
244
+ });
245
+
246
+ test("keeps the title, body and branches identical on the retry", async () => {
247
+ postSpy
248
+ .mockResolvedValueOnce(draftsNotSupported())
249
+ .mockResolvedValueOnce(prCreatedResponse());
250
+
251
+ await GitHubUtil.createPullRequestWithToken({
252
+ ...CREATE_ARGS,
253
+ isDraft: true,
254
+ });
255
+
256
+ expect({ ...requestBody(2), draft: true }).toEqual(requestBody(1));
257
+ });
258
+
259
+ /*
260
+ * The regression this guards: the retry used to fire on ANY 422. A
261
+ * duplicate-PR 422 was retried, failed identically, and the caller was
262
+ * handed the second error — same status, and no longer the real story.
263
+ */
264
+ test("does NOT retry a 422 that is not about drafts", async () => {
265
+ postSpy.mockResolvedValueOnce(pullRequestAlreadyExists());
266
+
267
+ await expect(
268
+ GitHubUtil.createPullRequestWithToken({
269
+ ...CREATE_ARGS,
270
+ isDraft: true,
271
+ }),
272
+ ).rejects.toBeInstanceOf(HTTPErrorResponse);
273
+
274
+ expect(postSpy).toHaveBeenCalledTimes(1);
275
+ });
276
+
277
+ /*
278
+ * With the default now non-draft there is nothing to fall back FROM, so a
279
+ * retry here would be a silent duplicate create attempt against GitHub.
280
+ */
281
+ test("never retries when the request was not a draft in the first place", async () => {
282
+ postSpy.mockResolvedValueOnce(draftsNotSupported());
283
+
284
+ await expect(
285
+ GitHubUtil.createPullRequestWithToken(CREATE_ARGS),
286
+ ).rejects.toBeInstanceOf(HTTPErrorResponse);
287
+
288
+ expect(postSpy).toHaveBeenCalledTimes(1);
289
+ });
290
+
291
+ test("does not retry a non-422 failure even when it mentions drafts", async () => {
292
+ postSpy.mockResolvedValueOnce(
293
+ errorResponse(403, { message: "Draft access forbidden" }),
294
+ );
295
+
296
+ await expect(
297
+ GitHubUtil.createPullRequestWithToken({
298
+ ...CREATE_ARGS,
299
+ isDraft: true,
300
+ }),
301
+ ).rejects.toBeInstanceOf(HTTPErrorResponse);
302
+
303
+ expect(postSpy).toHaveBeenCalledTimes(1);
304
+ });
305
+
306
+ test("throws the second failure when the retry also fails", async () => {
307
+ postSpy.mockResolvedValueOnce(draftsNotSupported()).mockResolvedValueOnce(
308
+ errorResponse(422, {
309
+ message: "Validation Failed",
310
+ errors: [{ message: "head sha can't be blank" }],
311
+ }),
312
+ );
313
+
314
+ await expect(
315
+ GitHubUtil.createPullRequestWithToken({
316
+ ...CREATE_ARGS,
317
+ isDraft: true,
318
+ }),
319
+ ).rejects.toBeInstanceOf(HTTPErrorResponse);
320
+
321
+ expect(postSpy).toHaveBeenCalledTimes(2);
322
+ });
323
+ });
324
+ });
325
+
326
+ /*
327
+ * The predicate behind the fallback, tested directly: it decides whether a
328
+ * failed create is "this repository has no drafts" or something else that
329
+ * must not be retried.
330
+ */
331
+ describe("GitHubUtil.isDraftNotSupportedError", () => {
332
+ test("true when the 422 message names drafts", () => {
333
+ expect(GitHubUtil.isDraftNotSupportedError(draftsNotSupported())).toBe(
334
+ true,
335
+ );
336
+ });
337
+
338
+ test("true when only the errors array names drafts", () => {
339
+ expect(
340
+ GitHubUtil.isDraftNotSupportedError(
341
+ errorResponse(422, {
342
+ message: "Validation Failed",
343
+ errors: [{ message: "Draft pull requests are not supported." }],
344
+ }),
345
+ ),
346
+ ).toBe(true);
347
+ });
348
+
349
+ test("case-insensitive — GitHub capitalizes 'Draft' at the start of a sentence", () => {
350
+ expect(
351
+ GitHubUtil.isDraftNotSupportedError(
352
+ errorResponse(422, { message: "DRAFT PULL REQUESTS UNSUPPORTED" }),
353
+ ),
354
+ ).toBe(true);
355
+ });
356
+
357
+ test("false for a duplicate-pull-request 422", () => {
358
+ expect(
359
+ GitHubUtil.isDraftNotSupportedError(pullRequestAlreadyExists()),
360
+ ).toBe(false);
361
+ });
362
+
363
+ test("false for an invalid-head 422", () => {
364
+ expect(
365
+ GitHubUtil.isDraftNotSupportedError(
366
+ errorResponse(422, {
367
+ message: "Validation Failed",
368
+ errors: [{ field: "head", code: "invalid" }],
369
+ }),
370
+ ),
371
+ ).toBe(false);
372
+ });
373
+
374
+ test("false for any non-422, even one that mentions drafts", () => {
375
+ expect(
376
+ GitHubUtil.isDraftNotSupportedError(
377
+ errorResponse(403, { message: "Draft access forbidden" }),
378
+ ),
379
+ ).toBe(false);
380
+
381
+ expect(
382
+ GitHubUtil.isDraftNotSupportedError(
383
+ errorResponse(500, { message: "Draft service unavailable" }),
384
+ ),
385
+ ).toBe(false);
386
+ });
387
+
388
+ test("false — not a crash — on a 422 with an empty body", () => {
389
+ expect(GitHubUtil.isDraftNotSupportedError(errorResponse(422, {}))).toBe(
390
+ false,
391
+ );
392
+ });
393
+ });
@@ -0,0 +1,202 @@
1
+ import {
2
+ extractImageAccessTokens,
3
+ setIsPublicForMarkdownImages,
4
+ syncIsPublicForMarkdownImages,
5
+ } from "../../../Server/Utils/InlineImageAccessTokenSync";
6
+ import FileService from "../../../Server/Services/FileService";
7
+ import { beforeEach, describe, expect, it } from "@jest/globals";
8
+
9
+ jest.mock("../../../Server/Services/FileService", () => {
10
+ return {
11
+ findOneBy: jest.fn(),
12
+ updateOneById: jest.fn(),
13
+ };
14
+ });
15
+
16
+ jest.mock("../../../Server/Utils/Logger", () => {
17
+ return {
18
+ __esModule: true,
19
+ default: {
20
+ error: jest.fn(),
21
+ warn: jest.fn(),
22
+ info: jest.fn(),
23
+ debug: jest.fn(),
24
+ },
25
+ };
26
+ });
27
+
28
+ const urlFor: (token: string) => string = (token: string): string => {
29
+ return `https://example.com/file/image/access-token/${token}`;
30
+ };
31
+
32
+ describe("extractImageAccessTokens", () => {
33
+ it("returns nothing for empty, null or undefined markdown", () => {
34
+ expect(extractImageAccessTokens("")).toEqual([]);
35
+ expect(extractImageAccessTokens(null)).toEqual([]);
36
+ expect(extractImageAccessTokens(undefined)).toEqual([]);
37
+ });
38
+
39
+ it("returns nothing when the markdown has no inline images", () => {
40
+ expect(extractImageAccessTokens("Just some **notes** about the outage.")) //
41
+ .toEqual([]);
42
+ });
43
+
44
+ it("extracts a single token", () => {
45
+ expect(extractImageAccessTokens(`![shot](${urlFor("abc123")})`)).toEqual([
46
+ "abc123",
47
+ ]);
48
+ });
49
+
50
+ it("extracts every distinct token in the markdown", () => {
51
+ const markdown: string = `![a](${urlFor("aaa111")}) and ![b](${urlFor("bbb222")})`;
52
+
53
+ expect(extractImageAccessTokens(markdown)).toEqual(["aaa111", "bbb222"]);
54
+ });
55
+
56
+ it("de-duplicates a token referenced more than once", () => {
57
+ const markdown: string = `![a](${urlFor("aaa111")}) again ![a](${urlFor("aaa111")})`;
58
+
59
+ expect(extractImageAccessTokens(markdown)).toEqual(["aaa111"]);
60
+ });
61
+
62
+ it("ignores urls that are not access-token image urls", () => {
63
+ const markdown: string = `![logo](https://example.com/file/image/abc123) ![x](https://example.com/img.png)`;
64
+
65
+ expect(extractImageAccessTokens(markdown)).toEqual([]);
66
+ });
67
+
68
+ it("only matches hex tokens", () => {
69
+ expect(extractImageAccessTokens(`![a](${urlFor("zzzznothex")})`)).toEqual(
70
+ [],
71
+ );
72
+ });
73
+ });
74
+
75
+ describe("setIsPublicForMarkdownImages", () => {
76
+ beforeEach(() => {
77
+ jest.clearAllMocks();
78
+ });
79
+
80
+ it("does nothing when the markdown has no inline images", async () => {
81
+ await setIsPublicForMarkdownImages("no images here", true);
82
+
83
+ expect(FileService.findOneBy).not.toHaveBeenCalled();
84
+ expect(FileService.updateOneById).not.toHaveBeenCalled();
85
+ });
86
+
87
+ it("flips every referenced file to public", async () => {
88
+ (FileService.findOneBy as unknown as jest.Mock).mockResolvedValue({
89
+ _id: "11111111-1111-1111-1111-111111111111",
90
+ } as never);
91
+
92
+ await setIsPublicForMarkdownImages(
93
+ `![a](${urlFor("aaa111")}) ![b](${urlFor("bbb222")})`,
94
+ true,
95
+ );
96
+
97
+ expect(FileService.updateOneById).toHaveBeenCalledTimes(2);
98
+ expect(
99
+ (FileService.updateOneById as unknown as jest.Mock).mock.calls[0]?.[0],
100
+ ).toMatchObject({
101
+ data: { isPublic: true },
102
+ });
103
+ });
104
+
105
+ it("flips files back to private when told to", async () => {
106
+ (FileService.findOneBy as unknown as jest.Mock).mockResolvedValue({
107
+ _id: "11111111-1111-1111-1111-111111111111",
108
+ } as never);
109
+
110
+ await setIsPublicForMarkdownImages(`![a](${urlFor("aaa111")})`, false);
111
+
112
+ expect(
113
+ (FileService.updateOneById as unknown as jest.Mock).mock.calls[0]?.[0],
114
+ ).toMatchObject({
115
+ data: { isPublic: false },
116
+ });
117
+ });
118
+
119
+ it("skips tokens with no matching file", async () => {
120
+ (FileService.findOneBy as unknown as jest.Mock).mockResolvedValue(
121
+ null as never,
122
+ );
123
+
124
+ await setIsPublicForMarkdownImages(`![a](${urlFor("aaa111")})`, true);
125
+
126
+ expect(FileService.updateOneById).not.toHaveBeenCalled();
127
+ });
128
+
129
+ /*
130
+ * One unreadable file must not strand the rest of the note's images in the
131
+ * wrong visibility — a half-published note is worse than a slow one.
132
+ */
133
+ it("keeps going when one token fails", async () => {
134
+ (FileService.findOneBy as unknown as jest.Mock)
135
+ .mockRejectedValueOnce(new Error("db down") as never)
136
+ .mockResolvedValueOnce({
137
+ _id: "22222222-2222-2222-2222-222222222222",
138
+ } as never);
139
+
140
+ await setIsPublicForMarkdownImages(
141
+ `![a](${urlFor("aaa111")}) ![b](${urlFor("bbb222")})`,
142
+ true,
143
+ );
144
+
145
+ expect(FileService.updateOneById).toHaveBeenCalledTimes(1);
146
+ });
147
+ });
148
+
149
+ describe("syncIsPublicForMarkdownImages", () => {
150
+ beforeEach(() => {
151
+ jest.clearAllMocks();
152
+ });
153
+
154
+ it("flips images public like the unwrapped call", async () => {
155
+ (FileService.findOneBy as unknown as jest.Mock).mockResolvedValue({
156
+ _id: "11111111-1111-1111-1111-111111111111",
157
+ } as never);
158
+
159
+ await syncIsPublicForMarkdownImages(
160
+ `![a](${urlFor("aaa111")})`,
161
+ true,
162
+ "test context",
163
+ );
164
+
165
+ expect(
166
+ (FileService.updateOneById as unknown as jest.Mock).mock.calls[0]?.[0],
167
+ ).toMatchObject({
168
+ data: { isPublic: true },
169
+ });
170
+ });
171
+
172
+ /*
173
+ * Visibility sync is best-effort by design: publishing a note must not fail
174
+ * because an image could not be flipped.
175
+ */
176
+ it("never throws, even when the markdown itself is unusable", async () => {
177
+ await expect(
178
+ syncIsPublicForMarkdownImages(
179
+ 123 as unknown as string,
180
+ true,
181
+ "test context",
182
+ ),
183
+ ).resolves.toBeUndefined();
184
+ });
185
+
186
+ it("never throws when the update itself fails", async () => {
187
+ (FileService.findOneBy as unknown as jest.Mock).mockResolvedValue({
188
+ _id: "11111111-1111-1111-1111-111111111111",
189
+ } as never);
190
+ (FileService.updateOneById as unknown as jest.Mock).mockRejectedValue(
191
+ new Error("write failed") as never,
192
+ );
193
+
194
+ await expect(
195
+ syncIsPublicForMarkdownImages(
196
+ `![a](${urlFor("aaa111")})`,
197
+ true,
198
+ "test context",
199
+ ),
200
+ ).resolves.toBeUndefined();
201
+ });
202
+ });