@oneuptime/common 14.0.6 → 14.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (556) hide show
  1. package/Models/DatabaseModels/Incident.ts +267 -1
  2. package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
  3. package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
  4. package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
  5. package/Models/DatabaseModels/InventoryItem.ts +23 -12
  6. package/Models/DatabaseModels/StatusPage.ts +55 -0
  7. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  8. package/Server/API/IncidentAPI.ts +162 -0
  9. package/Server/API/StatusPageAPI.ts +487 -171
  10. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  20. package/Server/Infrastructure/Semaphore.ts +10 -0
  21. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  22. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  23. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  24. package/Server/Services/AlertService.ts +32 -36
  25. package/Server/Services/DatabaseService.ts +93 -10
  26. package/Server/Services/IncidentAlertService.ts +609 -2
  27. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  28. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  29. package/Server/Services/IncidentEpisodeService.ts +15 -0
  30. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  31. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  32. package/Server/Services/IncidentService.ts +1663 -103
  33. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  34. package/Server/Services/IncidentTemplateService.ts +109 -0
  35. package/Server/Services/InventoryItemService.ts +38 -0
  36. package/Server/Services/IoTDeviceCredentialService.ts +13 -4
  37. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  38. package/Server/Services/ScheduledMaintenanceService.ts +118 -64
  39. package/Server/Services/SmsService.ts +10 -0
  40. package/Server/Services/StatusPageService.ts +65 -8
  41. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  42. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  43. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  44. package/Server/Services/UserNotificationRuleService.ts +55 -53
  45. package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
  46. package/Server/Types/Markdown.ts +153 -6
  47. package/Server/Types/Workflow/Components/Email.ts +61 -2
  48. package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
  49. package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
  50. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  51. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  52. package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
  53. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  54. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  55. package/Server/Utils/Response.ts +31 -0
  56. package/Server/Utils/SSRFProtection.ts +76 -16
  57. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  58. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  59. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  60. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  61. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  62. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  63. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  64. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  65. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  66. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  67. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  68. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  69. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  70. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  71. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  72. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  73. package/Server/Utils/StatusPageResource.ts +51 -20
  74. package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
  75. package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
  76. package/Server/Utils/Topology/TopologyRequest.ts +463 -0
  77. package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
  78. package/Server/Utils/Topology/TopologySql.ts +1024 -0
  79. package/Server/Utils/VM/VMAPI.ts +1 -0
  80. package/Server/Utils/VM/VMRunner.ts +10 -0
  81. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  82. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  83. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  84. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  85. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  86. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  87. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  88. package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
  89. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
  90. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
  91. package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
  92. package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
  93. package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
  94. package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
  95. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  96. package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
  97. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
  98. package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
  99. package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
  100. package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
  101. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  102. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  103. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +534 -6
  104. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
  105. package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
  106. package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
  107. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  108. package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
  109. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  110. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  111. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  112. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  113. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  114. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  115. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  116. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  117. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  118. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  119. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  120. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
  121. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
  122. package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
  123. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  124. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
  125. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
  126. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  127. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  128. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  129. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  130. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  131. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  132. package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
  133. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
  134. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
  135. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  136. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  137. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  138. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  139. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  140. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  141. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  142. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  143. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  144. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  145. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  146. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  147. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  148. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  149. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  150. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  151. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  152. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  153. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
  154. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  155. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  156. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  157. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  158. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  159. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  160. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  161. package/Tests/Server/Services/DomainService.test.ts +525 -0
  162. package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
  163. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
  164. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  165. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  166. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  167. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  168. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  169. package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
  170. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  171. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  172. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  173. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  174. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  175. package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
  176. package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
  177. package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
  178. package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
  179. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  180. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  181. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  182. package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
  183. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  184. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  185. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  186. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  187. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  188. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  189. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  190. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  191. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  192. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  193. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  194. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  195. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  196. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  197. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  198. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  199. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  200. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  201. package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
  202. package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
  203. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  204. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  205. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  206. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  207. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  208. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  209. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  210. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  211. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  212. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  213. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  214. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  215. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  216. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  217. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  218. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  219. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  220. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  221. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  222. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  223. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  224. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  225. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  226. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  227. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  228. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  229. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  230. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  231. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
  232. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
  233. package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
  234. package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
  235. package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
  236. package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
  237. package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
  238. package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
  239. package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
  240. package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
  241. package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
  242. package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
  243. package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
  244. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  245. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  246. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  247. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  248. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  249. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  250. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  251. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  252. package/Tests/Types/SafeHtml.test.ts +102 -0
  253. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  254. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  255. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  256. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  257. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  258. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  259. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  260. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  261. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  262. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  263. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  264. package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
  265. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  266. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  267. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  268. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  269. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  270. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  271. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  272. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
  273. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  274. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  275. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  276. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  277. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  278. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  279. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  280. package/Types/CustomField/CustomFieldType.ts +14 -0
  281. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  282. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  283. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  284. package/Types/Incident/IncidentAlertLink.ts +19 -0
  285. package/Types/SafeHtml.ts +93 -0
  286. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  287. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  288. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  289. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  290. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  291. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  292. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  293. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  294. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  295. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  296. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  297. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  298. package/Types/Telemetry/InventoryLiveness.ts +26 -0
  299. package/Types/Topology/TopologyApi.ts +428 -0
  300. package/Types/Topology/TopologyTypeRules.ts +191 -0
  301. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  302. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  303. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  304. package/UI/Components/Forms/ModelForm.tsx +70 -8
  305. package/UI/Components/Forms/Validation.ts +1 -1
  306. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  307. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  308. package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
  309. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  310. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  311. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  312. package/Utils/API.ts +247 -2
  313. package/Utils/Incident/IncidentFromAlerts.ts +78 -0
  314. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  315. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  316. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  317. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  318. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  319. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  320. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  321. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  322. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  323. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  324. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  325. package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
  326. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  327. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  328. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  329. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  330. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  331. package/build/dist/Server/API/IncidentAPI.js +100 -1
  332. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  333. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  334. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  335. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  336. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  337. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
  338. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
  339. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  340. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  341. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  342. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  343. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  344. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  345. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  346. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  347. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  348. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  349. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  350. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  351. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  352. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  353. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  354. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  355. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  356. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  357. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  358. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  359. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  360. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  361. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  362. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  363. package/build/dist/Server/Services/AlertService.js +21 -33
  364. package/build/dist/Server/Services/AlertService.js.map +1 -1
  365. package/build/dist/Server/Services/DatabaseService.js +56 -7
  366. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  367. package/build/dist/Server/Services/IncidentAlertService.js +457 -3
  368. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
  369. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  370. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  371. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  372. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  373. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  374. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  375. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  376. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  377. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  378. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  379. package/build/dist/Server/Services/IncidentService.js +1148 -42
  380. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  381. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  382. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  383. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  384. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  385. package/build/dist/Server/Services/InventoryItemService.js +31 -0
  386. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  387. package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
  388. package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
  389. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  390. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  391. package/build/dist/Server/Services/ScheduledMaintenanceService.js +84 -57
  392. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  393. package/build/dist/Server/Services/SmsService.js +2 -22
  394. package/build/dist/Server/Services/SmsService.js.map +1 -1
  395. package/build/dist/Server/Services/StatusPageService.js +47 -9
  396. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  397. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  398. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  399. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  400. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  401. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  402. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  403. package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
  404. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  405. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
  406. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  407. package/build/dist/Server/Types/Markdown.js +110 -6
  408. package/build/dist/Server/Types/Markdown.js.map +1 -1
  409. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  410. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  411. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
  412. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
  413. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
  414. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
  415. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  416. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  417. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  418. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  419. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
  420. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
  421. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  422. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  425. package/build/dist/Server/Utils/Response.js +30 -0
  426. package/build/dist/Server/Utils/Response.js.map +1 -1
  427. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  428. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  429. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  430. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  431. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  432. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  433. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  434. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  435. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  436. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  437. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  438. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  439. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  440. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  441. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  442. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  443. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  444. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  445. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  446. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  447. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  448. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  449. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  450. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  451. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  452. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  453. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  454. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  455. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  456. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  457. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  458. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  459. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  460. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  461. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  462. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  463. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
  464. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
  465. package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
  466. package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
  467. package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
  468. package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
  469. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
  470. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
  471. package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
  472. package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
  473. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  474. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  475. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  476. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  477. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  478. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  479. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  480. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  481. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  482. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  483. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  484. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  485. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  486. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  487. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  488. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  489. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  490. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  491. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  492. package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
  493. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
  494. package/build/dist/Types/SafeHtml.js +84 -0
  495. package/build/dist/Types/SafeHtml.js.map +1 -0
  496. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  497. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  498. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  499. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  500. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  501. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  502. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  503. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  504. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  505. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  506. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  507. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  508. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  509. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  510. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  511. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  512. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  513. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  514. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  515. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  516. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  517. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  518. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  519. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  520. package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
  521. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
  522. package/build/dist/Types/Topology/TopologyApi.js +55 -0
  523. package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
  524. package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
  525. package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
  526. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  527. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  528. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  529. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  530. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  531. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  532. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  533. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  534. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  535. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  536. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  537. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  538. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  539. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  540. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  541. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  542. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  543. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  544. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  545. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  546. package/build/dist/Utils/API.js +177 -1
  547. package/build/dist/Utils/API.js.map +1 -1
  548. package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
  549. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
  550. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  551. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  552. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  553. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  554. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  555. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  556. package/package.json +1 -1
