@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,488 @@
1
+ import { CustomFieldDefinition } from "./CustomFieldDefinition";
2
+ import {
3
+ CustomFieldDropdownOption,
4
+ parseCustomFieldDropdownOptions,
5
+ } from "./CustomFieldDropdownOption";
6
+ import CustomFieldType from "./CustomFieldType";
7
+ import { isCustomFieldValueEmpty } from "./CustomFieldValueMapping";
8
+
9
+ /*
10
+ * Checks the custom field values a write puts on a record against the
11
+ * project's field definitions: a Number field holds a number, a Dropdown one
12
+ * of its options, and so on. Until this existed nothing on the server looked
13
+ * at the values at all, so the API could store "banana" in a Number field and
14
+ * every screen, filter and email downstream had to cope.
15
+ *
16
+ * It is deliberately narrow, because it sits in front of writes that have
17
+ * worked for years:
18
+ *
19
+ * - ONLY KEYS THIS WRITE CHANGES are checked. The Custom Fields card sends
20
+ * the record's whole bag back on every save, so a value stored before
21
+ * this check - or one that has since stopped being valid, like a
22
+ * dropdown option an admin removed - must keep passing, or fixing one
23
+ * field would be refused because of another. A multi-select keeps the
24
+ * entries it already had for the same reason; only the new ones are
25
+ * checked.
26
+ * - KEYS WITH NO DEFINITION pass. The bag is keyed by display name, and an
27
+ * API client may hold values for a field that was deleted or renamed.
28
+ * - EMPTY VALUES pass. Clearing a field is always allowed, and nothing here
29
+ * says a field is required: incidents created by monitors, the API,
30
+ * Slack, Microsoft Teams or AI cannot fill in a form (see "Required on
31
+ * create", which the dashboard enforces).
32
+ * - FIELDS WITH NO TYPE pass: there is nothing to check them against.
33
+ *
34
+ * Callers decide which writes are checked at all. IncidentService skips root
35
+ * writes, and runs this before mapped values (copied from monitors) are
36
+ * folded into the write, so neither is ever refused here.
37
+ *
38
+ * Pure, with no database or React imports, so it can be tested directly and
39
+ * the dashboard could run the same rules.
40
+ */
41
+
42
+ export interface CustomFieldValueValidationError {
43
+ fieldName: string;
44
+ message: string;
45
+ }
46
+
47
+ /*
48
+ * Values and option lists are quoted back in the message, bounded so that a
49
+ * pasted essay or a field with two hundred options still reads as one line.
50
+ */
51
+ const MAX_QUOTED_VALUE_LENGTH: number = 80;
52
+ const MAX_LISTED_OPTIONS: number = 10;
53
+
54
+ type QuoteFunction = (value: unknown) => string;
55
+
56
+ const quote: QuoteFunction = (value: unknown): string => {
57
+ let text: string;
58
+
59
+ try {
60
+ text = typeof value === "string" ? value : JSON.stringify(value);
61
+ } catch {
62
+ text = String(value);
63
+ }
64
+
65
+ if (text === undefined) {
66
+ text = String(value);
67
+ }
68
+
69
+ if (text.length > MAX_QUOTED_VALUE_LENGTH) {
70
+ text = `${text.slice(0, MAX_QUOTED_VALUE_LENGTH)}...`;
71
+ }
72
+
73
+ return `"${text}"`;
74
+ };
75
+
76
+ type ListOptionsFunction = (options: Array<string>) => string;
77
+
78
+ const listOptions: ListOptionsFunction = (options: Array<string>): string => {
79
+ const listed: string = options
80
+ .slice(0, MAX_LISTED_OPTIONS)
81
+ .map((option: string) => {
82
+ return quote(option);
83
+ })
84
+ .join(", ");
85
+
86
+ const more: number = options.length - MAX_LISTED_OPTIONS;
87
+
88
+ return more > 0 ? `${listed} and ${more} more` : listed;
89
+ };
90
+
91
+ /*
92
+ * The same notion of "the same answer" that value mapping uses, so an
93
+ * unchanged multi-select in another order is still unchanged.
94
+ */
95
+ type CanonicalizeFunction = (value: unknown) => string;
96
+
97
+ const canonicalize: CanonicalizeFunction = (value: unknown): string => {
98
+ if (Array.isArray(value)) {
99
+ return JSON.stringify(
100
+ [...value]
101
+ .map((entry: unknown) => {
102
+ return String(entry);
103
+ })
104
+ .sort(),
105
+ );
106
+ }
107
+
108
+ if (value instanceof Date) {
109
+ // toISOString throws on an invalid date; that one is checked, not compared.
110
+ return isNaN(value.getTime())
111
+ ? "invalid-date"
112
+ : JSON.stringify(value.toISOString());
113
+ }
114
+
115
+ return JSON.stringify(value ?? null);
116
+ };
117
+
118
+ type IsPlainObjectFunction = (
119
+ value: unknown,
120
+ ) => value is Record<string, unknown>;
121
+
122
+ const isPlainObject: IsPlainObjectFunction = (
123
+ value: unknown,
124
+ ): value is Record<string, unknown> => {
125
+ return (
126
+ value !== null &&
127
+ typeof value === "object" &&
128
+ !Array.isArray(value) &&
129
+ !(value instanceof Date)
130
+ );
131
+ };
132
+
133
+ type IsScalarFunction = (value: unknown) => boolean;
134
+
135
+ // The values a dropdown option can be written as: its text, or a bare number.
136
+ const isOptionScalar: IsScalarFunction = (value: unknown): boolean => {
137
+ return (
138
+ typeof value === "string" ||
139
+ (typeof value === "number" && Number.isFinite(value)) ||
140
+ typeof value === "boolean"
141
+ );
142
+ };
143
+
144
+ const isNumberValue: IsScalarFunction = (value: unknown): boolean => {
145
+ if (typeof value === "number") {
146
+ return Number.isFinite(value);
147
+ }
148
+
149
+ /*
150
+ * The dashboard's number input submits what was typed, as a string -
151
+ * "0", "42", "3.5" - which is how most stored Number values look.
152
+ */
153
+ if (typeof value === "string") {
154
+ const trimmed: string = value.trim();
155
+
156
+ return trimmed.length > 0 && Number.isFinite(Number(trimmed));
157
+ }
158
+
159
+ return false;
160
+ };
161
+
162
+ const isBooleanValue: IsScalarFunction = (value: unknown): boolean => {
163
+ /*
164
+ * The strings are accepted because the table and filters already read them
165
+ * as Yes and No, and hand-written API requests send them.
166
+ */
167
+ return typeof value === "boolean" || value === "true" || value === "false";
168
+ };
169
+
170
+ const isDateValue: IsScalarFunction = (value: unknown): boolean => {
171
+ if (value instanceof Date) {
172
+ return !isNaN(value.getTime());
173
+ }
174
+
175
+ if (typeof value !== "string" || value.trim().length === 0) {
176
+ return false;
177
+ }
178
+
179
+ return !isNaN(new Date(value.trim()).getTime());
180
+ };
181
+
182
+ // Text of any kind: what was typed, or a number or yes/no sent over the API.
183
+ const isTextValue: IsScalarFunction = (value: unknown): boolean => {
184
+ return (
185
+ typeof value === "string" ||
186
+ (typeof value === "number" && Number.isFinite(value)) ||
187
+ typeof value === "boolean"
188
+ );
189
+ };
190
+
191
+ type ValidateOneFunction = (data: {
192
+ definition: CustomFieldDefinition;
193
+ value: unknown;
194
+ storedValue: unknown;
195
+ }) => string | null;
196
+
197
+ const validateDropdownValue: ValidateOneFunction = (data: {
198
+ definition: CustomFieldDefinition;
199
+ value: unknown;
200
+ storedValue: unknown;
201
+ }): string | null => {
202
+ const name: string = data.definition.name;
203
+
204
+ if (!isOptionScalar(data.value)) {
205
+ return `${quote(name)} holds one of its options, but was sent ${quote(
206
+ data.value,
207
+ )}.`;
208
+ }
209
+
210
+ const options: Array<string> = parseCustomFieldDropdownOptions(
211
+ data.definition.dropdownOptions,
212
+ ).map((option: CustomFieldDropdownOption) => {
213
+ return option.value;
214
+ });
215
+
216
+ /*
217
+ * A dropdown nobody has given options to cannot be checked against them;
218
+ * refusing every value would make the field unusable over the API until an
219
+ * admin finishes defining it.
220
+ */
221
+ if (options.length === 0 || options.includes(String(data.value))) {
222
+ return null;
223
+ }
224
+
225
+ return `${quote(data.value)} is not one of the options for ${quote(
226
+ name,
227
+ )}. Choose one of: ${listOptions(options)}.`;
228
+ };
229
+
230
+ const validateMultiSelectValue: ValidateOneFunction = (data: {
231
+ definition: CustomFieldDefinition;
232
+ value: unknown;
233
+ storedValue: unknown;
234
+ }): string | null => {
235
+ const name: string = data.definition.name;
236
+
237
+ /*
238
+ * A single value is accepted as a selection of one: a field that was
239
+ * switched from single to multi select still holds bare strings, and the
240
+ * table renders one that way.
241
+ */
242
+ const entries: Array<unknown> = Array.isArray(data.value)
243
+ ? data.value
244
+ : [data.value];
245
+
246
+ const invalidShape: unknown = entries.find((entry: unknown) => {
247
+ return !isOptionScalar(entry);
248
+ });
249
+
250
+ if (invalidShape !== undefined) {
251
+ return `${quote(name)} holds a list of its options, but was sent ${quote(
252
+ data.value,
253
+ )}.`;
254
+ }
255
+
256
+ const options: Array<string> = parseCustomFieldDropdownOptions(
257
+ data.definition.dropdownOptions,
258
+ ).map((option: CustomFieldDropdownOption) => {
259
+ return option.value;
260
+ });
261
+
262
+ if (options.length === 0) {
263
+ return null;
264
+ }
265
+
266
+ // Entries the record already holds stay valid, removed options included.
267
+ const alreadyHeld: Set<string> = new Set<string>(
268
+ (Array.isArray(data.storedValue)
269
+ ? data.storedValue
270
+ : data.storedValue === undefined || data.storedValue === null
271
+ ? []
272
+ : [data.storedValue]
273
+ ).map((entry: unknown) => {
274
+ return String(entry);
275
+ }),
276
+ );
277
+
278
+ const notOptions: Array<string> = entries
279
+ .map((entry: unknown) => {
280
+ return String(entry);
281
+ })
282
+ .filter((entry: string) => {
283
+ return !alreadyHeld.has(entry) && !options.includes(entry);
284
+ });
285
+
286
+ if (notOptions.length === 0) {
287
+ return null;
288
+ }
289
+
290
+ return `${notOptions
291
+ .map((entry: string) => {
292
+ return quote(entry);
293
+ })
294
+ .join(", ")} ${
295
+ notOptions.length === 1 ? "is not one of" : "are not among"
296
+ } the options for ${quote(name)}. Choose from: ${listOptions(options)}.`;
297
+ };
298
+
299
+ const validateValue: ValidateOneFunction = (data: {
300
+ definition: CustomFieldDefinition;
301
+ value: unknown;
302
+ storedValue: unknown;
303
+ }): string | null => {
304
+ const name: string = data.definition.name;
305
+
306
+ switch (data.definition.customFieldType) {
307
+ case CustomFieldType.Text:
308
+ case CustomFieldType.LongText:
309
+ case CustomFieldType.Markdown:
310
+ return isTextValue(data.value)
311
+ ? null
312
+ : `${quote(name)} holds text, but was sent ${quote(data.value)}.`;
313
+
314
+ case CustomFieldType.Number:
315
+ return isNumberValue(data.value)
316
+ ? null
317
+ : `${quote(name)} holds a number, but was sent ${quote(data.value)}.`;
318
+
319
+ case CustomFieldType.Boolean:
320
+ return isBooleanValue(data.value)
321
+ ? null
322
+ : `${quote(name)} holds true or false, but was sent ${quote(
323
+ data.value,
324
+ )}.`;
325
+
326
+ case CustomFieldType.Date:
327
+ return isDateValue(data.value)
328
+ ? null
329
+ : `${quote(name)} holds a date, but was sent ${quote(data.value)}.`;
330
+
331
+ case CustomFieldType.DateTime:
332
+ return isDateValue(data.value)
333
+ ? null
334
+ : `${quote(name)} holds a date and time, but was sent ${quote(
335
+ data.value,
336
+ )}.`;
337
+
338
+ case CustomFieldType.Dropdown:
339
+ return validateDropdownValue(data);
340
+
341
+ case CustomFieldType.MultiSelectDropdown:
342
+ return validateMultiSelectValue(data);
343
+
344
+ default:
345
+ // No type, or one this version does not know: nothing to check.
346
+ return null;
347
+ }
348
+ };
349
+
350
+ export type ValidateCustomFieldValuesFunction = (data: {
351
+ // The project's field definitions for this kind of record.
352
+ definitions: Array<CustomFieldDefinition>;
353
+ // The bag this write stores. Anything but an object is left alone.
354
+ customFields: unknown;
355
+ // The bag the record holds now. Empty (or missing) on create.
356
+ storedCustomFields?: unknown;
357
+ }) => Array<CustomFieldValueValidationError>;
358
+
359
+ /**
360
+ * Every value this write changes that does not fit its field, in the order
361
+ * the write lists them. Empty when the write is fine.
362
+ */
363
+ export const validateCustomFieldValues: ValidateCustomFieldValuesFunction =
364
+ (data: {
365
+ definitions: Array<CustomFieldDefinition>;
366
+ customFields: unknown;
367
+ storedCustomFields?: unknown;
368
+ }): Array<CustomFieldValueValidationError> => {
369
+ if (!isPlainObject(data.customFields)) {
370
+ return [];
371
+ }
372
+
373
+ const stored: Record<string, unknown> = isPlainObject(
374
+ data.storedCustomFields,
375
+ )
376
+ ? data.storedCustomFields
377
+ : {};
378
+
379
+ const definitionsByName: Map<string, CustomFieldDefinition> = new Map<
380
+ string,
381
+ CustomFieldDefinition
382
+ >();
383
+
384
+ for (const definition of data.definitions || []) {
385
+ if (definition && typeof definition.name === "string") {
386
+ definitionsByName.set(definition.name, definition);
387
+ }
388
+ }
389
+
390
+ const errors: Array<CustomFieldValueValidationError> = [];
391
+
392
+ for (const [fieldName, value] of Object.entries(data.customFields)) {
393
+ const definition: CustomFieldDefinition | undefined =
394
+ definitionsByName.get(fieldName);
395
+
396
+ if (!definition) {
397
+ continue;
398
+ }
399
+
400
+ if (isCustomFieldValueEmpty(value)) {
401
+ continue;
402
+ }
403
+
404
+ const storedValue: unknown = Object.prototype.hasOwnProperty.call(
405
+ stored,
406
+ fieldName,
407
+ )
408
+ ? stored[fieldName]
409
+ : undefined;
410
+
411
+ if (canonicalize(value) === canonicalize(storedValue)) {
412
+ continue;
413
+ }
414
+
415
+ const message: string | null = validateValue({
416
+ definition: definition,
417
+ value: value,
418
+ storedValue: storedValue,
419
+ });
420
+
421
+ if (message) {
422
+ errors.push({ fieldName: fieldName, message: message });
423
+ }
424
+ }
425
+
426
+ return errors;
427
+ };
428
+
429
+ export type FormatCustomFieldValueValidationErrorsFunction = (
430
+ errors: Array<CustomFieldValueValidationError>,
431
+ ) => string;
432
+
433
+ /** One message for an API error response. */
434
+ export const formatCustomFieldValueValidationErrors: FormatCustomFieldValueValidationErrorsFunction =
435
+ (errors: Array<CustomFieldValueValidationError>): string => {
436
+ return errors
437
+ .map((error: CustomFieldValueValidationError) => {
438
+ return error.message;
439
+ })
440
+ .join(" ");
441
+ };
442
+
443
+ export type KeepValidCustomFieldValuesFunction = (data: {
444
+ definitions: Array<CustomFieldDefinition>;
445
+ customFields: unknown;
446
+ }) => Record<string, unknown>;
447
+
448
+ /**
449
+ * The values of a bag that a create would accept, each judged on its own as
450
+ * if it were new: an option since removed from a dropdown, or a word in a
451
+ * Number field, is left out.
452
+ *
453
+ * For a bag copied onto a new record from elsewhere - an incident template's
454
+ * values, when an incident is declared from it. Those were written long
455
+ * before the incident and may no longer fit their fields; sent as they are,
456
+ * one stale value would refuse the whole declaration over a field the person
457
+ * declaring may not even be shown. Keys with no definition, and empty values,
458
+ * are kept, as validateCustomFieldValues passes them.
459
+ */
460
+ export const keepValidCustomFieldValues: KeepValidCustomFieldValuesFunction =
461
+ (data: {
462
+ definitions: Array<CustomFieldDefinition>;
463
+ customFields: unknown;
464
+ }): Record<string, unknown> => {
465
+ if (!isPlainObject(data.customFields)) {
466
+ return {};
467
+ }
468
+
469
+ const invalidFieldNames: Set<string> = new Set<string>(
470
+ validateCustomFieldValues({
471
+ definitions: data.definitions,
472
+ customFields: data.customFields,
473
+ storedCustomFields: {},
474
+ }).map((error: CustomFieldValueValidationError) => {
475
+ return error.fieldName;
476
+ }),
477
+ );
478
+
479
+ const result: Record<string, unknown> = {};
480
+
481
+ for (const [fieldName, value] of Object.entries(data.customFields)) {
482
+ if (!invalidFieldNames.has(fieldName)) {
483
+ result[fieldName] = value;
484
+ }
485
+ }
486
+
487
+ return result;
488
+ };
@@ -0,0 +1,211 @@
1
+ import SubscriberNotificationTemplateCompiler from "../StatusPage/SubscriberNotificationTemplateCompiler";
2
+
3
+ /*
4
+ * The key an incident custom field is reached by in a template:
5
+ * {{customFields.<key>}} in a status page's custom subscriber email, and in
6
+ * anything else that fills placeholders the same way.
7
+ *
8
+ * Why a key at all, rather than the field's name: values are stored under
9
+ * the display name ("Expected Resolution"), and a name can hold anything a
10
+ * person types - spaces, hyphens, accents, emoji, other scripts. The template
11
+ * compiler only fills placeholders made of ASCII word characters and dots
12
+ * (SubscriberNotificationTemplateCompiler.isPlaceholderName), so a name is
13
+ * not something a template can refer to. The key is made from the name once,
14
+ * when the field is created, and never changes: renaming a field must not
15
+ * silently break every template that already uses it.
16
+ *
17
+ * Why not Slug.getSlug: it joins words with hyphens and appends a random
18
+ * number, and a hyphen is exactly what the placeholder pattern rejects.
19
+ *
20
+ * The shape: lowercase letters and digits in runs joined by single
21
+ * underscores - "expected_resolution" - with no leading, trailing or doubled
22
+ * underscore. A key already taken in the project gets "_2", "_3", ... in the
23
+ * order fields were created.
24
+ *
25
+ * Pure, and free of database and React imports, so the service that stamps a
26
+ * new field, the migration that stamps the existing ones and the dashboard
27
+ * all produce the same key from the same name.
28
+ */
29
+
30
+ /*
31
+ * Long enough for any name a person would give a field, short enough to type
32
+ * into a template. The column itself allows 100 characters.
33
+ */
34
+ export const CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH: number = 64;
35
+
36
+ /*
37
+ * What a name with nothing usable in it becomes: "!!!", or a name written
38
+ * wholly in a script with no Latin transliteration here, such as "影响" or
39
+ * "Влияние". Numbered like any other collision, so a project with several
40
+ * gets field, field_2, field_3.
41
+ */
42
+ export const CUSTOM_FIELD_VARIABLE_KEY_FALLBACK: string = "field";
43
+
44
+ /*
45
+ * Where a key sits among the template variables: {{customFields.<key>}}.
46
+ */
47
+ export const CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX: string = "customFields.";
48
+
49
+ const VARIABLE_KEY_PATTERN: RegExp = /^[a-z0-9]+(?:_[a-z0-9]+)*$/;
50
+
51
+ /*
52
+ * Latin letters that Unicode normalisation does not take apart into a base
53
+ * letter and an accent, so stripping accents alone would lose them: "Größe"
54
+ * would become "gr_e". Everything else accented (é, ñ, å, ç, ő, ...) is
55
+ * handled by NFKD below.
56
+ */
57
+ const LETTERS_WITHOUT_DECOMPOSITION: Record<string, string> = {
58
+ ß: "ss",
59
+ ẞ: "ss",
60
+ æ: "ae",
61
+ Æ: "ae",
62
+ œ: "oe",
63
+ Œ: "oe",
64
+ ø: "o",
65
+ Ø: "o",
66
+ đ: "d",
67
+ Đ: "d",
68
+ ð: "d",
69
+ Ð: "d",
70
+ þ: "th",
71
+ Þ: "th",
72
+ ł: "l",
73
+ Ł: "l",
74
+ ı: "i",
75
+ ħ: "h",
76
+ Ħ: "h",
77
+ ŧ: "t",
78
+ Ŧ: "t",
79
+ ŋ: "ng",
80
+ Ŋ: "ng",
81
+ ĸ: "k",
82
+ };
83
+
84
+ type TransliterateFunction = (name: string) => string;
85
+
86
+ const transliterate: TransliterateFunction = (name: string): string => {
87
+ let result: string = "";
88
+
89
+ for (const character of name) {
90
+ result += LETTERS_WITHOUT_DECOMPOSITION[character] ?? character;
91
+ }
92
+
93
+ /*
94
+ * NFKD splits "é" into "e" plus a combining accent, and also folds
95
+ * compatibility forms: full-width "A" to "A", "fi" to "fi", "①" to "1".
96
+ * Dropping the combining marks then leaves the base letters.
97
+ */
98
+ return result.normalize("NFKD").replace(/\p{M}/gu, "");
99
+ };
100
+
101
+ type TrimToLengthFunction = (key: string, maxLength: number) => string;
102
+
103
+ // Cut to a length without leaving a dangling underscore at the end.
104
+ const trimToLength: TrimToLengthFunction = (
105
+ key: string,
106
+ maxLength: number,
107
+ ): string => {
108
+ return key.slice(0, Math.max(maxLength, 0)).replace(/_+$/, "");
109
+ };
110
+
111
+ export type GetCustomFieldVariableKeyBaseFunction = (name: string) => string;
112
+
113
+ /**
114
+ * The key a field of this name gets when nothing else in the project has it.
115
+ * Never empty, and always a valid key.
116
+ */
117
+ export const getCustomFieldVariableKeyBase: GetCustomFieldVariableKeyBaseFunction =
118
+ (name: string): string => {
119
+ const words: string = transliterate(typeof name === "string" ? name : "")
120
+ .toLowerCase()
121
+ .replace(/[^a-z0-9]+/g, "_")
122
+ .replace(/^_+|_+$/g, "");
123
+
124
+ const key: string = trimToLength(
125
+ words,
126
+ CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH,
127
+ );
128
+
129
+ return key || CUSTOM_FIELD_VARIABLE_KEY_FALLBACK;
130
+ };
131
+
132
+ export type IsValidCustomFieldVariableKeyFunction = (key: unknown) => boolean;
133
+
134
+ /**
135
+ * Whether a key has the generator's shape and can be used as
136
+ * {{customFields.<key>}} in a template the compiler fills.
137
+ */
138
+ export const isValidCustomFieldVariableKey: IsValidCustomFieldVariableKeyFunction =
139
+ (key: unknown): boolean => {
140
+ return (
141
+ typeof key === "string" &&
142
+ key.length > 0 &&
143
+ key.length <= CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH &&
144
+ VARIABLE_KEY_PATTERN.test(key) &&
145
+ SubscriberNotificationTemplateCompiler.isPlaceholderName(
146
+ `${CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX}${key}`,
147
+ )
148
+ );
149
+ };
150
+
151
+ export type GenerateCustomFieldVariableKeyFunction = (data: {
152
+ name: string;
153
+ /*
154
+ * Keys the other fields of the same project already hold. Compared exactly:
155
+ * every key this module produces is lowercase already.
156
+ */
157
+ existingKeys: Iterable<string | null | undefined>;
158
+ }) => string;
159
+
160
+ /**
161
+ * The key for a new field: its name's key, or the first of name_2, name_3,
162
+ * ... that no other field in the project holds.
163
+ */
164
+ export const generateCustomFieldVariableKey: GenerateCustomFieldVariableKeyFunction =
165
+ (data: {
166
+ name: string;
167
+ existingKeys: Iterable<string | null | undefined>;
168
+ }): string => {
169
+ const taken: Set<string> = new Set<string>();
170
+
171
+ for (const key of data.existingKeys) {
172
+ if (typeof key === "string" && key.length > 0) {
173
+ taken.add(key);
174
+ }
175
+ }
176
+
177
+ const base: string = getCustomFieldVariableKeyBase(data.name);
178
+
179
+ if (!taken.has(base)) {
180
+ return base;
181
+ }
182
+
183
+ /*
184
+ * One more than there are keys is always enough: at most that many of the
185
+ * candidates can be taken.
186
+ */
187
+ for (let suffix: number = 2; suffix <= taken.size + 2; suffix++) {
188
+ const ending: string = `_${suffix}`;
189
+ const candidate: string = `${trimToLength(
190
+ base,
191
+ CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH - ending.length,
192
+ )}${ending}`;
193
+
194
+ if (!taken.has(candidate)) {
195
+ return candidate;
196
+ }
197
+ }
198
+
199
+ // Unreachable (see the loop bound); kept so the function always returns.
200
+ return `${CUSTOM_FIELD_VARIABLE_KEY_FALLBACK}_${taken.size + 2}`;
201
+ };
202
+
203
+ export type GetCustomFieldTemplateVariableNameFunction = (
204
+ key: string,
205
+ ) => string;
206
+
207
+ /** "expected_resolution" -> "customFields.expected_resolution". */
208
+ export const getCustomFieldTemplateVariableName: GetCustomFieldTemplateVariableNameFunction =
209
+ (key: string): string => {
210
+ return `${CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX}${key}`;
211
+ };