@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,430 @@
1
+ import {
2
+ AddInventoryItemProjectEntityKeyIndex1795200000000,
3
+ INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX,
4
+ INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX_RUNBOOK,
5
+ } from "../../../../Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex";
6
+ import logger from "../../../../Server/Utils/Logger";
7
+ import ObjectID from "../../../../Types/ObjectID";
8
+ import { DataSource, QueryRunner } from "typeorm";
9
+
10
+ /*
11
+ * Migration 1795200000000 EXECUTED on Postgres, against a clone of the
12
+ * migrated InventoryItem table: the branch that builds the index, the branch
13
+ * that leaves a large table to the operator's CONCURRENTLY runbook (and that
14
+ * runbook then working), the planner-statistics and bounded-count estimates,
15
+ * an INVALID leftover of a failed online build, and the statement_timeout the
16
+ * build runs under — the connection's own, even after something on the
17
+ * session raised it.
18
+ *
19
+ * The inline-build threshold is lowered through the migration's constructor so
20
+ * a few dozen rows stand in for a million.
21
+ *
22
+ * Opt in with RUN_POSTGRES_TOPOLOGY_TESTS=true (the Topology Postgres suites'
23
+ * flag) against a Postgres migrated to the current head. The table's
24
+ * STRUCTURE is cloned into a unique schema that is the connection's whole
25
+ * search_path, and dropped afterwards; no real row is read or written.
26
+ * Credentials from DATABASE_USERNAME / DATABASE_PASSWORD, database from
27
+ * TOPOLOGY_TEST_DATABASE_NAME or DATABASE_NAME, endpoint from
28
+ * TOPOLOGY_TEST_DATABASE_HOST / _PORT (default localhost:5400).
29
+ */
30
+ const describePostgres: typeof describe =
31
+ process.env["RUN_POSTGRES_TOPOLOGY_TESTS"] === "true"
32
+ ? describe
33
+ : describe.skip;
34
+
35
+ /* The connection's statement_timeout, distinct from any server default. */
36
+ const CONNECTION_STATEMENT_TIMEOUT_MS: number = 43_210;
37
+
38
+ /* DDL on an index, as the recorded statements spell it. */
39
+ const INDEX_DDL: RegExp = /^(CREATE|DROP) INDEX/;
40
+
41
+ /* Any index on exactly these columns, whatever the clone named it. */
42
+ const ENTITY_KEY_INDEX_DEFINITION: RegExp = /\("projectId", "entityKey"\)$/;
43
+
44
+ interface IndexState {
45
+ name: string;
46
+ isValid: boolean;
47
+ isUnique: boolean;
48
+ definition: string;
49
+ }
50
+
51
+ describePostgres(
52
+ "AddInventoryItemProjectEntityKeyIndex1795200000000 against Postgres",
53
+ () => {
54
+ const schema: string = `topology_migration_${ObjectID.generate()
55
+ .toString()
56
+ .replace(/-/g, "")}`;
57
+ const projectId: string = ObjectID.generate().toString();
58
+ let database: DataSource;
59
+
60
+ beforeAll(async () => {
61
+ database = new DataSource({
62
+ type: "postgres",
63
+ host: process.env["TOPOLOGY_TEST_DATABASE_HOST"] || "localhost",
64
+ port: Number(process.env["TOPOLOGY_TEST_DATABASE_PORT"] || "5400"),
65
+ username: process.env["DATABASE_USERNAME"] || "postgres",
66
+ password: process.env["DATABASE_PASSWORD"] || "password",
67
+ database:
68
+ process.env["TOPOLOGY_TEST_DATABASE_NAME"] ||
69
+ process.env["DATABASE_NAME"] ||
70
+ "oneuptimedb",
71
+ entities: [],
72
+ schema,
73
+ synchronize: false,
74
+ extra: {
75
+ options: `-c search_path=${schema}`,
76
+ statement_timeout: CONNECTION_STATEMENT_TIMEOUT_MS,
77
+ },
78
+ });
79
+ await database.initialize();
80
+ await database.query(`CREATE SCHEMA "${schema}"`);
81
+ expect(
82
+ (await database.query("SELECT current_schema()"))[0].current_schema,
83
+ ).toBe(schema);
84
+ });
85
+
86
+ afterAll(async () => {
87
+ if (database?.isInitialized) {
88
+ await database.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
89
+ await database.destroy();
90
+ }
91
+ });
92
+
93
+ /*
94
+ * A fresh clone per test: new tables have never been analyzed, so each
95
+ * test starts from the statistics a just-created table has.
96
+ */
97
+ beforeEach(async () => {
98
+ await database.query(`DROP TABLE IF EXISTS "${schema}"."InventoryItem"`);
99
+ await database.query(
100
+ `CREATE TABLE "${schema}"."InventoryItem" (LIKE public."InventoryItem" INCLUDING ALL)`,
101
+ );
102
+ for (const index of await indexes()) {
103
+ await database.query(`DROP INDEX "${schema}"."${index.name}"`);
104
+ }
105
+ });
106
+
107
+ afterEach(() => {
108
+ jest.restoreAllMocks();
109
+ });
110
+
111
+ /* The indexes on ("projectId", "entityKey") in the test schema. */
112
+ async function indexes(): Promise<Array<IndexState>> {
113
+ const rows: Array<{
114
+ name: string;
115
+ isValid: boolean;
116
+ isUnique: boolean;
117
+ definition: string;
118
+ }> = await database.query(
119
+ `SELECT c.relname AS "name", x.indisvalid AS "isValid",
120
+ x.indisunique AS "isUnique",
121
+ pg_get_indexdef(x.indexrelid) AS "definition"
122
+ FROM pg_index x
123
+ JOIN pg_class c ON c.oid = x.indexrelid
124
+ JOIN pg_class t ON t.oid = x.indrelid
125
+ JOIN pg_namespace n ON n.oid = t.relnamespace
126
+ WHERE n.nspname = $1 AND t.relname = 'InventoryItem'`,
127
+ [schema],
128
+ );
129
+ return rows.filter((index: IndexState): boolean => {
130
+ return ENTITY_KEY_INDEX_DEFINITION.test(index.definition);
131
+ });
132
+ }
133
+
134
+ /* `count` items with distinct keys, or all sharing one key. */
135
+ async function seed(count: number, sharedKey?: string): Promise<void> {
136
+ await database.query(
137
+ `INSERT INTO "${schema}"."InventoryItem"
138
+ ("_id", "createdAt", "updatedAt", "version", "projectId", "entityType",
139
+ "entityKey", "displayName", "source", "lastSeenAt", "isArchived")
140
+ SELECT gen_random_uuid(), now(), now(), 1, $1, 'type-' || g,
141
+ COALESCE($2, 'key-' || g), 'item ' || g, 'discovered', now(), false
142
+ FROM generate_series(1, $3) AS g`,
143
+ [projectId, sharedKey ?? null, count],
144
+ );
145
+ }
146
+
147
+ interface Run {
148
+ statements: Array<string>;
149
+ /* current_setting('statement_timeout') right after up() returned. */
150
+ statementTimeout: string;
151
+ }
152
+
153
+ /*
154
+ * Runs up() the way TypeORM runs a migration: inside its own transaction
155
+ * on one connection. `before` runs on that connection first.
156
+ */
157
+ async function runUp(
158
+ maxRows: number,
159
+ before?: (queryRunner: QueryRunner) => Promise<void>,
160
+ ): Promise<Run> {
161
+ const queryRunner: QueryRunner = database.createQueryRunner();
162
+ await queryRunner.connect();
163
+ const statements: Array<string> = [];
164
+ const query: QueryRunner["query"] = queryRunner.query.bind(queryRunner);
165
+ queryRunner.query = (async (
166
+ sql: string,
167
+ params?: Array<unknown>,
168
+ ): Promise<unknown> => {
169
+ statements.push(sql.replace(/\s+/g, " ").trim());
170
+ return await query(sql, params);
171
+ }) as QueryRunner["query"];
172
+ try {
173
+ if (before) {
174
+ await before(queryRunner);
175
+ }
176
+ await queryRunner.startTransaction();
177
+ try {
178
+ await new AddInventoryItemProjectEntityKeyIndex1795200000000(
179
+ maxRows,
180
+ ).up(queryRunner);
181
+ const setting: Array<{ value: string }> = await query(
182
+ `SELECT current_setting('statement_timeout') AS "value"`,
183
+ );
184
+ await queryRunner.commitTransaction();
185
+ return { statements, statementTimeout: setting[0]!.value };
186
+ } catch (error) {
187
+ await queryRunner.rollbackTransaction();
188
+ throw error;
189
+ }
190
+ } finally {
191
+ // Nothing `before` set may follow the connection back into the pool.
192
+ await query(`RESET statement_timeout`);
193
+ await queryRunner.release();
194
+ }
195
+ }
196
+
197
+ function silenceWarnings(): { mock: { calls: Array<Array<unknown>> } } {
198
+ return jest.spyOn(logger, "warn").mockImplementation((): void => {
199
+ return undefined;
200
+ }) as unknown as { mock: { calls: Array<Array<unknown>> } };
201
+ }
202
+
203
+ function created(run: Run): boolean {
204
+ return run.statements.some((statement: string): boolean => {
205
+ return statement.startsWith("CREATE INDEX");
206
+ });
207
+ }
208
+
209
+ function counted(run: Run): boolean {
210
+ return run.statements.some((statement: string): boolean => {
211
+ return statement.includes("COUNT(*)");
212
+ });
213
+ }
214
+
215
+ test("a table under the threshold gets the plain, valid, non-unique index", async () => {
216
+ await seed(5);
217
+ const run: Run = await runUp(10);
218
+ expect(created(run)).toBe(true);
219
+ expect(await indexes()).toEqual([
220
+ {
221
+ name: INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX,
222
+ isValid: true,
223
+ isUnique: false,
224
+ definition: expect.stringMatching(
225
+ /^CREATE INDEX "IDX_INVENTORY_ITEM_PROJECT_ENTITY_KEY" ON .*"InventoryItem" USING btree \("projectId", "entityKey"\)$/,
226
+ ),
227
+ },
228
+ ]);
229
+ });
230
+
231
+ test("the build runs under the connection's statement_timeout, even after the session raised it", async () => {
232
+ await seed(3);
233
+ const run: Run = await runUp(
234
+ 10,
235
+ async (queryRunner: QueryRunner): Promise<void> => {
236
+ await queryRunner.query(`SET statement_timeout = '600s'`);
237
+ },
238
+ );
239
+ expect(created(run)).toBe(true);
240
+ expect(run.statementTimeout).toBe(`${CONNECTION_STATEMENT_TIMEOUT_MS}ms`);
241
+ });
242
+
243
+ test("a table past the threshold is left to the runbook, which then works, and a rerun is a no-op", async () => {
244
+ const warn: { mock: { calls: Array<Array<unknown>> } } =
245
+ silenceWarnings();
246
+ await seed(50);
247
+
248
+ const skipped: Run = await runUp(10);
249
+ expect(created(skipped)).toBe(false);
250
+ expect(await indexes()).toEqual([]);
251
+ expect(warn.mock.calls).toHaveLength(1);
252
+ expect(String(warn.mock.calls[0]![0])).toContain(
253
+ INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX_RUNBOOK,
254
+ );
255
+
256
+ // The operator's online build, outside any transaction.
257
+ await database.query(INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX_RUNBOOK);
258
+ expect(await indexes()).toEqual([
259
+ expect.objectContaining({
260
+ name: INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX,
261
+ isValid: true,
262
+ isUnique: false,
263
+ }),
264
+ ]);
265
+
266
+ const rerun: Run = await runUp(10);
267
+ expect(
268
+ rerun.statements.filter((statement: string): boolean => {
269
+ return INDEX_DDL.test(statement);
270
+ }),
271
+ ).toEqual([]);
272
+ expect(counted(rerun)).toBe(false);
273
+ expect(warn.mock.calls).toHaveLength(1);
274
+ });
275
+
276
+ test("an analyzed table is judged by the planner's statistics, without a count", async () => {
277
+ silenceWarnings();
278
+ await seed(50);
279
+ await database.query(`ANALYZE "${schema}"."InventoryItem"`);
280
+
281
+ const skipped: Run = await runUp(10);
282
+ expect(counted(skipped)).toBe(false);
283
+ expect(created(skipped)).toBe(false);
284
+
285
+ const built: Run = await runUp(100);
286
+ expect(counted(built)).toBe(false);
287
+ expect(created(built)).toBe(true);
288
+ expect(await indexes()).toHaveLength(1);
289
+ });
290
+
291
+ test("a never-analyzed table is counted, only up to just past the threshold", async () => {
292
+ silenceWarnings();
293
+ await seed(50);
294
+ const run: Run = await runUp(10);
295
+ /*
296
+ * Autovacuum may analyze the fresh table first; either way the table
297
+ * is judged too large.
298
+ */
299
+ expect(created(run)).toBe(false);
300
+ expect(await indexes()).toEqual([]);
301
+ });
302
+
303
+ test("an INVALID leftover of a failed online build is dropped and rebuilt", async () => {
304
+ // Two rows sharing (projectId, entityKey) make a UNIQUE online build fail.
305
+ await seed(2, "shared-key");
306
+ await expect(
307
+ database.query(
308
+ `CREATE UNIQUE INDEX CONCURRENTLY "${INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX}" ON "InventoryItem" ("projectId", "entityKey")`,
309
+ ),
310
+ ).rejects.toThrow();
311
+ expect(await indexes()).toEqual([
312
+ expect.objectContaining({
313
+ name: INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX,
314
+ isValid: false,
315
+ isUnique: true,
316
+ }),
317
+ ]);
318
+
319
+ const run: Run = await runUp(10);
320
+ expect(run.statements).toContain(
321
+ `DROP INDEX IF EXISTS "${INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX}"`,
322
+ );
323
+ expect(await indexes()).toEqual([
324
+ expect.objectContaining({
325
+ name: INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX,
326
+ isValid: true,
327
+ isUnique: false,
328
+ }),
329
+ ]);
330
+ });
331
+
332
+ /*
333
+ * An operator's CONCURRENTLY build that is still running is INVALID
334
+ * until it finishes. Dropping it would queue for ACCESS EXCLUSIVE behind
335
+ * the build and stall every reader and writer of the table, so the
336
+ * migration must leave it alone and complete at once.
337
+ */
338
+ test("an online build still in progress is left to finish, never dropped", async () => {
339
+ await seed(3);
340
+ const writer: QueryRunner = database.createQueryRunner();
341
+ const builder: QueryRunner = database.createQueryRunner();
342
+ await writer.connect();
343
+ await builder.connect();
344
+ try {
345
+ // An open writer makes CONCURRENTLY wait with its index INVALID.
346
+ await writer.startTransaction();
347
+ await writer.query(
348
+ `INSERT INTO "InventoryItem"
349
+ ("_id", "createdAt", "updatedAt", "version", "projectId", "entityType",
350
+ "entityKey", "displayName", "source", "lastSeenAt", "isArchived")
351
+ VALUES (gen_random_uuid(), now(), now(), 1, $1, 'type-w', 'key-w',
352
+ 'writer', 'discovered', now(), false)`,
353
+ [projectId],
354
+ );
355
+ const build: Promise<unknown> = builder.query(
356
+ `CREATE INDEX CONCURRENTLY "${INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX}" ON "InventoryItem" ("projectId", "entityKey")`,
357
+ );
358
+
359
+ let inProgress: boolean = false;
360
+ for (let attempt: number = 0; attempt < 50 && !inProgress; attempt++) {
361
+ const rows: Array<unknown> = await database.query(
362
+ `SELECT 1 FROM pg_stat_progress_create_index p
363
+ JOIN pg_class c ON c.oid = p.index_relid
364
+ WHERE c.relname = $1`,
365
+ [INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX],
366
+ );
367
+ inProgress = rows.length > 0;
368
+ if (!inProgress) {
369
+ await new Promise<void>((resolve: () => void): void => {
370
+ setTimeout(resolve, 100);
371
+ });
372
+ }
373
+ }
374
+ expect(inProgress).toBe(true);
375
+ expect(await indexes()).toEqual([
376
+ expect.objectContaining({ isValid: false }),
377
+ ]);
378
+
379
+ const warn: { mock: { calls: Array<Array<unknown>> } } =
380
+ silenceWarnings();
381
+ const startedAt: number = Date.now();
382
+ const run: Run = await runUp(10);
383
+ expect(Date.now() - startedAt).toBeLessThan(3_000);
384
+ expect(run.statements).not.toContain(
385
+ `DROP INDEX IF EXISTS "${INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX}"`,
386
+ );
387
+ expect(created(run)).toBe(false);
388
+ expect(String(warn.mock.calls[0]![0])).toContain("in progress");
389
+
390
+ await writer.commitTransaction();
391
+ await build;
392
+ expect(await indexes()).toEqual([
393
+ expect.objectContaining({
394
+ name: INVENTORY_ITEM_PROJECT_ENTITY_KEY_INDEX,
395
+ isValid: true,
396
+ isUnique: false,
397
+ }),
398
+ ]);
399
+ } finally {
400
+ if (writer.isTransactionActive) {
401
+ await writer.rollbackTransaction();
402
+ }
403
+ await writer.release();
404
+ await builder.release();
405
+ }
406
+ });
407
+
408
+ test("down() drops it, inside its own transaction as TypeORM runs it", async () => {
409
+ await seed(1);
410
+ await runUp(10);
411
+ expect(await indexes()).toHaveLength(1);
412
+ const queryRunner: QueryRunner = database.createQueryRunner();
413
+ await queryRunner.connect();
414
+ try {
415
+ await queryRunner.startTransaction();
416
+ await new AddInventoryItemProjectEntityKeyIndex1795200000000().down(
417
+ queryRunner,
418
+ );
419
+ const lockTimeout: Array<{ value: string }> = await queryRunner.query(
420
+ `SELECT current_setting('lock_timeout') AS "value"`,
421
+ );
422
+ expect(lockTimeout[0]!.value).toBe("5s");
423
+ await queryRunner.commitTransaction();
424
+ } finally {
425
+ await queryRunner.release();
426
+ }
427
+ expect(await indexes()).toEqual([]);
428
+ });
429
+ },
430
+ );
@@ -0,0 +1,279 @@
1
+ import {
2
+ activeSql,
3
+ epochMsSql,
4
+ } from "../../../../Server/Utils/Topology/TopologySql";
5
+ import EntitySource from "../../../../Types/Telemetry/EntitySource";
6
+ import {
7
+ ActivityFields,
8
+ isEntityActive,
9
+ } from "../../../../../App/FeatureSet/Dashboard/src/Components/Topology/TopologyActivity";
10
+ import { DataSource, QueryRunner } from "typeorm";
11
+
12
+ /*
13
+ * "Did this resource report in the range?" is answered twice: by Postgres
14
+ * (activeSql, which picks each resource's active container, counts active
15
+ * collection items and runs-on targets) and by the browser
16
+ * (TopologyActivity.isEntityActive, which decides what the maps draw). The
17
+ * browser judges the rows the server shipped — `source` and `lastSeenAt`
18
+ * floored to epoch milliseconds (epochMsSql) — against the range start the
19
+ * server echoed. If the two ever disagree, a resource is drawn inactive
20
+ * inside the container the server chose as its ACTIVE one, or counted as
21
+ * active in a total the map then leaves out.
22
+ *
23
+ * This runs the real predicate text through a real Postgres over a truth
24
+ * table, feeds the same rows through the browser predicate exactly as the
25
+ * decoder would hand them over, and requires the answers to match — at
26
+ * microsecond precision around the range start, and whatever the session
27
+ * time zone is. No table is read or written: the rows are an unnest() of
28
+ * bound arrays.
29
+ *
30
+ * Opt in with RUN_POSTGRES_TOPOLOGY_TESTS=true (the flag the other Topology
31
+ * Postgres suite uses). Credentials from DATABASE_USERNAME /
32
+ * DATABASE_PASSWORD, database from TOPOLOGY_TEST_DATABASE_NAME or
33
+ * DATABASE_NAME, endpoint from TOPOLOGY_TEST_DATABASE_HOST / _PORT (default
34
+ * localhost:5400).
35
+ */
36
+ const describePostgres: typeof describe =
37
+ process.env["RUN_POSTGRES_TOPOLOGY_TESTS"] === "true"
38
+ ? describe
39
+ : describe.skip;
40
+
41
+ /* The server floors the range start to the minute; this one already is. */
42
+ const RANGE_START_ISO: string = "2026-09-20T10:00:00.000Z";
43
+
44
+ interface ParityCase {
45
+ label: string;
46
+ source: string | null;
47
+ /* timestamptz input text (microseconds and offsets allowed), or null. */
48
+ lastSeenAt: string | null;
49
+ active: boolean;
50
+ }
51
+
52
+ const CASES: Array<ParityCase> = [
53
+ {
54
+ label: "discovered, exactly at the range start",
55
+ source: EntitySource.Discovered,
56
+ lastSeenAt: "2026-09-20T10:00:00.000000Z",
57
+ active: true,
58
+ },
59
+ {
60
+ label: "discovered, 1 microsecond before the range start",
61
+ source: EntitySource.Discovered,
62
+ lastSeenAt: "2026-09-20T09:59:59.999999Z",
63
+ active: false,
64
+ },
65
+ {
66
+ label: "discovered, 1 microsecond after the range start",
67
+ source: EntitySource.Discovered,
68
+ lastSeenAt: "2026-09-20T10:00:00.000001Z",
69
+ active: true,
70
+ },
71
+ {
72
+ label: "discovered, half a millisecond before (floors below the start)",
73
+ source: EntitySource.Discovered,
74
+ lastSeenAt: "2026-09-20T09:59:59.999500Z",
75
+ active: false,
76
+ },
77
+ {
78
+ label: "discovered, 999 microseconds after (floors onto the start)",
79
+ source: EntitySource.Discovered,
80
+ lastSeenAt: "2026-09-20T10:00:00.000999Z",
81
+ active: true,
82
+ },
83
+ {
84
+ label: "discovered, the start written in another offset",
85
+ source: EntitySource.Discovered,
86
+ lastSeenAt: "2026-09-20T12:00:00+02:00",
87
+ active: true,
88
+ },
89
+ {
90
+ label: "discovered, 1 ms before the start in another offset",
91
+ source: EntitySource.Discovered,
92
+ lastSeenAt: "2026-09-20T05:59:59.999-04:00",
93
+ active: false,
94
+ },
95
+ {
96
+ label: "discovered, a day stale",
97
+ source: EntitySource.Discovered,
98
+ lastSeenAt: "2026-09-19T10:00:00Z",
99
+ active: false,
100
+ },
101
+ {
102
+ label: "discovered, never reported",
103
+ source: EntitySource.Discovered,
104
+ lastSeenAt: null,
105
+ active: true,
106
+ },
107
+ {
108
+ label: "no source, stale",
109
+ source: null,
110
+ lastSeenAt: "2026-09-19T10:00:00Z",
111
+ active: false,
112
+ },
113
+ {
114
+ label: "no source, never reported",
115
+ source: null,
116
+ lastSeenAt: null,
117
+ active: true,
118
+ },
119
+ {
120
+ label: "blank source, stale",
121
+ source: "",
122
+ lastSeenAt: "2026-09-19T10:00:00Z",
123
+ active: false,
124
+ },
125
+ {
126
+ label: "blank source, in range",
127
+ source: "",
128
+ lastSeenAt: "2026-09-20T10:05:00Z",
129
+ active: true,
130
+ },
131
+ {
132
+ label: "manual, a year stale",
133
+ source: EntitySource.Manual,
134
+ lastSeenAt: "2025-09-20T10:00:00Z",
135
+ active: true,
136
+ },
137
+ {
138
+ label: "inventory-mirrored, stale",
139
+ source: EntitySource.Inventory,
140
+ lastSeenAt: "2026-09-19T10:00:00Z",
141
+ active: true,
142
+ },
143
+ {
144
+ label: "manual, never reported",
145
+ source: EntitySource.Manual,
146
+ lastSeenAt: null,
147
+ active: true,
148
+ },
149
+ {
150
+ label: "'Discovered' in another case is not discovered",
151
+ source: "Discovered",
152
+ lastSeenAt: "2026-09-19T10:00:00Z",
153
+ active: true,
154
+ },
155
+ {
156
+ label: "a whitespace source is not blank",
157
+ source: " ",
158
+ lastSeenAt: "2026-09-19T10:00:00Z",
159
+ active: true,
160
+ },
161
+ ];
162
+
163
+ interface ParityRow {
164
+ ord: number;
165
+ active: boolean;
166
+ lastSeenAt: number | null;
167
+ }
168
+
169
+ /* The row as the Dashboard decoder hands it to isEntityActive. */
170
+ function decodedFields(
171
+ source: string | null,
172
+ lastSeenAt: number | null,
173
+ ): ActivityFields {
174
+ const fields: ActivityFields = {};
175
+ if (typeof source === "string") {
176
+ fields.source = source;
177
+ }
178
+ if (typeof lastSeenAt === "number") {
179
+ fields.lastSeenAt = new Date(lastSeenAt);
180
+ }
181
+ return fields;
182
+ }
183
+
184
+ async function evaluate(runner: QueryRunner): Promise<Array<ParityRow>> {
185
+ const sql: string =
186
+ `SELECT t."ord" AS "ord", ${activeSql("t", "$1")} AS "active", ` +
187
+ `${epochMsSql('t."lastSeenAt"')} AS "lastSeenAt" ` +
188
+ `FROM unnest($2::int[], $3::text[], $4::timestamptz[]) ` +
189
+ `AS t("ord", "source", "lastSeenAt") ORDER BY t."ord"`;
190
+ return (await runner.query(sql, [
191
+ RANGE_START_ISO,
192
+ CASES.map((_testCase: ParityCase, index: number): number => {
193
+ return index;
194
+ }),
195
+ CASES.map((testCase: ParityCase): string | null => {
196
+ return testCase.source;
197
+ }),
198
+ CASES.map((testCase: ParityCase): string | null => {
199
+ return testCase.lastSeenAt;
200
+ }),
201
+ ])) as Array<ParityRow>;
202
+ }
203
+
204
+ describePostgres("Topology activity: SQL and browser agree on Postgres", () => {
205
+ let database: DataSource;
206
+ let runner: QueryRunner;
207
+
208
+ beforeAll(async () => {
209
+ database = new DataSource({
210
+ type: "postgres",
211
+ host: process.env["TOPOLOGY_TEST_DATABASE_HOST"] || "localhost",
212
+ port: Number(process.env["TOPOLOGY_TEST_DATABASE_PORT"] || "5400"),
213
+ username: process.env["DATABASE_USERNAME"] || "postgres",
214
+ password: process.env["DATABASE_PASSWORD"] || "password",
215
+ database:
216
+ process.env["TOPOLOGY_TEST_DATABASE_NAME"] ||
217
+ process.env["DATABASE_NAME"] ||
218
+ "oneuptimedb",
219
+ entities: [],
220
+ synchronize: false,
221
+ });
222
+ await database.initialize();
223
+ /* One connection, so the session time zone below sticks. */
224
+ runner = database.createQueryRunner();
225
+ await runner.connect();
226
+ });
227
+
228
+ afterAll(async () => {
229
+ await runner?.release();
230
+ if (database?.isInitialized) {
231
+ await database.destroy();
232
+ }
233
+ });
234
+
235
+ test.each(["UTC", "America/Los_Angeles", "Asia/Kolkata"])(
236
+ "every row agrees, with the session time zone at %s",
237
+ async (timeZone: string) => {
238
+ await runner.query(`SELECT set_config('TimeZone', $1, false)`, [
239
+ timeZone,
240
+ ]);
241
+ const rows: Array<ParityRow> = await evaluate(runner);
242
+ const rangeStart: Date = new Date(RANGE_START_ISO);
243
+
244
+ expect(rows).toHaveLength(CASES.length);
245
+ for (const row of rows) {
246
+ const testCase: ParityCase = CASES[row.ord]!;
247
+ const browser: boolean = isEntityActive(
248
+ decodedFields(testCase.source, row.lastSeenAt),
249
+ rangeStart,
250
+ );
251
+ expect({ label: testCase.label, sql: row.active, browser }).toEqual({
252
+ label: testCase.label,
253
+ sql: testCase.active,
254
+ browser: testCase.active,
255
+ });
256
+ }
257
+ },
258
+ );
259
+
260
+ test("the epoch the browser receives is the floor of the stored instant", async () => {
261
+ await runner.query(`SELECT set_config('TimeZone', 'UTC', false)`);
262
+ const rows: Array<ParityRow> = await evaluate(runner);
263
+ const byLabel: Map<string, number | null> = new Map<string, number | null>(
264
+ rows.map((row: ParityRow): [string, number | null] => {
265
+ return [CASES[row.ord]!.label, row.lastSeenAt];
266
+ }),
267
+ );
268
+
269
+ expect(
270
+ byLabel.get(
271
+ "discovered, half a millisecond before (floors below the start)",
272
+ ),
273
+ ).toBe(Date.parse("2026-09-20T09:59:59.999Z"));
274
+ expect(
275
+ byLabel.get("discovered, 999 microseconds after (floors onto the start)"),
276
+ ).toBe(Date.parse(RANGE_START_ISO));
277
+ expect(byLabel.get("discovered, never reported")).toBeNull();
278
+ });
279
+ });