@@ -0,0 +1,1049 @@
1
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
2
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
3
+ import Email from "../../../Types/Email";
4
+ import ObjectID from "../../../Types/ObjectID";
5
+ import Phone from "../../../Types/Phone";
6
+ import StatusPageSubscriberNotificationMethod from "../../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
7
+ import StatusPageSubscriberUnsubscribe from "../../../Types/StatusPage/StatusPageSubscriberUnsubscribe";
8
+ import { escapeMarkdownInline } from "../../../Utils/Markdown/MarkdownEscape";
9
+ import logger, { EXTERNAL_FAULT, LogAttributes } from "../Logger";
10
+ import {
11
+ ExcludedStatusPage,
12
+ StatusPageExclusionReason,
13
+ } from "./StatusPageExclusion";
14
+ import SubscriberNotificationTiming from "./SubscriberNotificationTiming";
15
+
16
+ /*
17
+ * What one subscriber send - an incident created, a state change, a public
18
+ * note, a postmortem, or the episode equivalents - did on each status page it
19
+ * went to. It does three jobs for the send:
20
+ *
21
+ * - Email and SMS once per address. For an incident limited to specific
22
+ * status pages (see IncidentStatusPageScope), someone subscribed on two of
23
+ * the selected pages - a regional manager on Site 03 and Site 07 - gets one
24
+ * email, not two. Pages are visited in name order, so the first page's
25
+ * template, branding and unsubscribe link are the ones they get. Webhook,
26
+ * Slack and Teams deliveries are never merged: their payloads carry the
27
+ * page they are for (statusPageId, resourcesAffected, unsubscribeUrl), and
28
+ * an integration listening per page must see every page. An unscoped
29
+ * incident sends exactly as it always has, one message per subscription.
30
+ * The addresses are remembered for this send only. An address counts as
31
+ * sent only once its message was: one whose message failed - its page's
32
+ * own SMTP or Twilio is down, say - or whose handler threw before sending
33
+ * is freed again (deliver, releaseUndeliveredAddresses), so a later page
34
+ * that has it sends it rather than counting it as already sent.
35
+ * - Delivery (deliver): each message is awaited, and counted sent or failed.
36
+ * A failure is a send that throws (an unreachable host, a refused URL), one
37
+ * that answers with an HTTPErrorResponse (the Notification service's email
38
+ * and SMS endpoints, and the Slack and webhook senders, return one rather
39
+ * than throw), or one still unanswered after
40
+ * SubscriberNotificationTiming.SEND_TIMEOUT_IN_MS. The SMS endpoint
41
+ * answers success for an SMS the project deliberately does not send -
42
+ * SMS notifications turned off, too little SMS balance - unless the
43
+ * request asks otherwise, so the jobs send with SmsService's
44
+ * failIfNotSent: such an SMS then answers with an error and counts as
45
+ * failed, and a page reached that way is not recorded as told.
46
+ * - A record of it: for the incident (or episode) feed item, each page, the
47
+ * subject its email went out with, and how many messages were sent and
48
+ * failed on each channel, plus the pages that were left out and why; and
49
+ * the same counts, in plain text, for the notification's status message.
50
+ * A page "succeeded" when every one of its subscribers was reached and no
51
+ * message to them failed; the incident created notification records only
52
+ * those pages as told, so Retry resumes after them.
53
+ */
54
+
55
+ export enum StatusPageDeliverySkipReason {
56
+ // The status page does not show incidents (showIncidentsOnStatusPage off).
57
+ HidesIncidents = "HidesIncidents",
58
+ // The status page does not show episodes (showEpisodesOnStatusPage off).
59
+ HidesEpisodes = "HidesEpisodes",
60
+ // Already sent this notification (Incident.statusPagesNotifiedOnCreation).
61
+ AlreadyNotified = "AlreadyNotified",
62
+ // Sending to this page failed part-way.
63
+ Failed = "Failed",
64
+ /*
65
+ * The send ran out of time (SubscriberNotificationTiming.SEND_WINDOW_IN_MS)
66
+ * before it reached every subscriber of this page, or before it reached
67
+ * the page at all.
68
+ */
69
+ OutOfTime = "OutOfTime",
70
+ }
71
+
72
+ /*
73
+ * What Retry does after a send that did not reach everyone, for the status
74
+ * message to say so.
75
+ */
76
+ export enum SubscriberNotificationRetryScope {
77
+ /*
78
+ * The incident created notification: Retry sends only to the pages not
79
+ * sent it in full (Incident.statusPagesNotifiedOnCreation).
80
+ */
81
+ PagesNotYetSent = "PagesNotYetSent",
82
+ // Everything else: Retry sends to every page again.
83
+ EveryPage = "EveryPage",
84
+ }
85
+
86
+ type DedupedMethod =
87
+ | StatusPageSubscriberNotificationMethod.Email
88
+ | StatusPageSubscriberNotificationMethod.SMS;
89
+
90
+ /*
91
+ * An address this send has taken for one page: reserved when that page's
92
+ * handler asked to send to it, sent once its message was.
93
+ */
94
+ interface AddressReservation {
95
+ statusPageId: string;
96
+ sent: boolean;
97
+ }
98
+
99
+ interface StatusPageDelivery {
100
+ statusPageId: string;
101
+ statusPageName: string;
102
+ skipReason?: StatusPageDeliverySkipReason | undefined;
103
+ subject?: string | undefined;
104
+ // The send got as far as reading this page's subscribers.
105
+ started: boolean;
106
+ // Every subscriber of the page was reached.
107
+ finished: boolean;
108
+ sent: Map<StatusPageSubscriberNotificationMethod, number>;
109
+ failed: Map<StatusPageSubscriberNotificationMethod, number>;
110
+ // Email and SMS not sent again: the address already had it in this send.
111
+ alreadySent: Map<DedupedMethod, number>;
112
+ }
113
+
114
+ // The order channels are listed in, and what each is called.
115
+ const METHOD_LABELS: Array<[StatusPageSubscriberNotificationMethod, string]> = [
116
+ [StatusPageSubscriberNotificationMethod.Email, "email"],
117
+ [StatusPageSubscriberNotificationMethod.SMS, "SMS"],
118
+ [StatusPageSubscriberNotificationMethod.Slack, "Slack"],
119
+ [StatusPageSubscriberNotificationMethod.MicrosoftTeams, "Microsoft Teams"],
120
+ [StatusPageSubscriberNotificationMethod.Webhook, "webhook"],
121
+ ];
122
+
123
+ // Why a page was passed over, for a page the send never started on.
124
+ const SKIP_REASON_TEXT: Record<StatusPageDeliverySkipReason, string> = {
125
+ [StatusPageDeliverySkipReason.HidesIncidents]:
126
+ "not sent, this status page does not show incidents",
127
+ [StatusPageDeliverySkipReason.HidesEpisodes]:
128
+ "not sent, this status page does not show episodes",
129
+ [StatusPageDeliverySkipReason.AlreadyNotified]:
130
+ "not sent again, its subscribers were already sent this notification",
131
+ [StatusPageDeliverySkipReason.Failed]:
132
+ "Sending to this status page failed part-way",
133
+ [StatusPageDeliverySkipReason.OutOfTime]:
134
+ "not sent, the send ran out of time before it reached this status page",
135
+ };
136
+
137
+ // Said after the counts of a page the send stopped part-way through.
138
+ const STOPPED_PART_WAY_TEXT: Partial<
139
+ Record<StatusPageDeliverySkipReason, string>
140
+ > = {
141
+ [StatusPageDeliverySkipReason.Failed]:
142
+ "Sending to this status page failed part-way, so some of its subscribers may not have been sent it.",
143
+ [StatusPageDeliverySkipReason.OutOfTime]:
144
+ "The send ran out of time part-way through this status page, so some of its subscribers were not sent it.",
145
+ };
146
+
147
+ const EXCLUSION_HEADINGS: Record<
148
+ StatusPageExclusionReason,
149
+ { one: string; many: string }
150
+ > = {
151
+ [StatusPageExclusionReason.OutsideIncidentScope]: {
152
+ one: "status page outside the status pages this is limited to",
153
+ many: "status pages outside the status pages this is limited to",
154
+ },
155
+ [StatusPageExclusionReason.OnlyShowsScopedIncidents]: {
156
+ one: "status page that only shows incidents limited to it",
157
+ many: "status pages that only show incidents limited to them",
158
+ },
159
+ };
160
+
161
+ // How many names a list of left-out pages shows before summing up the rest.
162
+ const MAX_LISTED_EXCLUDED_PAGES: number = 25;
163
+
164
+ // How many pages the status message lists before summing up the rest.
165
+ const MAX_LISTED_STATUS_MESSAGE_PAGES: number = 25;
166
+
167
+ export default class SubscriberNotificationDeliveryRecord {
168
+ public readonly dedupeEmailAndSms: boolean;
169
+
170
+ private readonly sendTimeoutInMs: number;
171
+ private readonly deliveries: Array<StatusPageDelivery> = [];
172
+ private readonly sentEmails: Map<string, AddressReservation> = new Map();
173
+ private readonly sentPhones: Map<string, AddressReservation> = new Map();
174
+ private readonly excludedStatusPages: Array<ExcludedStatusPage> = [];
175
+ private subscriberMatched: boolean = false;
176
+
177
+ public constructor(data: {
178
+ dedupeEmailAndSms: boolean;
179
+ // How long a message may go unanswered before it counts as failed.
180
+ sendTimeoutInMs?: number | undefined;
181
+ }) {
182
+ this.dedupeEmailAndSms = data.dedupeEmailAndSms;
183
+ this.sendTimeoutInMs =
184
+ data.sendTimeoutInMs ?? SubscriberNotificationTiming.SEND_TIMEOUT_IN_MS;
185
+ }
186
+
187
+ // The name a status page goes by in the feed: its name in the dashboard.
188
+ public static getStatusPageName(statusPage: StatusPage): string {
189
+ return (
190
+ statusPage.name?.trim() ||
191
+ statusPage.pageTitle?.trim() ||
192
+ "Untitled status page"
193
+ );
194
+ }
195
+
196
+ // The form addresses are compared in: trimmed, lower-cased.
197
+ public static normalizeEmail(email: Email | string): string {
198
+ return email.toString().trim().toLowerCase();
199
+ }
200
+
201
+ // The form phone numbers are compared in: digits, with a leading +.
202
+ public static normalizePhone(phone: Phone | string): string {
203
+ const text: string = phone.toString().trim();
204
+ const digits: string = text.replace(/\D/g, "");
205
+
206
+ return text.startsWith("+") ? `+${digits}` : digits;
207
+ }
208
+
209
+ // A status page the send is about to go to, in the order they are visited.
210
+ public startStatusPage(statusPage: StatusPage): void {
211
+ this.getOrCreateDelivery(statusPage).started = true;
212
+ }
213
+
214
+ // Every subscriber of a status page the send started on was reached.
215
+ public finishStatusPage(statusPage: StatusPage): void {
216
+ this.getOrCreateDelivery(statusPage).finished = true;
217
+ }
218
+
219
+ // A status page the send passed over or stopped part-way through, and why.
220
+ public skipStatusPage(
221
+ statusPage: StatusPage,
222
+ reason: StatusPageDeliverySkipReason,
223
+ ): void {
224
+ this.getOrCreateDelivery(statusPage).skipReason = reason;
225
+ }
226
+
227
+ /*
228
+ * A subscriber whose preferences let this send through: it is sent
229
+ * something, unless its address already was through an earlier page.
230
+ */
231
+ public recordSubscriberMatched(): void {
232
+ this.subscriberMatched = true;
233
+ }
234
+
235
+ // Whether any subscriber's preferences let this send through.
236
+ public hasMatchedAnySubscriber(): boolean {
237
+ return this.subscriberMatched;
238
+ }
239
+
240
+ // Pages the incident's monitors reach that its scope left out.
241
+ public addExcludedStatusPages(
242
+ excludedStatusPages: Array<ExcludedStatusPage>,
243
+ ): void {
244
+ this.excludedStatusPages.push(...excludedStatusPages);
245
+ }
246
+
247
+ /*
248
+ * Whether to email this address on this page. With dedupe on, an address
249
+ * already emailed in this send is not emailed again, and that is counted
250
+ * against the page; otherwise always yes.
251
+ */
252
+ public shouldSendEmail(data: {
253
+ statusPage: StatusPage;
254
+ email: Email | string;
255
+ }): boolean {
256
+ return this.claim({
257
+ statusPage: data.statusPage,
258
+ address: SubscriberNotificationDeliveryRecord.normalizeEmail(data.email),
259
+ sent: this.sentEmails,
260
+ method: StatusPageSubscriberNotificationMethod.Email,
261
+ });
262
+ }
263
+
264
+ // As shouldSendEmail, for a phone number and SMS.
265
+ public shouldSendSms(data: {
266
+ statusPage: StatusPage;
267
+ phone: Phone | string;
268
+ }): boolean {
269
+ return this.claim({
270
+ statusPage: data.statusPage,
271
+ address: SubscriberNotificationDeliveryRecord.normalizePhone(data.phone),
272
+ sent: this.sentPhones,
273
+ method: StatusPageSubscriberNotificationMethod.SMS,
274
+ });
275
+ }
276
+
277
+ /*
278
+ * Send one message and wait for the answer, counting it sent or failed on
279
+ * its page and channel. True when it was sent. Never throws: a failed
280
+ * message is recorded and logged, and the send goes on to the next one.
281
+ *
282
+ * An email passes its subject; the first one on a page is the one the
283
+ * feed shows. An email or SMS passes `to`, the address shouldSendEmail or
284
+ * shouldSendSms let through: sent, it stays taken for the rest of this
285
+ * send; failed, it is freed, so a later page that has it sends it.
286
+ */
287
+ public async deliver(data: {
288
+ statusPage: StatusPage;
289
+ method: StatusPageSubscriberNotificationMethod;
290
+ subject?: string | undefined;
291
+ to?: Email | Phone | string | undefined;
292
+ send: () => Promise<unknown>;
293
+ logAttributes?: LogAttributes | undefined;
294
+ }): Promise<boolean> {
295
+ let failed: boolean = false;
296
+ let failure: unknown = undefined;
297
+
298
+ try {
299
+ const result: unknown = await this.waitForAnswer(data.send);
300
+
301
+ if (result instanceof HTTPErrorResponse) {
302
+ failed = true;
303
+ failure = result;
304
+ }
305
+ } catch (err) {
306
+ // Whatever was thrown, even nothing at all.
307
+ failed = true;
308
+ failure = err;
309
+ }
310
+
311
+ this.settleAddress({
312
+ method: data.method,
313
+ to: data.to,
314
+ sent: !failed,
315
+ });
316
+
317
+ if (!failed) {
318
+ this.recordSent({
319
+ statusPage: data.statusPage,
320
+ method: data.method,
321
+ subject: data.subject,
322
+ });
323
+
324
+ return true;
325
+ }
326
+
327
+ this.recordFailed({
328
+ statusPage: data.statusPage,
329
+ method: data.method,
330
+ subject: data.subject,
331
+ });
332
+
333
+ /*
334
+ * Delivery to a channel the SUBSCRIBER chose: their mailbox, their
335
+ * phone, their Slack/Teams webhook, their HTTP endpoint. A bounce, a
336
+ * 404 on a deleted webhook or an unreachable host is their side of the
337
+ * wire, not a OneUptime defect - and one status page can fan out to
338
+ * thousands of subscribers, so leaving these at ERROR buries real
339
+ * failures under a single tenant's dead webhook. What failed is counted
340
+ * in the record; the error itself (which can carry a subscriber's
341
+ * webhook URL) stays in the server log.
342
+ */
343
+ logger.error(
344
+ `Subscriber ${data.method} notification failed on status page ${this.normalizeId(
345
+ data.statusPage._id || data.statusPage.id?.toString() || "",
346
+ )}: ${describeFailure(failure)}`,
347
+ {
348
+ ...EXTERNAL_FAULT,
349
+ ...(data.logAttributes || {}),
350
+ },
351
+ );
352
+
353
+ return false;
354
+ }
355
+
356
+ // A message sent on a status page.
357
+ public recordSent(data: {
358
+ statusPage: StatusPage;
359
+ method: StatusPageSubscriberNotificationMethod;
360
+ subject?: string | undefined;
361
+ }): void {
362
+ const delivery: StatusPageDelivery = this.getOrCreateDelivery(
363
+ data.statusPage,
364
+ );
365
+
366
+ increment(delivery.sent, data.method);
367
+ this.recordSubject(delivery, data.subject);
368
+ }
369
+
370
+ // A message that could not be sent on a status page.
371
+ public recordFailed(data: {
372
+ statusPage: StatusPage;
373
+ method: StatusPageSubscriberNotificationMethod;
374
+ subject?: string | undefined;
375
+ }): void {
376
+ const delivery: StatusPageDelivery = this.getOrCreateDelivery(
377
+ data.statusPage,
378
+ );
379
+
380
+ increment(delivery.failed, data.method);
381
+ this.recordSubject(delivery, data.subject);
382
+ }
383
+
384
+ // How many messages were sent on a status page, on one channel or all.
385
+ public getSentCount(
386
+ statusPageId: ObjectID | string,
387
+ method?: StatusPageSubscriberNotificationMethod,
388
+ ): number {
389
+ return countOf(this.findDelivery(statusPageId)?.sent, method);
390
+ }
391
+
392
+ // How many messages failed on a status page, on one channel or all.
393
+ public getFailedCount(
394
+ statusPageId: ObjectID | string,
395
+ method?: StatusPageSubscriberNotificationMethod,
396
+ ): number {
397
+ return countOf(this.findDelivery(statusPageId)?.failed, method);
398
+ }
399
+
400
+ // The subject the email on a status page went out with, if one did.
401
+ public getSubject(statusPageId: ObjectID | string): string | undefined {
402
+ return this.findDelivery(statusPageId)?.subject;
403
+ }
404
+
405
+ // Whether any message was sent or tried, on any page.
406
+ public hasAttemptedAny(): boolean {
407
+ return this.deliveries.some((delivery: StatusPageDelivery): boolean => {
408
+ return countOf(delivery.sent) + countOf(delivery.failed) > 0;
409
+ });
410
+ }
411
+
412
+ /*
413
+ * Whether the send fell short anywhere: a message failed, a page failed
414
+ * part-way, or the send ran out of time before it reached everyone.
415
+ */
416
+ public hasFailures(): boolean {
417
+ return this.deliveries.some((delivery: StatusPageDelivery): boolean => {
418
+ return isShortfall(delivery);
419
+ });
420
+ }
421
+
422
+ /*
423
+ * Whether every subscriber of a status page was reached and nobody's
424
+ * message failed: the page was sent this in full, and a retry need not
425
+ * go back to it.
426
+ */
427
+ public didStatusPageSucceed(statusPageId: ObjectID | string): boolean {
428
+ const delivery: StatusPageDelivery | undefined =
429
+ this.findDelivery(statusPageId);
430
+
431
+ return Boolean(
432
+ delivery &&
433
+ delivery.started &&
434
+ delivery.finished &&
435
+ !isShortfall(delivery),
436
+ );
437
+ }
438
+
439
+ // How many messages were sent and failed across every page.
440
+ public getTotals(): { sent: number; failed: number } {
441
+ let sent: number = 0;
442
+ let failed: number = 0;
443
+
444
+ for (const delivery of this.deliveries) {
445
+ sent += countOf(delivery.sent);
446
+ failed += countOf(delivery.failed);
447
+ }
448
+
449
+ return { sent, failed };
450
+ }
451
+
452
+ /*
453
+ * The record as Markdown for the incident (or episode) feed item: one line
454
+ * per status page with what was sent and failed and the subject, the pages
455
+ * passed over and why, and the pages the scope left out. Empty when the
456
+ * send touched no status page at all.
457
+ */
458
+ public toMarkdown(): string {
459
+ const sections: Array<string> = [];
460
+
461
+ if (this.deliveries.length > 0) {
462
+ const lines: Array<string> = this.deliveries.map(
463
+ (delivery: StatusPageDelivery): string => {
464
+ return `- **${escapeMarkdownInline(delivery.statusPageName)}**: ${this.describeDelivery(delivery)}`;
465
+ },
466
+ );
467
+
468
+ sections.push(`**Status pages:**\n\n${lines.join("\n")}`);
469
+
470
+ if (this.dedupeEmailAndSms) {
471
+ sections.push(
472
+ "Email and SMS were sent once per address across these status pages, because this is limited to specific status pages. Someone subscribed on more than one of them got the message of the first page in this list.",
473
+ );
474
+ }
475
+ }
476
+
477
+ for (const reason of [
478
+ StatusPageExclusionReason.OutsideIncidentScope,
479
+ StatusPageExclusionReason.OnlyShowsScopedIncidents,
480
+ ]) {
481
+ const names: Array<string> = this.excludedStatusPages
482
+ .filter((excluded: ExcludedStatusPage): boolean => {
483
+ return excluded.reason === reason;
484
+ })
485
+ .map((excluded: ExcludedStatusPage): string => {
486
+ return escapeMarkdownInline(
487
+ SubscriberNotificationDeliveryRecord.getStatusPageName(
488
+ excluded.statusPage,
489
+ ),
490
+ );
491
+ });
492
+
493
+ if (names.length === 0) {
494
+ continue;
495
+ }
496
+
497
+ const listed: Array<string> = names.slice(0, MAX_LISTED_EXCLUDED_PAGES);
498
+ const more: number = names.length - listed.length;
499
+
500
+ sections.push(
501
+ `**Not sent to ${names.length} ${names.length === 1 ? EXCLUSION_HEADINGS[reason].one : EXCLUSION_HEADINGS[reason].many}:** ${listed.join(", ")}${more > 0 ? `, and ${more} more` : ""}.`,
502
+ );
503
+ }
504
+
505
+ return sections.join("\n\n");
506
+ }
507
+
508
+ /*
509
+ * The notification's status message, in plain text: whether everyone was
510
+ * sent it, then what was sent and failed on each status page the send
511
+ * went to - "Site 03: 41 email sent. Site 07: 16 email sent, 2 failed." -
512
+ * and, when it fell short, what Retry will do.
513
+ *
514
+ * `sentMessage` is the job's own line for a send that reached everyone.
515
+ * Pages passed over (they hide incidents, or were already sent it) are in
516
+ * the feed item, not here - unless the send went to no page at all. Then
517
+ * the message says so, and why: every page was left out by the scope,
518
+ * passed over, or there was none. "Sent successfully to all subscribers"
519
+ * would tell whoever reads the badge that subscribers heard when nobody
520
+ * did - the usual outcome, for an incident created on a monitor that only
521
+ * pages showing scoped incidents list.
522
+ */
523
+ public toStatusMessage(data: {
524
+ sentMessage: string;
525
+ retryScope: SubscriberNotificationRetryScope;
526
+ }): string {
527
+ if (!this.hasFailures() && !this.hasStartedAnyStatusPage()) {
528
+ return this.describeNoStatusPageSent();
529
+ }
530
+
531
+ const parts: Array<string> = [];
532
+ const { sent, failed } = this.getTotals();
533
+
534
+ if (!this.hasFailures()) {
535
+ parts.push(data.sentMessage);
536
+ } else {
537
+ parts.push(
538
+ failed > 0
539
+ ? `Not every subscriber was sent this notification: ${failed} of ${sent + failed} message${sent + failed === 1 ? "" : "s"} failed.`
540
+ : "Not every subscriber was sent this notification.",
541
+ );
542
+
543
+ if (
544
+ this.deliveries.some((delivery: StatusPageDelivery): boolean => {
545
+ return delivery.skipReason === StatusPageDeliverySkipReason.OutOfTime;
546
+ })
547
+ ) {
548
+ parts.push(
549
+ "The send ran out of time before it reached every subscriber.",
550
+ );
551
+ }
552
+
553
+ if (
554
+ this.deliveries.some((delivery: StatusPageDelivery): boolean => {
555
+ return delivery.skipReason === StatusPageDeliverySkipReason.Failed;
556
+ })
557
+ ) {
558
+ parts.push("Sending to a status page failed part-way.");
559
+ }
560
+ }
561
+
562
+ const reported: Array<StatusPageDelivery> = this.deliveries.filter(
563
+ (delivery: StatusPageDelivery): boolean => {
564
+ return delivery.started || isShortfall(delivery);
565
+ },
566
+ );
567
+ const listed: Array<StatusPageDelivery> = reported.slice(
568
+ 0,
569
+ MAX_LISTED_STATUS_MESSAGE_PAGES,
570
+ );
571
+
572
+ for (const delivery of listed) {
573
+ parts.push(
574
+ `${toPlainText(delivery.statusPageName)}: ${this.describeForStatusMessage(delivery)}.`,
575
+ );
576
+ }
577
+
578
+ if (reported.length > listed.length) {
579
+ parts.push(
580
+ `And ${reported.length - listed.length} more status page${reported.length - listed.length === 1 ? "" : "s"}.`,
581
+ );
582
+ }
583
+
584
+ if (this.hasFailures()) {
585
+ if (
586
+ data.retryScope === SubscriberNotificationRetryScope.PagesNotYetSent
587
+ ) {
588
+ const notSent: Array<string> = this.deliveries
589
+ .filter((delivery: StatusPageDelivery): boolean => {
590
+ return isShortfall(delivery);
591
+ })
592
+ .map((delivery: StatusPageDelivery): string => {
593
+ return toPlainText(delivery.statusPageName);
594
+ });
595
+
596
+ parts.push(
597
+ `Retry sends it again only to the status pages that were not sent it in full${notSent.length > 0 ? `: ${joinNames(notSent)}` : ""}.`,
598
+ );
599
+ } else {
600
+ parts.push(
601
+ "Retry sends it again to every status page, including the subscribers who already got it.",
602
+ );
603
+ }
604
+ }
605
+
606
+ return parts.join(" ");
607
+ }
608
+
609
+ // Whether the send went to any status page, whatever it then sent there.
610
+ private hasStartedAnyStatusPage(): boolean {
611
+ return this.deliveries.some((delivery: StatusPageDelivery): boolean => {
612
+ return delivery.started;
613
+ });
614
+ }
615
+
616
+ /*
617
+ * The status message of a send that went to no status page: the pages the
618
+ * scope left out, by reason, and the ones passed over, each with why.
619
+ */
620
+ private describeNoStatusPageSent(): string {
621
+ const parts: Array<string> = [
622
+ "Not sent to any subscriber: no status page was sent this notification.",
623
+ ];
624
+
625
+ const leftOut: Array<string> = [];
626
+
627
+ for (const reason of [
628
+ StatusPageExclusionReason.OutsideIncidentScope,
629
+ StatusPageExclusionReason.OnlyShowsScopedIncidents,
630
+ ]) {
631
+ const count: number = this.excludedStatusPages.filter(
632
+ (excluded: ExcludedStatusPage): boolean => {
633
+ return excluded.reason === reason;
634
+ },
635
+ ).length;
636
+
637
+ if (count > 0) {
638
+ leftOut.push(
639
+ `${count} ${count === 1 ? EXCLUSION_HEADINGS[reason].one : EXCLUSION_HEADINGS[reason].many}`,
640
+ );
641
+ }
642
+ }
643
+
644
+ if (leftOut.length > 0) {
645
+ parts.push(`Left out by its status page scope: ${leftOut.join("; ")}.`);
646
+ }
647
+
648
+ const passedOver: Array<StatusPageDelivery> = this.deliveries.filter(
649
+ (delivery: StatusPageDelivery): boolean => {
650
+ return isPassedOver(delivery);
651
+ },
652
+ );
653
+ const listed: Array<StatusPageDelivery> = passedOver.slice(
654
+ 0,
655
+ MAX_LISTED_STATUS_MESSAGE_PAGES,
656
+ );
657
+
658
+ for (const delivery of listed) {
659
+ parts.push(
660
+ `${toPlainText(delivery.statusPageName)}: ${SKIP_REASON_TEXT[delivery.skipReason!]}.`,
661
+ );
662
+ }
663
+
664
+ if (passedOver.length > listed.length) {
665
+ parts.push(
666
+ `And ${passedOver.length - listed.length} more status page${passedOver.length - listed.length === 1 ? "" : "s"}.`,
667
+ );
668
+ }
669
+
670
+ if (leftOut.length === 0 && passedOver.length === 0) {
671
+ parts.push("No status page lists what it is about.");
672
+ }
673
+
674
+ return parts.join(" ");
675
+ }
676
+
677
+ private describeDelivery(delivery: StatusPageDelivery): string {
678
+ if (isPassedOver(delivery)) {
679
+ return `${SKIP_REASON_TEXT[delivery.skipReason!]}.`;
680
+ }
681
+
682
+ let text: string = `${this.describeCounts(delivery)}.`;
683
+
684
+ const stoppedPartWay: string | undefined = delivery.skipReason
685
+ ? STOPPED_PART_WAY_TEXT[delivery.skipReason]
686
+ : undefined;
687
+
688
+ if (stoppedPartWay) {
689
+ text += ` ${stoppedPartWay}`;
690
+ }
691
+
692
+ const alreadyEmailed: number =
693
+ delivery.alreadySent.get(StatusPageSubscriberNotificationMethod.Email) ||
694
+ 0;
695
+ const alreadyTexted: number =
696
+ delivery.alreadySent.get(StatusPageSubscriberNotificationMethod.SMS) || 0;
697
+
698
+ const alreadySent: Array<string> = [];
699
+
700
+ if (alreadyEmailed > 0) {
701
+ alreadySent.push(
702
+ `${alreadyEmailed} email address${alreadyEmailed === 1 ? "" : "es"}`,
703
+ );
704
+ }
705
+
706
+ if (alreadyTexted > 0) {
707
+ alreadySent.push(
708
+ `${alreadyTexted} phone number${alreadyTexted === 1 ? "" : "s"}`,
709
+ );
710
+ }
711
+
712
+ if (alreadySent.length > 0) {
713
+ text += ` Not sent again to ${alreadySent.join(" and ")} already sent it through another status page.`;
714
+ }
715
+
716
+ if (delivery.subject !== undefined) {
717
+ text += ` Subject: "${escapeMarkdownInline(delivery.subject)}".`;
718
+ }
719
+
720
+ return text;
721
+ }
722
+
723
+ // One page's line in the status message, without the page's name.
724
+ private describeForStatusMessage(delivery: StatusPageDelivery): string {
725
+ if (isPassedOver(delivery)) {
726
+ return SKIP_REASON_TEXT[delivery.skipReason!];
727
+ }
728
+
729
+ const counts: string = this.describeCounts(delivery);
730
+
731
+ if (delivery.skipReason === StatusPageDeliverySkipReason.OutOfTime) {
732
+ return `${counts}, then the send ran out of time`;
733
+ }
734
+
735
+ if (delivery.skipReason === StatusPageDeliverySkipReason.Failed) {
736
+ return `${counts}, then sending failed part-way`;
737
+ }
738
+
739
+ return counts;
740
+ }
741
+
742
+ /*
743
+ * What was sent and what failed on a page, by channel: "41 email, 3
744
+ * webhook sent" or "16 email sent; 2 email, 1 SMS failed".
745
+ */
746
+ private describeCounts(delivery: StatusPageDelivery): string {
747
+ const sent: Array<string> = describeByChannel(delivery.sent);
748
+ const failed: Array<string> = describeByChannel(delivery.failed);
749
+
750
+ if (sent.length === 0 && failed.length === 0) {
751
+ return delivery.skipReason || countOf(delivery.alreadySent) > 0
752
+ ? "nothing sent"
753
+ : "nothing sent, no subscriber matched";
754
+ }
755
+
756
+ const sentText: string =
757
+ sent.length > 0 ? `${sent.join(", ")} sent` : "nothing sent";
758
+
759
+ return failed.length > 0
760
+ ? `${sentText}; ${failed.join(", ")} failed`
761
+ : sentText;
762
+ }
763
+
764
+ private recordSubject(
765
+ delivery: StatusPageDelivery,
766
+ subject: string | undefined,
767
+ ): void {
768
+ if (subject === undefined || delivery.subject !== undefined) {
769
+ return;
770
+ }
771
+
772
+ /*
773
+ * The feed is read by everyone who can read the incident. A custom
774
+ * subject template can carry {{unsubscribeUrl}}, whose token belongs to
775
+ * the one subscriber that email went to, so the feed keeps the link
776
+ * with its token redacted.
777
+ */
778
+ delivery.subject =
779
+ StatusPageSubscriberUnsubscribe.redactCredentials(subject);
780
+ }
781
+
782
+ /*
783
+ * The send's answer, or a rejection once it has gone unanswered for the
784
+ * send timeout. The request itself is not cancelled; this only stops
785
+ * waiting for it, so one hanging endpoint cannot hold a slot of the send
786
+ * forever.
787
+ */
788
+ private async waitForAnswer(send: () => Promise<unknown>): Promise<unknown> {
789
+ let timer: ReturnType<typeof setTimeout> | undefined;
790
+
791
+ try {
792
+ return await Promise.race([
793
+ send(),
794
+ new Promise<never>(
795
+ (_resolve: (value: never) => void, reject: (err: Error) => void) => {
796
+ timer = setTimeout(() => {
797
+ reject(
798
+ new Error(
799
+ `No answer after ${Math.round(this.sendTimeoutInMs / 1000)} seconds.`,
800
+ ),
801
+ );
802
+ }, this.sendTimeoutInMs);
803
+ },
804
+ ),
805
+ ]);
806
+ } finally {
807
+ if (timer !== undefined) {
808
+ clearTimeout(timer);
809
+ }
810
+ }
811
+ }
812
+
813
+ /*
814
+ * Free the addresses a page reserved and never sent to: its handler threw
815
+ * before sending, or the page stopped part-way. Called once every handler
816
+ * of the page has finished (SubscriberNotificationFanOut), so none of them
817
+ * is still sending; pages are visited one after another, so a later page
818
+ * that has one of these addresses then sends it.
819
+ */
820
+ public releaseUndeliveredAddresses(statusPage: StatusPage): void {
821
+ const statusPageId: string = this.normalizeId(
822
+ statusPage._id || statusPage.id?.toString() || "",
823
+ );
824
+
825
+ for (const reservations of [this.sentEmails, this.sentPhones]) {
826
+ for (const [address, reservation] of Array.from(reservations)) {
827
+ if (!reservation.sent && reservation.statusPageId === statusPageId) {
828
+ reservations.delete(address);
829
+ }
830
+ }
831
+ }
832
+ }
833
+
834
+ /*
835
+ * What a delivered email or SMS means for its address: taken for the rest
836
+ * of this send when it went out, freed when it failed.
837
+ */
838
+ private settleAddress(data: {
839
+ method: StatusPageSubscriberNotificationMethod;
840
+ to: Email | Phone | string | undefined;
841
+ sent: boolean;
842
+ }): void {
843
+ if (!this.dedupeEmailAndSms || data.to === undefined) {
844
+ return;
845
+ }
846
+
847
+ let reservations: Map<string, AddressReservation>;
848
+ let address: string;
849
+
850
+ if (data.method === StatusPageSubscriberNotificationMethod.Email) {
851
+ reservations = this.sentEmails;
852
+ address = SubscriberNotificationDeliveryRecord.normalizeEmail(
853
+ data.to.toString(),
854
+ );
855
+ } else if (data.method === StatusPageSubscriberNotificationMethod.SMS) {
856
+ reservations = this.sentPhones;
857
+ address = SubscriberNotificationDeliveryRecord.normalizePhone(
858
+ data.to.toString(),
859
+ );
860
+ } else {
861
+ return;
862
+ }
863
+
864
+ const reservation: AddressReservation | undefined =
865
+ reservations.get(address);
866
+
867
+ if (!reservation) {
868
+ return;
869
+ }
870
+
871
+ if (data.sent) {
872
+ reservation.sent = true;
873
+ return;
874
+ }
875
+
876
+ reservations.delete(address);
877
+ }
878
+
879
+ private claim(data: {
880
+ statusPage: StatusPage;
881
+ address: string;
882
+ sent: Map<string, AddressReservation>;
883
+ method: DedupedMethod;
884
+ }): boolean {
885
+ if (!this.dedupeEmailAndSms || !data.address) {
886
+ return true;
887
+ }
888
+
889
+ if (!data.sent.has(data.address)) {
890
+ data.sent.set(data.address, {
891
+ statusPageId: this.normalizeId(
892
+ data.statusPage._id || data.statusPage.id?.toString() || "",
893
+ ),
894
+ sent: false,
895
+ });
896
+ return true;
897
+ }
898
+
899
+ const delivery: StatusPageDelivery = this.getOrCreateDelivery(
900
+ data.statusPage,
901
+ );
902
+
903
+ increment(delivery.alreadySent, data.method);
904
+
905
+ return false;
906
+ }
907
+
908
+ private findDelivery(
909
+ statusPageId: ObjectID | string,
910
+ ): StatusPageDelivery | undefined {
911
+ const id: string = this.normalizeId(statusPageId);
912
+
913
+ return this.deliveries.find((item: StatusPageDelivery): boolean => {
914
+ return item.statusPageId === id;
915
+ });
916
+ }
917
+
918
+ private getOrCreateDelivery(statusPage: StatusPage): StatusPageDelivery {
919
+ const statusPageId: string = this.normalizeId(
920
+ statusPage._id || statusPage.id?.toString() || "",
921
+ );
922
+
923
+ let delivery: StatusPageDelivery | undefined =
924
+ this.findDelivery(statusPageId);
925
+
926
+ if (!delivery) {
927
+ delivery = {
928
+ statusPageId: statusPageId,
929
+ statusPageName:
930
+ SubscriberNotificationDeliveryRecord.getStatusPageName(statusPage),
931
+ started: false,
932
+ finished: false,
933
+ sent: new Map(),
934
+ failed: new Map(),
935
+ alreadySent: new Map(),
936
+ };
937
+
938
+ this.deliveries.push(delivery);
939
+ }
940
+
941
+ return delivery;
942
+ }
943
+
944
+ private normalizeId(id: ObjectID | string): string {
945
+ return id.toString().trim().toLowerCase();
946
+ }
947
+ }
948
+
949
+ /*
950
+ * A page the send never went to: it hides incidents or episodes, it was
951
+ * already sent this notification, or the send ran out of time before it got
952
+ * there. Its line is the reason alone.
953
+ */
954
+ function isPassedOver(delivery: StatusPageDelivery): boolean {
955
+ if (!delivery.skipReason) {
956
+ return false;
957
+ }
958
+
959
+ if (delivery.skipReason === StatusPageDeliverySkipReason.OutOfTime) {
960
+ return !delivery.started;
961
+ }
962
+
963
+ return delivery.skipReason !== StatusPageDeliverySkipReason.Failed;
964
+ }
965
+
966
+ /*
967
+ * A page the send fell short on: a message to one of its subscribers
968
+ * failed, sending to it failed part-way, or the send ran out of time before
969
+ * it reached every subscriber.
970
+ */
971
+ function isShortfall(delivery: StatusPageDelivery): boolean {
972
+ return (
973
+ countOf(delivery.failed) > 0 ||
974
+ delivery.skipReason === StatusPageDeliverySkipReason.Failed ||
975
+ delivery.skipReason === StatusPageDeliverySkipReason.OutOfTime
976
+ );
977
+ }
978
+
979
+ // "41 email, 3 webhook": each channel with a count, in METHOD_LABELS order.
980
+ function describeByChannel(
981
+ counts: Map<StatusPageSubscriberNotificationMethod, number>,
982
+ ): Array<string> {
983
+ return METHOD_LABELS.filter(
984
+ ([method]: [StatusPageSubscriberNotificationMethod, string]): boolean => {
985
+ return (counts.get(method) || 0) > 0;
986
+ },
987
+ ).map(([method, label]: [StatusPageSubscriberNotificationMethod, string]) => {
988
+ return `${counts.get(method)} ${label}`;
989
+ });
990
+ }
991
+
992
+ function increment<TKey>(counts: Map<TKey, number>, key: TKey): void {
993
+ counts.set(key, (counts.get(key) || 0) + 1);
994
+ }
995
+
996
+ function countOf<TKey>(
997
+ counts: Map<TKey, number> | undefined,
998
+ key?: TKey,
999
+ ): number {
1000
+ if (!counts) {
1001
+ return 0;
1002
+ }
1003
+
1004
+ if (key !== undefined) {
1005
+ return counts.get(key) || 0;
1006
+ }
1007
+
1008
+ let total: number = 0;
1009
+
1010
+ for (const count of counts.values()) {
1011
+ total += count;
1012
+ }
1013
+
1014
+ return total;
1015
+ }
1016
+
1017
+ // What went wrong with a message, for the server log.
1018
+ function describeFailure(failure: unknown): string {
1019
+ if (failure instanceof HTTPErrorResponse) {
1020
+ return `HTTP ${failure.statusCode}${failure.message ? `: ${failure.message}` : ""}`;
1021
+ }
1022
+
1023
+ if (failure instanceof Error) {
1024
+ return failure.message || failure.name;
1025
+ }
1026
+
1027
+ return String(failure);
1028
+ }
1029
+
1030
+ // "Site 03", "Site 03 and Site 07", "Site 03, Site 05 and Site 07", capped.
1031
+ function joinNames(names: Array<string>): string {
1032
+ const listed: Array<string> = names.slice(0, MAX_LISTED_STATUS_MESSAGE_PAGES);
1033
+ const more: number = names.length - listed.length;
1034
+
1035
+ if (more > 0) {
1036
+ return `${listed.join(", ")} and ${more} more`;
1037
+ }
1038
+
1039
+ if (listed.length <= 1) {
1040
+ return listed.join("");
1041
+ }
1042
+
1043
+ return `${listed.slice(0, -1).join(", ")} and ${listed[listed.length - 1]}`;
1044
+ }
1045
+
1046
+ // A page name on one line, for the plain-text status message.
1047
+ function toPlainText(text: string): string {
1048
+ return text.replace(/[\r\n]+/g, " ");
1049
+ }