@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,992 @@
1
+ import ProbeNetworkFailureUtil, {
2
+ ProbeNetworkFailure,
3
+ ProbeNetworkFailureKind,
4
+ ProbeNetworkOperation,
5
+ } from "../../Utils/ProbeNetworkFailureUtil";
6
+ import { describe, expect, test } from "@jest/globals";
7
+
8
+ /*
9
+ * A probe with no usable IPv6 fails every IPv6 check before a packet leaves
10
+ * the machine, and OneUptime used to report that as the customer's host
11
+ * being down. The strings below are the ones reproduced in Docker
12
+ * (node:26-bookworm-slim, iputils-ping 20221126, traceroute 2.1.2) for the
13
+ * customer's address, 2001:518:2800:9::2, which answers from anywhere that
14
+ * has IPv6.
15
+ */
16
+
17
+ const CUSTOMER_ADDRESS: string = "2001:518:2800:9::2";
18
+
19
+ const PING: ProbeNetworkOperation = ProbeNetworkOperation.PingOrTraceroute;
20
+ const TCP: ProbeNetworkOperation = ProbeNetworkOperation.TcpConnect;
21
+
22
+ // IPv6 switched off on the probe's loopback: THE CUSTOMER'S CASE.
23
+ const PING6_NO_SOURCE_ADDRESS: string =
24
+ "ping6: connect: Cannot assign requested address\n";
25
+ // A Docker bridge without --ipv6: the kernel has no IPv6 route at all.
26
+ const PING6_NO_ROUTE: string = "ping6: connect: Network is unreachable\n";
27
+ // A kernel booted with ipv6.disable=1.
28
+ const PING6_NO_FAMILY: string =
29
+ "ping6: socket: Address family not supported by protocol\n";
30
+
31
+ // A Node socket error, shaped the way net.connect rejects.
32
+ function nodeError(
33
+ code: string,
34
+ message: string,
35
+ address?: string,
36
+ ): Error & { code: string; address?: string } {
37
+ const error: Error & { code: string; address?: string } = Object.assign(
38
+ new Error(message),
39
+ { code: code },
40
+ );
41
+
42
+ if (address) {
43
+ error.address = address;
44
+ }
45
+
46
+ return error;
47
+ }
48
+
49
+ function classifyPingOutput(output: string): ProbeNetworkFailure | null {
50
+ return ProbeNetworkFailureUtil.classifyOutput({
51
+ output: output,
52
+ operation: PING,
53
+ });
54
+ }
55
+
56
+ describe("ProbeNetworkFailureUtil.classifyOutput — what the probe could not send", () => {
57
+ test("the customer's ping6 output is a certain no-source-address failure", () => {
58
+ expect(classifyPingOutput(PING6_NO_SOURCE_ADDRESS)).toEqual({
59
+ kind: ProbeNetworkFailureKind.NoSourceAddress,
60
+ isCertain: true,
61
+ detail: "ping6: connect: Cannot assign requested address",
62
+ });
63
+ });
64
+
65
+ test("traceroute's stderr for the same probe, which has no prefix and a leading newline", () => {
66
+ expect(
67
+ classifyPingOutput("\nconnect: Cannot assign requested address\n"),
68
+ ).toEqual({
69
+ kind: ProbeNetworkFailureKind.NoSourceAddress,
70
+ isCertain: true,
71
+ detail: "connect: Cannot assign requested address",
72
+ });
73
+ });
74
+
75
+ test("the macOS/BSD spelling of EADDRNOTAVAIL", () => {
76
+ expect(
77
+ classifyPingOutput("ping6: connect: Can't assign requested address")
78
+ ?.kind,
79
+ ).toBe(ProbeNetworkFailureKind.NoSourceAddress);
80
+ });
81
+
82
+ test("no IPv6 route is certain for ping: connect() there only looks at the probe's own table", () => {
83
+ expect(classifyPingOutput(PING6_NO_ROUTE)).toEqual({
84
+ kind: ProbeNetworkFailureKind.NetworkUnreachable,
85
+ isCertain: true,
86
+ detail: "ping6: connect: Network is unreachable",
87
+ });
88
+ });
89
+
90
+ test("IPv6 disabled in the kernel", () => {
91
+ expect(classifyPingOutput(PING6_NO_FAMILY)).toEqual({
92
+ kind: ProbeNetworkFailureKind.AddressFamilyNotSupported,
93
+ isCertain: true,
94
+ detail: "ping6: socket: Address family not supported by protocol",
95
+ });
96
+ });
97
+
98
+ test("the macOS wording of EAFNOSUPPORT", () => {
99
+ expect(
100
+ classifyPingOutput(
101
+ "ping6: socket: Address family not supported by protocol family",
102
+ )?.kind,
103
+ ).toBe(ProbeNetworkFailureKind.AddressFamilyNotSupported);
104
+ });
105
+
106
+ test.each(["ping: connect: No route to host", "ping: unreachable host"])(
107
+ "%s stays a probe-side no-route, but only as likely",
108
+ (output: string) => {
109
+ expect(classifyPingOutput(output)).toMatchObject({
110
+ kind: ProbeNetworkFailureKind.NoRouteToHost,
111
+ isCertain: false,
112
+ });
113
+ },
114
+ );
115
+
116
+ test("the detail is the line that says so, not ping's banner or statistics", () => {
117
+ expect(
118
+ classifyPingOutput(
119
+ [
120
+ "PING 2001:db8::1(2001:db8::1) 56 data bytes",
121
+ "ping: sendmsg: Network is unreachable",
122
+ "",
123
+ "--- 2001:db8::1 ping statistics ---",
124
+ "1 packets transmitted, 0 received, 100% packet loss, time 0ms",
125
+ ].join("\n"),
126
+ )?.detail,
127
+ ).toBe("ping: sendmsg: Network is unreachable");
128
+ });
129
+
130
+ test("the detail is capped and never ends in a period", () => {
131
+ const failure: ProbeNetworkFailure | null = classifyPingOutput(
132
+ `connect: Cannot assign requested address${" x".repeat(200)}.`,
133
+ );
134
+
135
+ expect(failure?.detail.length).toBe(200);
136
+ expect(
137
+ classifyPingOutput("connect: Network is unreachable...")?.detail,
138
+ ).toBe("connect: Network is unreachable");
139
+ });
140
+
141
+ test("matching is case-insensitive and keeps the OS's own casing in the detail", () => {
142
+ expect(
143
+ classifyPingOutput("PING6: CONNECT: CANNOT ASSIGN REQUESTED ADDRESS")
144
+ ?.detail,
145
+ ).toBe("PING6: CONNECT: CANNOT ASSIGN REQUESTED ADDRESS");
146
+ });
147
+
148
+ test("the no-source-address marker wins when an output carries two", () => {
149
+ expect(
150
+ classifyPingOutput(
151
+ "connect: Network is unreachable\nconnect: Cannot assign requested address",
152
+ )?.kind,
153
+ ).toBe(ProbeNetworkFailureKind.NoSourceAddress);
154
+ });
155
+ });
156
+
157
+ describe("ProbeNetworkFailureUtil.classifyOutput — what is NOT the probe's fault", () => {
158
+ test.each([
159
+ // A router on the path sent these back about the target.
160
+ "From 2001:db8::1 icmp_seq=1 Destination Host Unreachable",
161
+ "From 10.0.0.1 icmp_seq=1 Destination Net Unreachable",
162
+ "From fe80::1%eth0 icmp_seq=1 Destination unreachable: Address unreachable",
163
+ "From fe80::1%eth0 icmp_seq=1 Destination unreachable: No route",
164
+ // A silent host.
165
+ "5 packets transmitted, 0 packets received, 100.0% packet loss",
166
+ // Not a routing problem at all.
167
+ "ping: socket: Operation not permitted",
168
+ "ping: rs1.example.net: Name or service not known",
169
+ "",
170
+ ])("%j", (output: string) => {
171
+ expect(classifyPingOutput(output)).toBeNull();
172
+ });
173
+
174
+ test("no route to host is a router's verdict on a TCP connect, so it is not classified there", () => {
175
+ expect(
176
+ ProbeNetworkFailureUtil.classifyOutput({
177
+ output: "connect EHOSTUNREACH 2001:db8::1:443",
178
+ operation: TCP,
179
+ }),
180
+ ).toBeNull();
181
+ expect(
182
+ ProbeNetworkFailureUtil.classifyOutput({
183
+ output: "No route to host",
184
+ operation: TCP,
185
+ }),
186
+ ).toBeNull();
187
+ });
188
+
189
+ test("an undefined output is treated as empty", () => {
190
+ expect(
191
+ ProbeNetworkFailureUtil.classifyOutput({
192
+ output: undefined as unknown as string,
193
+ operation: PING,
194
+ }),
195
+ ).toBeNull();
196
+ });
197
+ });
198
+
199
+ describe("ProbeNetworkFailureUtil.classifyOutput — TCP certainty", () => {
200
+ test("network is unreachable is only LIKELY the probe on a TCP connect", () => {
201
+ /*
202
+ * A router's ICMPv6 "no route" arriving while the SYN is outstanding
203
+ * produces the same errno, so the wording has to hedge.
204
+ */
205
+ expect(
206
+ ProbeNetworkFailureUtil.classifyOutput({
207
+ output: "Network is unreachable",
208
+ operation: TCP,
209
+ }),
210
+ ).toMatchObject({
211
+ kind: ProbeNetworkFailureKind.NetworkUnreachable,
212
+ isCertain: false,
213
+ });
214
+ });
215
+
216
+ test("a Node error message is classified by its code, not its text", () => {
217
+ // classifyOutput reads strerror text; "ENETUNREACH" is for classifyErrorCode.
218
+ expect(
219
+ ProbeNetworkFailureUtil.classifyOutput({
220
+ output: `connect ENETUNREACH ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
221
+ operation: TCP,
222
+ }),
223
+ ).toBeNull();
224
+ });
225
+
226
+ test("a TCP connect's no-source-address is certain but not certainly missing IPv6", () => {
227
+ /*
228
+ * connect(2) also returns EADDRNOTAVAIL when the probe has run out of
229
+ * local ports for the destination, which a probe with IPv6 can hit.
230
+ */
231
+ expect(
232
+ ProbeNetworkFailureUtil.classifyOutput({
233
+ output: "Cannot assign requested address",
234
+ operation: TCP,
235
+ }),
236
+ ).toMatchObject({ isCertain: true, isCauseAmbiguous: true });
237
+ expect(
238
+ classifyPingOutput(PING6_NO_SOURCE_ADDRESS)?.isCauseAmbiguous,
239
+ ).toBeUndefined();
240
+ expect(
241
+ ProbeNetworkFailureUtil.classifyOutput({
242
+ output: "Address family not supported by protocol",
243
+ operation: TCP,
244
+ })?.isCauseAmbiguous,
245
+ ).toBeUndefined();
246
+ });
247
+
248
+ test("the two failures that happen before any packet stay certain on TCP", () => {
249
+ expect(
250
+ ProbeNetworkFailureUtil.classifyOutput({
251
+ output: "Cannot assign requested address",
252
+ operation: TCP,
253
+ })?.isCertain,
254
+ ).toBe(true);
255
+ expect(
256
+ ProbeNetworkFailureUtil.classifyOutput({
257
+ output: "Address family not supported by protocol",
258
+ operation: TCP,
259
+ })?.isCertain,
260
+ ).toBe(true);
261
+ });
262
+ });
263
+
264
+ describe("ProbeNetworkFailureUtil.classifyErrorCode", () => {
265
+ test.each([
266
+ [
267
+ "EADDRNOTAVAIL",
268
+ PING,
269
+ ProbeNetworkFailureKind.NoSourceAddress,
270
+ true,
271
+ undefined,
272
+ ],
273
+ ["EADDRNOTAVAIL", TCP, ProbeNetworkFailureKind.NoSourceAddress, true, true],
274
+ [
275
+ "EAFNOSUPPORT",
276
+ PING,
277
+ ProbeNetworkFailureKind.AddressFamilyNotSupported,
278
+ true,
279
+ undefined,
280
+ ],
281
+ [
282
+ "EAFNOSUPPORT",
283
+ TCP,
284
+ ProbeNetworkFailureKind.AddressFamilyNotSupported,
285
+ true,
286
+ undefined,
287
+ ],
288
+ [
289
+ "ENETUNREACH",
290
+ PING,
291
+ ProbeNetworkFailureKind.NetworkUnreachable,
292
+ true,
293
+ undefined,
294
+ ],
295
+ [
296
+ "ENETUNREACH",
297
+ TCP,
298
+ ProbeNetworkFailureKind.NetworkUnreachable,
299
+ false,
300
+ undefined,
301
+ ],
302
+ ])(
303
+ "%s during %s is %s (certain: %s, ambiguous cause: %s)",
304
+ (
305
+ code: string,
306
+ operation: ProbeNetworkOperation,
307
+ kind: ProbeNetworkFailureKind,
308
+ isCertain: boolean,
309
+ isCauseAmbiguous: boolean | undefined,
310
+ ) => {
311
+ const failure: ProbeNetworkFailure | null =
312
+ ProbeNetworkFailureUtil.classifyErrorCode({
313
+ code: code,
314
+ operation: operation,
315
+ });
316
+
317
+ expect(failure).toEqual({
318
+ kind: kind,
319
+ isCertain: isCertain,
320
+ detail: code,
321
+ isCauseAmbiguous: isCauseAmbiguous,
322
+ });
323
+ expect(failure?.isCauseAmbiguous).toBe(isCauseAmbiguous);
324
+ },
325
+ );
326
+
327
+ test.each([
328
+ "EHOSTUNREACH",
329
+ "ECONNREFUSED",
330
+ "ETIMEDOUT",
331
+ "ECONNRESET",
332
+ "ENOTFOUND",
333
+ "EAI_AGAIN",
334
+ "ENOENT",
335
+ "",
336
+ ])("%j is not a probe-side failure", (code: string) => {
337
+ expect(
338
+ ProbeNetworkFailureUtil.classifyErrorCode({ code: code, operation: TCP }),
339
+ ).toBeNull();
340
+ expect(
341
+ ProbeNetworkFailureUtil.classifyErrorCode({
342
+ code: code,
343
+ operation: PING,
344
+ }),
345
+ ).toBeNull();
346
+ });
347
+
348
+ test("a child process's numeric exit status is not an errno", () => {
349
+ expect(
350
+ ProbeNetworkFailureUtil.classifyErrorCode({ code: 1, operation: PING }),
351
+ ).toBeNull();
352
+ expect(
353
+ ProbeNetworkFailureUtil.classifyErrorCode({
354
+ code: undefined,
355
+ operation: PING,
356
+ }),
357
+ ).toBeNull();
358
+ });
359
+
360
+ test("the caller's detail and address are carried through", () => {
361
+ expect(
362
+ ProbeNetworkFailureUtil.classifyErrorCode({
363
+ code: "EADDRNOTAVAIL",
364
+ operation: TCP,
365
+ detail: `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
366
+ address: CUSTOMER_ADDRESS,
367
+ }),
368
+ ).toEqual({
369
+ kind: ProbeNetworkFailureKind.NoSourceAddress,
370
+ isCertain: true,
371
+ detail: `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
372
+ address: CUSTOMER_ADDRESS,
373
+ isCauseAmbiguous: true,
374
+ });
375
+ });
376
+
377
+ test("a lower-case code is still recognised", () => {
378
+ expect(
379
+ ProbeNetworkFailureUtil.classifyErrorCode({
380
+ code: "eaddrnotavail",
381
+ operation: TCP,
382
+ })?.kind,
383
+ ).toBe(ProbeNetworkFailureKind.NoSourceAddress);
384
+ });
385
+ });
386
+
387
+ describe("ProbeNetworkFailureUtil.classifyError", () => {
388
+ test("the customer's TCP connect error, exactly as node raised it", () => {
389
+ expect(
390
+ ProbeNetworkFailureUtil.classifyError({
391
+ error: nodeError(
392
+ "EADDRNOTAVAIL",
393
+ `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
394
+ CUSTOMER_ADDRESS,
395
+ ),
396
+ operation: TCP,
397
+ }),
398
+ ).toEqual({
399
+ kind: ProbeNetworkFailureKind.NoSourceAddress,
400
+ isCertain: true,
401
+ detail: `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
402
+ address: CUSTOMER_ADDRESS,
403
+ isCauseAmbiguous: true,
404
+ });
405
+ });
406
+
407
+ test("fetch's TypeError, which hides the socket error in cause", () => {
408
+ const error: Error & { cause?: unknown } = new TypeError("fetch failed");
409
+ error.cause = nodeError(
410
+ "EADDRNOTAVAIL",
411
+ `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:443 - Local (:::0)`,
412
+ CUSTOMER_ADDRESS,
413
+ );
414
+
415
+ expect(
416
+ ProbeNetworkFailureUtil.classifyError({ error: error, operation: TCP }),
417
+ ).toMatchObject({
418
+ kind: ProbeNetworkFailureKind.NoSourceAddress,
419
+ address: CUSTOMER_ADDRESS,
420
+ });
421
+ });
422
+
423
+ test("an aggregate where every attempt failed on the probe is probe-side, as the least certain attempt", () => {
424
+ const aggregate: Error & { errors: Array<Error>; code: string } =
425
+ Object.assign(new Error(""), {
426
+ code: "EADDRNOTAVAIL",
427
+ errors: [
428
+ nodeError("EADDRNOTAVAIL", "connect EADDRNOTAVAIL 2001:db8::1:443"),
429
+ nodeError("ENETUNREACH", "connect ENETUNREACH 192.0.2.1:443"),
430
+ ],
431
+ });
432
+
433
+ expect(
434
+ ProbeNetworkFailureUtil.classifyError({
435
+ error: aggregate,
436
+ operation: TCP,
437
+ }),
438
+ ).toMatchObject({
439
+ kind: ProbeNetworkFailureKind.NetworkUnreachable,
440
+ isCertain: false,
441
+ });
442
+ });
443
+
444
+ test("a mixed-family aggregate names no address, so it cannot be worded as IPv6", () => {
445
+ /*
446
+ * A dual-stack name whose IPv4 attempt failed as well is not an IPv6
447
+ * problem, whichever attempt Node happened to try first.
448
+ */
449
+ const aggregate: Error & { errors: Array<Error>; code: string } =
450
+ Object.assign(new Error(""), {
451
+ code: "ENETUNREACH",
452
+ errors: [
453
+ nodeError(
454
+ "ENETUNREACH",
455
+ "connect ENETUNREACH 2001:db8::1:443 - Local (:::0)",
456
+ "2001:db8::1",
457
+ ),
458
+ nodeError(
459
+ "ENETUNREACH",
460
+ "connect ENETUNREACH 192.0.2.1:443",
461
+ "192.0.2.1",
462
+ ),
463
+ ],
464
+ });
465
+
466
+ const failure: ProbeNetworkFailure | null =
467
+ ProbeNetworkFailureUtil.classifyError({
468
+ error: aggregate,
469
+ operation: TCP,
470
+ });
471
+
472
+ expect(failure).toMatchObject({
473
+ kind: ProbeNetworkFailureKind.NetworkUnreachable,
474
+ isCertain: false,
475
+ detail: "connect ENETUNREACH 2001:db8::1:443 - Local (:::0)",
476
+ });
477
+ expect(failure?.address).toBeUndefined();
478
+ expect(
479
+ ProbeNetworkFailureUtil.isIPv6Failure({
480
+ host: "dual-stack.example",
481
+ failure: failure!,
482
+ }),
483
+ ).toBe(false);
484
+ });
485
+
486
+ test("an all-IPv6 aggregate keeps its address, and is worded as IPv6", () => {
487
+ const aggregate: Error & { errors: Array<Error>; code: string } =
488
+ Object.assign(new Error(""), {
489
+ code: "EADDRNOTAVAIL",
490
+ errors: [
491
+ nodeError(
492
+ "EADDRNOTAVAIL",
493
+ "connect EADDRNOTAVAIL 2001:db8::1:443 - Local (:::0)",
494
+ "2001:db8::1",
495
+ ),
496
+ nodeError(
497
+ "ENETUNREACH",
498
+ "connect ENETUNREACH 2001:db8::2:443 - Local (:::0)",
499
+ "2001:db8::2",
500
+ ),
501
+ ],
502
+ });
503
+
504
+ const failure: ProbeNetworkFailure | null =
505
+ ProbeNetworkFailureUtil.classifyError({
506
+ error: aggregate,
507
+ operation: TCP,
508
+ });
509
+
510
+ expect(failure).toMatchObject({
511
+ kind: ProbeNetworkFailureKind.NetworkUnreachable,
512
+ isCertain: false,
513
+ address: "2001:db8::2",
514
+ });
515
+ expect(
516
+ ProbeNetworkFailureUtil.isIPv6Failure({
517
+ host: "v6-only.example",
518
+ failure: failure!,
519
+ }),
520
+ ).toBe(true);
521
+ });
522
+
523
+ test("an aggregate with an attempt that named no address keeps none", () => {
524
+ const aggregate: Error & { errors: Array<Error>; code: string } =
525
+ Object.assign(new Error(""), {
526
+ code: "EADDRNOTAVAIL",
527
+ errors: [
528
+ nodeError(
529
+ "EADDRNOTAVAIL",
530
+ "connect EADDRNOTAVAIL 2001:db8::1:443 - Local (:::0)",
531
+ "2001:db8::1",
532
+ ),
533
+ nodeError("EADDRNOTAVAIL", "connect EADDRNOTAVAIL"),
534
+ ],
535
+ });
536
+
537
+ expect(
538
+ ProbeNetworkFailureUtil.classifyError({
539
+ error: aggregate,
540
+ operation: TCP,
541
+ })?.address,
542
+ ).toBeUndefined();
543
+ });
544
+
545
+ test("an aggregate where one attempt reached the network is NOT probe-side, whatever code node copied onto it", () => {
546
+ /*
547
+ * Happy eyeballs: the IPv6 attempt could not leave the probe, the IPv4
548
+ * one timed out. That is an IPv4 outage, and Node sets the aggregate's
549
+ * code to the FIRST attempt's, EADDRNOTAVAIL.
550
+ */
551
+ const aggregate: Error & { errors: Array<Error>; code: string } =
552
+ Object.assign(new Error(""), {
553
+ code: "EADDRNOTAVAIL",
554
+ errors: [
555
+ nodeError("EADDRNOTAVAIL", "connect EADDRNOTAVAIL 2001:db8::1:443"),
556
+ nodeError("ETIMEDOUT", "connect ETIMEDOUT 192.0.2.1:443"),
557
+ ],
558
+ });
559
+
560
+ expect(
561
+ ProbeNetworkFailureUtil.classifyError({
562
+ error: aggregate,
563
+ operation: TCP,
564
+ }),
565
+ ).toBeNull();
566
+ });
567
+
568
+ test("a wrapper that copied its cause's code defers to the cause", () => {
569
+ // The shape an AxiosError has around a happy-eyeballs AggregateError.
570
+ const wrapper: Error & { code: string; cause: unknown } = Object.assign(
571
+ new Error(""),
572
+ {
573
+ code: "EADDRNOTAVAIL",
574
+ cause: Object.assign(new Error(""), {
575
+ code: "EADDRNOTAVAIL",
576
+ errors: [
577
+ nodeError("EADDRNOTAVAIL", "connect EADDRNOTAVAIL 2001:db8::1:443"),
578
+ nodeError("ECONNREFUSED", "connect ECONNREFUSED 192.0.2.1:443"),
579
+ ],
580
+ }),
581
+ },
582
+ );
583
+
584
+ expect(
585
+ ProbeNetworkFailureUtil.classifyError({ error: wrapper, operation: TCP }),
586
+ ).toBeNull();
587
+ });
588
+
589
+ test("an error with no code falls back to its message", () => {
590
+ expect(
591
+ ProbeNetworkFailureUtil.classifyError({
592
+ error: new Error("connect: Network is unreachable"),
593
+ operation: PING,
594
+ })?.kind,
595
+ ).toBe(ProbeNetworkFailureKind.NetworkUnreachable);
596
+ });
597
+
598
+ test.each([
599
+ undefined,
600
+ null,
601
+ "EADDRNOTAVAIL",
602
+ 42,
603
+ new Error("connect ECONNREFUSED 192.0.2.1:443"),
604
+ nodeError("ECONNREFUSED", "connect ECONNREFUSED 192.0.2.1:443"),
605
+ nodeError("EHOSTUNREACH", "connect EHOSTUNREACH 2001:db8::1:443"),
606
+ ])("%p is not probe-side", (error: unknown) => {
607
+ expect(
608
+ ProbeNetworkFailureUtil.classifyError({ error: error, operation: TCP }),
609
+ ).toBeNull();
610
+ });
611
+
612
+ test("a cause chain that loops back on itself terminates", () => {
613
+ const error: Error & { cause?: unknown } = new Error("outer");
614
+ error.cause = error;
615
+
616
+ expect(
617
+ ProbeNetworkFailureUtil.classifyError({ error: error, operation: TCP }),
618
+ ).toBeNull();
619
+ });
620
+ });
621
+
622
+ describe("ProbeNetworkFailureUtil.describe — an IPv6 destination", () => {
623
+ test("the customer's case says the host was never contacted and is not being judged", () => {
624
+ const failure: ProbeNetworkFailure = classifyPingOutput(
625
+ PING6_NO_SOURCE_ADDRESS,
626
+ )!;
627
+
628
+ expect(
629
+ ProbeNetworkFailureUtil.describe({
630
+ host: CUSTOMER_ADDRESS,
631
+ failure: failure,
632
+ }),
633
+ ).toBe(
634
+ `This probe cannot send IPv6 traffic (ping6: connect: Cannot assign requested address), so ${CUSTOMER_ADDRESS} was never contacted. The probe has no usable IPv6 address or route; this says nothing about whether ${CUSTOMER_ADDRESS} is up. Monitor IPv6 destinations from a probe that has IPv6 connectivity.`,
635
+ );
636
+ });
637
+
638
+ test.each([PING6_NO_ROUTE, PING6_NO_FAMILY])(
639
+ "%j gets the same certain wording",
640
+ (output: string) => {
641
+ const text: string = ProbeNetworkFailureUtil.describe({
642
+ host: CUSTOMER_ADDRESS,
643
+ failure: classifyPingOutput(output)!,
644
+ });
645
+
646
+ expect(text).toContain("This probe cannot send IPv6 traffic");
647
+ expect(text).toContain(`${CUSTOMER_ADDRESS} was never contacted`);
648
+ expect(text).toContain(output.trim());
649
+ },
650
+ );
651
+
652
+ test("a TCP ENETUNREACH hedges, because a router could have said it", () => {
653
+ const failure: ProbeNetworkFailure =
654
+ ProbeNetworkFailureUtil.classifyErrorCode({
655
+ code: "ENETUNREACH",
656
+ operation: TCP,
657
+ detail: `connect ENETUNREACH ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
658
+ })!;
659
+
660
+ expect(
661
+ ProbeNetworkFailureUtil.describe({
662
+ host: CUSTOMER_ADDRESS,
663
+ failure: failure,
664
+ }),
665
+ ).toBe(
666
+ `This probe could not reach ${CUSTOMER_ADDRESS} over IPv6 (connect ENETUNREACH ${CUSTOMER_ADDRESS}:179 - Local (:::0)). Most likely this probe has no IPv6 route rather than ${CUSTOMER_ADDRESS} being down. Monitor IPv6 destinations from a probe that has IPv6 connectivity.`,
667
+ );
668
+ });
669
+
670
+ test("none of the IPv6 wordings reads as a verdict on the host", () => {
671
+ for (const output of [
672
+ PING6_NO_SOURCE_ADDRESS,
673
+ PING6_NO_ROUTE,
674
+ PING6_NO_FAMILY,
675
+ "ping6: connect: No route to host",
676
+ ]) {
677
+ const text: string = ProbeNetworkFailureUtil.describe({
678
+ host: CUSTOMER_ADDRESS,
679
+ failure: classifyPingOutput(output)!,
680
+ });
681
+
682
+ expect(text).not.toContain("Unable to reach host");
683
+ expect(text).not.toContain("No ICMP echo reply");
684
+ expect(text).toContain("IPv6");
685
+ }
686
+ });
687
+
688
+ test("a bracketed IPv6 host is still recognised as IPv6", () => {
689
+ expect(
690
+ ProbeNetworkFailureUtil.describe({
691
+ host: `[${CUSTOMER_ADDRESS}]`,
692
+ failure: classifyPingOutput(PING6_NO_SOURCE_ADDRESS)!,
693
+ }),
694
+ ).toContain("cannot send IPv6 traffic");
695
+ });
696
+
697
+ test("a DNS name whose error named an IPv6 address is worded as IPv6", () => {
698
+ const failure: ProbeNetworkFailure = ProbeNetworkFailureUtil.classifyError({
699
+ error: nodeError(
700
+ "EADDRNOTAVAIL",
701
+ `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:443 - Local (:::0)`,
702
+ CUSTOMER_ADDRESS,
703
+ ),
704
+ operation: TCP,
705
+ })!;
706
+
707
+ const text: string = ProbeNetworkFailureUtil.describe({
708
+ host: "rs1.example.net",
709
+ failure: failure,
710
+ });
711
+
712
+ expect(text).toContain(
713
+ "This probe could not open an IPv6 connection from its own side",
714
+ );
715
+ expect(text).toContain("rs1.example.net was never contacted");
716
+ });
717
+
718
+ test("the caller's own knowledge of the family wins", () => {
719
+ const failure: ProbeNetworkFailure = classifyPingOutput(
720
+ PING6_NO_SOURCE_ADDRESS,
721
+ )!;
722
+
723
+ expect(
724
+ ProbeNetworkFailureUtil.describe({
725
+ host: "rs1.example.net",
726
+ failure: failure,
727
+ isIPv6Destination: true,
728
+ }),
729
+ ).toContain("cannot send IPv6 traffic");
730
+ expect(
731
+ ProbeNetworkFailureUtil.describe({
732
+ host: CUSTOMER_ADDRESS,
733
+ failure: failure,
734
+ isIPv6Destination: false,
735
+ }),
736
+ ).not.toContain("IPv6");
737
+ });
738
+ });
739
+
740
+ describe("ProbeNetworkFailureUtil.describe — IPv4 and DNS names make no IPv6 claim", () => {
741
+ test("no route keeps the wording PingMonitor has always used", () => {
742
+ expect(
743
+ ProbeNetworkFailureUtil.describe({
744
+ host: "192.0.2.1",
745
+ failure: classifyPingOutput("ping: connect: Network is unreachable")!,
746
+ }),
747
+ ).toBe(
748
+ "This probe has no route to 192.0.2.1: ping: connect: Network is unreachable.",
749
+ );
750
+ });
751
+
752
+ test("no source address says the host was never contacted", () => {
753
+ const text: string = ProbeNetworkFailureUtil.describe({
754
+ host: "192.0.2.1",
755
+ failure: ProbeNetworkFailureUtil.classifyErrorCode({
756
+ code: "EADDRNOTAVAIL",
757
+ operation: TCP,
758
+ detail: "connect EADDRNOTAVAIL 192.0.2.1:443 - Local (0.0.0.0:0)",
759
+ })!,
760
+ });
761
+
762
+ expect(text).toBe(
763
+ "This probe could not send traffic to 192.0.2.1 (connect EADDRNOTAVAIL 192.0.2.1:443 - Local (0.0.0.0:0)), so 192.0.2.1 was never contacted. The failure is on the probe, not on 192.0.2.1.",
764
+ );
765
+ });
766
+
767
+ test.each([
768
+ ["192.0.2.1", "ping: connect: Network is unreachable"],
769
+ ["192.0.2.1", "ping: connect: Cannot assign requested address"],
770
+ ["192.0.2.1", "ping: connect: No route to host"],
771
+ ["rs1.example.net", "ping: connect: Network is unreachable"],
772
+ ["rs1.example.net", "ping: socket: Address family not supported"],
773
+ ])("%s with %j", (host: string, output: string) => {
774
+ const text: string = ProbeNetworkFailureUtil.describe({
775
+ host: host,
776
+ failure: classifyPingOutput(output)!,
777
+ });
778
+
779
+ expect(text).not.toContain("IPv6");
780
+ expect(text).toContain(host);
781
+ expect(text).toContain(output);
782
+ });
783
+ });
784
+
785
+ describe("ProbeNetworkFailureUtil.describe — only as sure as the evidence", () => {
786
+ test("an IPv4 'No route to host' leaves room for the host being down", () => {
787
+ /*
788
+ * macOS prints this for a failed neighbour lookup: an on-link host that
789
+ * is down, which is not the probe's fault at all.
790
+ */
791
+ expect(
792
+ ProbeNetworkFailureUtil.describe({
793
+ host: "192.168.1.250",
794
+ failure: classifyPingOutput("ping: sendto: No route to host")!,
795
+ }),
796
+ ).toBe(
797
+ "This probe may have no route to 192.168.1.250 (ping: sendto: No route to host); 192.168.1.250 itself may also be down.",
798
+ );
799
+ });
800
+
801
+ test("a TCP EADDRNOTAVAIL to IPv6 does not state the missing IPv6 as fact", () => {
802
+ /*
803
+ * Reproduced with ip_local_port_range pinned to one port: the second
804
+ * connect to [::1] fails exactly like the customer's, on a probe whose
805
+ * IPv6 works.
806
+ */
807
+ const failure: ProbeNetworkFailure = ProbeNetworkFailureUtil.classifyError({
808
+ error: nodeError(
809
+ "EADDRNOTAVAIL",
810
+ "connect EADDRNOTAVAIL ::1:8080 - Local (:::0)",
811
+ "::1",
812
+ ),
813
+ operation: TCP,
814
+ })!;
815
+
816
+ const text: string = ProbeNetworkFailureUtil.describe({
817
+ host: "::1",
818
+ failure: failure,
819
+ });
820
+
821
+ expect(text).toBe(
822
+ "This probe could not open an IPv6 connection from its own side (connect EADDRNOTAVAIL ::1:8080 - Local (:::0)), so ::1 was never contacted. This usually means the probe has no usable IPv6 address or route (less often, that it has run out of local ports); this says nothing about whether ::1 is up. Monitor IPv6 destinations from a probe that has IPv6 connectivity.",
823
+ );
824
+ expect(text).not.toContain("cannot send IPv6 traffic");
825
+ });
826
+
827
+ test("a TCP EAFNOSUPPORT keeps the definite wording: only a disabled family gives it", () => {
828
+ expect(
829
+ ProbeNetworkFailureUtil.describeError({
830
+ host: CUSTOMER_ADDRESS,
831
+ error: nodeError(
832
+ "EAFNOSUPPORT",
833
+ `connect EAFNOSUPPORT ${CUSTOMER_ADDRESS}:443`,
834
+ CUSTOMER_ADDRESS,
835
+ ),
836
+ operation: TCP,
837
+ }),
838
+ ).toContain(
839
+ `This probe cannot send IPv6 traffic (connect EAFNOSUPPORT ${CUSTOMER_ADDRESS}:443), so ${CUSTOMER_ADDRESS} was never contacted.`,
840
+ );
841
+ });
842
+
843
+ test("ping's and traceroute's EADDRNOTAVAIL keep the definite wording", () => {
844
+ expect(
845
+ ProbeNetworkFailureUtil.describeOutput({
846
+ host: CUSTOMER_ADDRESS,
847
+ output: PING6_NO_SOURCE_ADDRESS,
848
+ operation: PING,
849
+ }),
850
+ ).toContain("This probe cannot send IPv6 traffic");
851
+ });
852
+ });
853
+
854
+ describe("ProbeNetworkFailureUtil.isProbeSideCause", () => {
855
+ test("every describe() wording is recognised", () => {
856
+ const failures: Array<[string, ProbeNetworkFailure]> = [
857
+ [CUSTOMER_ADDRESS, classifyPingOutput(PING6_NO_SOURCE_ADDRESS)!],
858
+ [
859
+ CUSTOMER_ADDRESS,
860
+ classifyPingOutput("ping6: connect: No route to host")!,
861
+ ],
862
+ [
863
+ CUSTOMER_ADDRESS,
864
+ ProbeNetworkFailureUtil.classifyErrorCode({
865
+ code: "EADDRNOTAVAIL",
866
+ operation: TCP,
867
+ })!,
868
+ ],
869
+ [
870
+ "192.0.2.1",
871
+ classifyPingOutput("ping: connect: Network is unreachable")!,
872
+ ],
873
+ ["192.0.2.1", classifyPingOutput("ping: connect: No route to host")!],
874
+ [
875
+ "192.0.2.1",
876
+ classifyPingOutput("ping: connect: Cannot assign requested address")!,
877
+ ],
878
+ ];
879
+
880
+ for (const [host, failure] of failures) {
881
+ expect(
882
+ ProbeNetworkFailureUtil.isProbeSideCause(
883
+ ProbeNetworkFailureUtil.describe({ host: host, failure: failure }),
884
+ ),
885
+ ).toBe(true);
886
+ }
887
+ });
888
+
889
+ test.each([
890
+ "Request timed out",
891
+ "send EADDRNOTAVAIL 2001:518:2800:9::2:161",
892
+ "Unable to reach host 192.0.2.1. No ICMP echo reply from 192.0.2.1 (5 sent)",
893
+ "",
894
+ undefined,
895
+ null,
896
+ ])("%p is not", (text: string | undefined | null) => {
897
+ expect(ProbeNetworkFailureUtil.isProbeSideCause(text)).toBe(false);
898
+ });
899
+ });
900
+
901
+ describe("ProbeNetworkFailureUtil.describeClause", () => {
902
+ test.each([
903
+ [
904
+ CUSTOMER_ADDRESS,
905
+ "\nconnect: Cannot assign requested address\n",
906
+ "this probe cannot send IPv6 traffic (connect: Cannot assign requested address)",
907
+ ],
908
+ [
909
+ CUSTOMER_ADDRESS,
910
+ "connect: No route to host",
911
+ "this probe most likely has no IPv6 route (connect: No route to host)",
912
+ ],
913
+ [
914
+ "192.0.2.1",
915
+ "connect: Network is unreachable",
916
+ "this probe has no route to 192.0.2.1 (connect: Network is unreachable)",
917
+ ],
918
+ [
919
+ "192.0.2.1",
920
+ "connect: Cannot assign requested address",
921
+ "this probe could not send traffic to 192.0.2.1 (connect: Cannot assign requested address)",
922
+ ],
923
+ [
924
+ "192.0.2.1",
925
+ "connect: No route to host",
926
+ "this probe may have no route to 192.0.2.1 (connect: No route to host); 192.0.2.1 itself may also be down",
927
+ ],
928
+ ])("%s / %j", (host: string, output: string, expected: string) => {
929
+ expect(
930
+ ProbeNetworkFailureUtil.describeClause({
931
+ host: host,
932
+ failure: classifyPingOutput(output)!,
933
+ }),
934
+ ).toBe(expected);
935
+ });
936
+ });
937
+
938
+ describe("ProbeNetworkFailureUtil.describeOutput / describeError", () => {
939
+ test("describeOutput is classifyOutput then describe", () => {
940
+ expect(
941
+ ProbeNetworkFailureUtil.describeOutput({
942
+ host: CUSTOMER_ADDRESS,
943
+ output: PING6_NO_SOURCE_ADDRESS,
944
+ operation: PING,
945
+ }),
946
+ ).toBe(
947
+ ProbeNetworkFailureUtil.describe({
948
+ host: CUSTOMER_ADDRESS,
949
+ failure: classifyPingOutput(PING6_NO_SOURCE_ADDRESS)!,
950
+ }),
951
+ );
952
+ });
953
+
954
+ test("describeOutput is null when the probe was not at fault", () => {
955
+ expect(
956
+ ProbeNetworkFailureUtil.describeOutput({
957
+ host: CUSTOMER_ADDRESS,
958
+ output: "5 packets transmitted, 0 received, 100% packet loss",
959
+ operation: PING,
960
+ }),
961
+ ).toBeNull();
962
+ });
963
+
964
+ test("describeError words a TCP failure against an IPv6 host", () => {
965
+ expect(
966
+ ProbeNetworkFailureUtil.describeError({
967
+ host: CUSTOMER_ADDRESS,
968
+ error: nodeError(
969
+ "EADDRNOTAVAIL",
970
+ `connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:179 - Local (:::0)`,
971
+ CUSTOMER_ADDRESS,
972
+ ),
973
+ operation: TCP,
974
+ }),
975
+ ).toBe(
976
+ `This probe could not open an IPv6 connection from its own side (connect EADDRNOTAVAIL ${CUSTOMER_ADDRESS}:179 - Local (:::0)), so ${CUSTOMER_ADDRESS} was never contacted. This usually means the probe has no usable IPv6 address or route (less often, that it has run out of local ports); this says nothing about whether ${CUSTOMER_ADDRESS} is up. Monitor IPv6 destinations from a probe that has IPv6 connectivity.`,
977
+ );
978
+ });
979
+
980
+ test("describeError is null for a refused connection, which proves the host is up", () => {
981
+ expect(
982
+ ProbeNetworkFailureUtil.describeError({
983
+ host: CUSTOMER_ADDRESS,
984
+ error: nodeError(
985
+ "ECONNREFUSED",
986
+ `connect ECONNREFUSED ${CUSTOMER_ADDRESS}:179`,
987
+ ),
988
+ operation: TCP,
989
+ }),
990
+ ).toBeNull();
991
+ });
992
+ });