@oneuptime/common 14.0.7 → 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.
- package/Models/DatabaseModels/Incident.ts +267 -1
- package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
- package/Models/DatabaseModels/StatusPage.ts +55 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
- package/Server/API/IncidentAPI.ts +162 -0
- package/Server/API/StatusPageAPI.ts +487 -171
- package/Server/API/StatusPageSubscriberAPI.ts +63 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
- package/Server/Infrastructure/Semaphore.ts +10 -0
- package/Server/Middleware/IdentityRateLimit.ts +108 -3
- package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
- package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
- package/Server/Services/DatabaseService.ts +93 -10
- package/Server/Services/IncidentCustomFieldService.ts +276 -1
- package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
- package/Server/Services/IncidentEpisodeService.ts +15 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
- package/Server/Services/IncidentPublicNoteService.ts +30 -1
- package/Server/Services/IncidentService.ts +1535 -65
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +68 -18
- package/Server/Services/SmsService.ts +10 -0
- package/Server/Services/StatusPageService.ts +65 -8
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
- package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
- package/Server/Services/StatusPageSubscriberService.ts +1161 -64
- package/Server/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
- package/Server/Utils/SSRFProtection.ts +76 -16
- package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
- package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
- package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
- package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
- package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
- package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
- package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
- package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
- package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
- package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
- package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
- package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
- package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
- package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
- package/Server/Utils/StatusPageResource.ts +51 -20
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
- package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
- package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
- package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
- package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
- package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
- package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
- package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +242 -5
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
- package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
- package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
- package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
- package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
- package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
- package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
- package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
- package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
- package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
- package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
- package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
- package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
- package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
- package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
- package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
- package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
- package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
- package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
- package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
- package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
- package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
- package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
- package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
- package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
- package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
- package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
- package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
- package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
- package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
- package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
- package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
- package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
- package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
- package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
- package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
- package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
- package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
- package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
- package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
- package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
- package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
- package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
- package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
- package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
- package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
- package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
- package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
- package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
- package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
- package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
- package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
- package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
- package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
- package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
- package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
- package/Tests/Types/SafeHtml.test.ts +102 -0
- package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
- package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
- package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
- package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
- package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
- package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
- package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
- package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
- package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
- package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
- package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
- package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
- package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
- package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
- package/Types/CustomField/CustomFieldOrder.ts +55 -0
- package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
- package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
- package/Types/CustomField/CustomFieldType.ts +14 -0
- package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
- package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
- package/Types/SafeHtml.ts +93 -0
- package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
- package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
- package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
- package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
- package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
- package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
- package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
- package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
- package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
- package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
- package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
- package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
- package/UI/Components/Forms/ModelForm.tsx +70 -8
- package/UI/Components/Forms/Validation.ts +1 -1
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
- package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
- package/UI/Components/ModelTable/ModelTable.tsx +6 -7
- package/UI/Utils/SensitiveUrlToken.ts +10 -5
- package/Utils/API.ts +247 -2
- package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
- package/Utils/Markdown/MarkdownEscape.ts +65 -0
- package/Utils/ProbeNetworkFailureUtil.ts +673 -0
- package/build/dist/Models/DatabaseModels/Incident.js +267 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +100 -1
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +417 -176
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
- package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1052 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js +2 -22
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +47 -9
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +110 -6
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +48 -6
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageResource.js +39 -7
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
- package/build/dist/Types/SafeHtml.js +84 -0
- package/build/dist/Types/SafeHtml.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +1 -1
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
- package/build/dist/Utils/API.js +177 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
- package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
- package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { isCustomFieldValueEmpty } from "./CustomFieldValueMapping";
|
|
2
|
+
/*
|
|
3
|
+
* How a stored custom field value reads as words, for everything that puts a
|
|
4
|
+
* value in front of a person outside the Custom Fields card: an incident
|
|
5
|
+
* note's {{customFields.<key>}} placeholder (Common/Utils/Incident/
|
|
6
|
+
* IncidentNoteTemplateVariables) and the status page subscriber messages
|
|
7
|
+
* (Common/Server/Utils/StatusPage/IncidentTemplateVariableBuilder).
|
|
8
|
+
*
|
|
9
|
+
* Only the reading is shared here. Where the words then go decides the rest:
|
|
10
|
+
* a note escapes them as Markdown, an email as HTML, an SMS takes them as
|
|
11
|
+
* they are. A date and time reads in the note author's time zone in a note,
|
|
12
|
+
* and in the status page's subscriber time zones in its emails, so that is
|
|
13
|
+
* left to the caller too.
|
|
14
|
+
*
|
|
15
|
+
* Pure, with no database or React imports, so the server and the dashboard
|
|
16
|
+
* share it and it is tested directly.
|
|
17
|
+
*/
|
|
18
|
+
/*
|
|
19
|
+
* A day with no time zone: "2026-09-27", or a wall-clock time on it with no
|
|
20
|
+
* offset ("2026-09-27T00:00:00"). Its day is the one it names, wherever it is
|
|
21
|
+
* read.
|
|
22
|
+
*/
|
|
23
|
+
const DAY_WITHOUT_TIME_ZONE_PATTERN = /^(\d{4}-\d{2}-\d{2})(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)?$/;
|
|
24
|
+
/*
|
|
25
|
+
* What a Date value's instant is moved by before its UTC day is read; see
|
|
26
|
+
* formatCustomFieldCalendarDate.
|
|
27
|
+
*/
|
|
28
|
+
const PICKED_DAY_SHIFT_MS = 13 * 60 * 60 * 1000;
|
|
29
|
+
/**
|
|
30
|
+
* Plain text for a value of no particular type: a list joined with commas
|
|
31
|
+
* (its empty entries left out), a Date as its ISO string, an object as JSON,
|
|
32
|
+
* anything else as String() gives it. null and undefined are "".
|
|
33
|
+
*/
|
|
34
|
+
export const customFieldValueToText = (value) => {
|
|
35
|
+
if (value === null || value === undefined) {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return value
|
|
40
|
+
.filter((entry) => {
|
|
41
|
+
return !isCustomFieldValueEmpty(entry);
|
|
42
|
+
})
|
|
43
|
+
.map((entry) => {
|
|
44
|
+
return customFieldValueToText(entry);
|
|
45
|
+
})
|
|
46
|
+
.join(", ");
|
|
47
|
+
}
|
|
48
|
+
if (value instanceof Date) {
|
|
49
|
+
return isNaN(value.getTime()) ? "" : value.toISOString();
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "object") {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.stringify(value);
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
return String(value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return String(value);
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The instant a Date or DateTime value holds, or null when it holds none
|
|
63
|
+
* (an empty string, text that is not a date).
|
|
64
|
+
*/
|
|
65
|
+
export const customFieldValueToDate = (value) => {
|
|
66
|
+
if (value instanceof Date) {
|
|
67
|
+
return isNaN(value.getTime()) ? null : value;
|
|
68
|
+
}
|
|
69
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
70
|
+
const date = new Date(value.trim());
|
|
71
|
+
return isNaN(date.getTime()) ? null : date;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Yes or No for a Boolean value, which is stored as true or false (or, from
|
|
77
|
+
* an older form, as "true" or "false"). Anything else reads as it is.
|
|
78
|
+
*/
|
|
79
|
+
export const formatCustomFieldBoolean = (value) => {
|
|
80
|
+
if (value === true || value === "true") {
|
|
81
|
+
return "Yes";
|
|
82
|
+
}
|
|
83
|
+
if (value === false || value === "false") {
|
|
84
|
+
return "No";
|
|
85
|
+
}
|
|
86
|
+
return customFieldValueToText(value);
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* A Date value - a calendar day with no time of day - as the day that was
|
|
90
|
+
* picked, "2026-09-27". A value that is no date reads as it is.
|
|
91
|
+
*
|
|
92
|
+
* The dashboard does not store the day itself. Its date input stores the
|
|
93
|
+
* picked day's midnight in the author's time zone, as a UTC instant: 27 Sep
|
|
94
|
+
* picked in Berlin (UTC+2) is "2026-09-26T22:00:00.000Z", in New York
|
|
95
|
+
* (UTC-4) "2026-09-27T04:00:00.000Z". So neither the UTC day of the instant
|
|
96
|
+
* (26 Sep for Berlin: every author east of UTC would get the day before) nor
|
|
97
|
+
* the reader's day (a reader in another zone than the author's) is the day
|
|
98
|
+
* that was picked, and who picked it, and where, is not stored.
|
|
99
|
+
*
|
|
100
|
+
* The instant is read as the nearest plausible local midnight instead: moved
|
|
101
|
+
* 13 hours on, its UTC day is the picked day for an author anywhere from
|
|
102
|
+
* UTC-10:59 to UTC+13:00 - Hawaii to New Zealand in summer - and for a value
|
|
103
|
+
* an API client stored as UTC midnight. Only an author in UTC-11 or UTC-12,
|
|
104
|
+
* or past UTC+13, gets the day after or the day before. It is the same day
|
|
105
|
+
* whoever reads it, so a note and an email about one incident agree.
|
|
106
|
+
*
|
|
107
|
+
* A bare day ("2026-09-27"), or a time with no offset, is the day it names.
|
|
108
|
+
*/
|
|
109
|
+
export const formatCustomFieldCalendarDate = (value) => {
|
|
110
|
+
const match = typeof value === "string"
|
|
111
|
+
? value.trim().match(DAY_WITHOUT_TIME_ZONE_PATTERN)
|
|
112
|
+
: null;
|
|
113
|
+
if (match) {
|
|
114
|
+
return match[1];
|
|
115
|
+
}
|
|
116
|
+
const date = customFieldValueToDate(value);
|
|
117
|
+
const pickedDay = date
|
|
118
|
+
? new Date(date.getTime() + PICKED_DAY_SHIFT_MS)
|
|
119
|
+
: null;
|
|
120
|
+
// Invalid only at the very end of the range a Date can hold.
|
|
121
|
+
if (!pickedDay || isNaN(pickedDay.getTime())) {
|
|
122
|
+
return customFieldValueToText(value);
|
|
123
|
+
}
|
|
124
|
+
return pickedDay.toISOString().slice(0, 10);
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=CustomFieldValueFormat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldValueFormat.js","sourceRoot":"","sources":["../../../../Types/CustomField/CustomFieldValueFormat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { parseCustomFieldDropdownOptions, } from "./CustomFieldDropdownOption";
|
|
2
|
+
import CustomFieldType from "./CustomFieldType";
|
|
3
|
+
import { isCustomFieldValueEmpty } from "./CustomFieldValueMapping";
|
|
4
|
+
/*
|
|
5
|
+
* Values and option lists are quoted back in the message, bounded so that a
|
|
6
|
+
* pasted essay or a field with two hundred options still reads as one line.
|
|
7
|
+
*/
|
|
8
|
+
const MAX_QUOTED_VALUE_LENGTH = 80;
|
|
9
|
+
const MAX_LISTED_OPTIONS = 10;
|
|
10
|
+
const quote = (value) => {
|
|
11
|
+
let text;
|
|
12
|
+
try {
|
|
13
|
+
text = typeof value === "string" ? value : JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
text = String(value);
|
|
17
|
+
}
|
|
18
|
+
if (text === undefined) {
|
|
19
|
+
text = String(value);
|
|
20
|
+
}
|
|
21
|
+
if (text.length > MAX_QUOTED_VALUE_LENGTH) {
|
|
22
|
+
text = `${text.slice(0, MAX_QUOTED_VALUE_LENGTH)}...`;
|
|
23
|
+
}
|
|
24
|
+
return `"${text}"`;
|
|
25
|
+
};
|
|
26
|
+
const listOptions = (options) => {
|
|
27
|
+
const listed = options
|
|
28
|
+
.slice(0, MAX_LISTED_OPTIONS)
|
|
29
|
+
.map((option) => {
|
|
30
|
+
return quote(option);
|
|
31
|
+
})
|
|
32
|
+
.join(", ");
|
|
33
|
+
const more = options.length - MAX_LISTED_OPTIONS;
|
|
34
|
+
return more > 0 ? `${listed} and ${more} more` : listed;
|
|
35
|
+
};
|
|
36
|
+
const canonicalize = (value) => {
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
return JSON.stringify([...value]
|
|
39
|
+
.map((entry) => {
|
|
40
|
+
return String(entry);
|
|
41
|
+
})
|
|
42
|
+
.sort());
|
|
43
|
+
}
|
|
44
|
+
if (value instanceof Date) {
|
|
45
|
+
// toISOString throws on an invalid date; that one is checked, not compared.
|
|
46
|
+
return isNaN(value.getTime())
|
|
47
|
+
? "invalid-date"
|
|
48
|
+
: JSON.stringify(value.toISOString());
|
|
49
|
+
}
|
|
50
|
+
return JSON.stringify(value !== null && value !== void 0 ? value : null);
|
|
51
|
+
};
|
|
52
|
+
const isPlainObject = (value) => {
|
|
53
|
+
return (value !== null &&
|
|
54
|
+
typeof value === "object" &&
|
|
55
|
+
!Array.isArray(value) &&
|
|
56
|
+
!(value instanceof Date));
|
|
57
|
+
};
|
|
58
|
+
// The values a dropdown option can be written as: its text, or a bare number.
|
|
59
|
+
const isOptionScalar = (value) => {
|
|
60
|
+
return (typeof value === "string" ||
|
|
61
|
+
(typeof value === "number" && Number.isFinite(value)) ||
|
|
62
|
+
typeof value === "boolean");
|
|
63
|
+
};
|
|
64
|
+
const isNumberValue = (value) => {
|
|
65
|
+
if (typeof value === "number") {
|
|
66
|
+
return Number.isFinite(value);
|
|
67
|
+
}
|
|
68
|
+
/*
|
|
69
|
+
* The dashboard's number input submits what was typed, as a string -
|
|
70
|
+
* "0", "42", "3.5" - which is how most stored Number values look.
|
|
71
|
+
*/
|
|
72
|
+
if (typeof value === "string") {
|
|
73
|
+
const trimmed = value.trim();
|
|
74
|
+
return trimmed.length > 0 && Number.isFinite(Number(trimmed));
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
};
|
|
78
|
+
const isBooleanValue = (value) => {
|
|
79
|
+
/*
|
|
80
|
+
* The strings are accepted because the table and filters already read them
|
|
81
|
+
* as Yes and No, and hand-written API requests send them.
|
|
82
|
+
*/
|
|
83
|
+
return typeof value === "boolean" || value === "true" || value === "false";
|
|
84
|
+
};
|
|
85
|
+
const isDateValue = (value) => {
|
|
86
|
+
if (value instanceof Date) {
|
|
87
|
+
return !isNaN(value.getTime());
|
|
88
|
+
}
|
|
89
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return !isNaN(new Date(value.trim()).getTime());
|
|
93
|
+
};
|
|
94
|
+
// Text of any kind: what was typed, or a number or yes/no sent over the API.
|
|
95
|
+
const isTextValue = (value) => {
|
|
96
|
+
return (typeof value === "string" ||
|
|
97
|
+
(typeof value === "number" && Number.isFinite(value)) ||
|
|
98
|
+
typeof value === "boolean");
|
|
99
|
+
};
|
|
100
|
+
const validateDropdownValue = (data) => {
|
|
101
|
+
const name = data.definition.name;
|
|
102
|
+
if (!isOptionScalar(data.value)) {
|
|
103
|
+
return `${quote(name)} holds one of its options, but was sent ${quote(data.value)}.`;
|
|
104
|
+
}
|
|
105
|
+
const options = parseCustomFieldDropdownOptions(data.definition.dropdownOptions).map((option) => {
|
|
106
|
+
return option.value;
|
|
107
|
+
});
|
|
108
|
+
/*
|
|
109
|
+
* A dropdown nobody has given options to cannot be checked against them;
|
|
110
|
+
* refusing every value would make the field unusable over the API until an
|
|
111
|
+
* admin finishes defining it.
|
|
112
|
+
*/
|
|
113
|
+
if (options.length === 0 || options.includes(String(data.value))) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return `${quote(data.value)} is not one of the options for ${quote(name)}. Choose one of: ${listOptions(options)}.`;
|
|
117
|
+
};
|
|
118
|
+
const validateMultiSelectValue = (data) => {
|
|
119
|
+
const name = data.definition.name;
|
|
120
|
+
/*
|
|
121
|
+
* A single value is accepted as a selection of one: a field that was
|
|
122
|
+
* switched from single to multi select still holds bare strings, and the
|
|
123
|
+
* table renders one that way.
|
|
124
|
+
*/
|
|
125
|
+
const entries = Array.isArray(data.value)
|
|
126
|
+
? data.value
|
|
127
|
+
: [data.value];
|
|
128
|
+
const invalidShape = entries.find((entry) => {
|
|
129
|
+
return !isOptionScalar(entry);
|
|
130
|
+
});
|
|
131
|
+
if (invalidShape !== undefined) {
|
|
132
|
+
return `${quote(name)} holds a list of its options, but was sent ${quote(data.value)}.`;
|
|
133
|
+
}
|
|
134
|
+
const options = parseCustomFieldDropdownOptions(data.definition.dropdownOptions).map((option) => {
|
|
135
|
+
return option.value;
|
|
136
|
+
});
|
|
137
|
+
if (options.length === 0) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
// Entries the record already holds stay valid, removed options included.
|
|
141
|
+
const alreadyHeld = new Set((Array.isArray(data.storedValue)
|
|
142
|
+
? data.storedValue
|
|
143
|
+
: data.storedValue === undefined || data.storedValue === null
|
|
144
|
+
? []
|
|
145
|
+
: [data.storedValue]).map((entry) => {
|
|
146
|
+
return String(entry);
|
|
147
|
+
}));
|
|
148
|
+
const notOptions = entries
|
|
149
|
+
.map((entry) => {
|
|
150
|
+
return String(entry);
|
|
151
|
+
})
|
|
152
|
+
.filter((entry) => {
|
|
153
|
+
return !alreadyHeld.has(entry) && !options.includes(entry);
|
|
154
|
+
});
|
|
155
|
+
if (notOptions.length === 0) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return `${notOptions
|
|
159
|
+
.map((entry) => {
|
|
160
|
+
return quote(entry);
|
|
161
|
+
})
|
|
162
|
+
.join(", ")} ${notOptions.length === 1 ? "is not one of" : "are not among"} the options for ${quote(name)}. Choose from: ${listOptions(options)}.`;
|
|
163
|
+
};
|
|
164
|
+
const validateValue = (data) => {
|
|
165
|
+
const name = data.definition.name;
|
|
166
|
+
switch (data.definition.customFieldType) {
|
|
167
|
+
case CustomFieldType.Text:
|
|
168
|
+
case CustomFieldType.LongText:
|
|
169
|
+
case CustomFieldType.Markdown:
|
|
170
|
+
return isTextValue(data.value)
|
|
171
|
+
? null
|
|
172
|
+
: `${quote(name)} holds text, but was sent ${quote(data.value)}.`;
|
|
173
|
+
case CustomFieldType.Number:
|
|
174
|
+
return isNumberValue(data.value)
|
|
175
|
+
? null
|
|
176
|
+
: `${quote(name)} holds a number, but was sent ${quote(data.value)}.`;
|
|
177
|
+
case CustomFieldType.Boolean:
|
|
178
|
+
return isBooleanValue(data.value)
|
|
179
|
+
? null
|
|
180
|
+
: `${quote(name)} holds true or false, but was sent ${quote(data.value)}.`;
|
|
181
|
+
case CustomFieldType.Date:
|
|
182
|
+
return isDateValue(data.value)
|
|
183
|
+
? null
|
|
184
|
+
: `${quote(name)} holds a date, but was sent ${quote(data.value)}.`;
|
|
185
|
+
case CustomFieldType.DateTime:
|
|
186
|
+
return isDateValue(data.value)
|
|
187
|
+
? null
|
|
188
|
+
: `${quote(name)} holds a date and time, but was sent ${quote(data.value)}.`;
|
|
189
|
+
case CustomFieldType.Dropdown:
|
|
190
|
+
return validateDropdownValue(data);
|
|
191
|
+
case CustomFieldType.MultiSelectDropdown:
|
|
192
|
+
return validateMultiSelectValue(data);
|
|
193
|
+
default:
|
|
194
|
+
// No type, or one this version does not know: nothing to check.
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Every value this write changes that does not fit its field, in the order
|
|
200
|
+
* the write lists them. Empty when the write is fine.
|
|
201
|
+
*/
|
|
202
|
+
export const validateCustomFieldValues = (data) => {
|
|
203
|
+
if (!isPlainObject(data.customFields)) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
const stored = isPlainObject(data.storedCustomFields)
|
|
207
|
+
? data.storedCustomFields
|
|
208
|
+
: {};
|
|
209
|
+
const definitionsByName = new Map();
|
|
210
|
+
for (const definition of data.definitions || []) {
|
|
211
|
+
if (definition && typeof definition.name === "string") {
|
|
212
|
+
definitionsByName.set(definition.name, definition);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const errors = [];
|
|
216
|
+
for (const [fieldName, value] of Object.entries(data.customFields)) {
|
|
217
|
+
const definition = definitionsByName.get(fieldName);
|
|
218
|
+
if (!definition) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (isCustomFieldValueEmpty(value)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const storedValue = Object.prototype.hasOwnProperty.call(stored, fieldName)
|
|
225
|
+
? stored[fieldName]
|
|
226
|
+
: undefined;
|
|
227
|
+
if (canonicalize(value) === canonicalize(storedValue)) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const message = validateValue({
|
|
231
|
+
definition: definition,
|
|
232
|
+
value: value,
|
|
233
|
+
storedValue: storedValue,
|
|
234
|
+
});
|
|
235
|
+
if (message) {
|
|
236
|
+
errors.push({ fieldName: fieldName, message: message });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return errors;
|
|
240
|
+
};
|
|
241
|
+
/** One message for an API error response. */
|
|
242
|
+
export const formatCustomFieldValueValidationErrors = (errors) => {
|
|
243
|
+
return errors
|
|
244
|
+
.map((error) => {
|
|
245
|
+
return error.message;
|
|
246
|
+
})
|
|
247
|
+
.join(" ");
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* The values of a bag that a create would accept, each judged on its own as
|
|
251
|
+
* if it were new: an option since removed from a dropdown, or a word in a
|
|
252
|
+
* Number field, is left out.
|
|
253
|
+
*
|
|
254
|
+
* For a bag copied onto a new record from elsewhere - an incident template's
|
|
255
|
+
* values, when an incident is declared from it. Those were written long
|
|
256
|
+
* before the incident and may no longer fit their fields; sent as they are,
|
|
257
|
+
* one stale value would refuse the whole declaration over a field the person
|
|
258
|
+
* declaring may not even be shown. Keys with no definition, and empty values,
|
|
259
|
+
* are kept, as validateCustomFieldValues passes them.
|
|
260
|
+
*/
|
|
261
|
+
export const keepValidCustomFieldValues = (data) => {
|
|
262
|
+
if (!isPlainObject(data.customFields)) {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
const invalidFieldNames = new Set(validateCustomFieldValues({
|
|
266
|
+
definitions: data.definitions,
|
|
267
|
+
customFields: data.customFields,
|
|
268
|
+
storedCustomFields: {},
|
|
269
|
+
}).map((error) => {
|
|
270
|
+
return error.fieldName;
|
|
271
|
+
}));
|
|
272
|
+
const result = {};
|
|
273
|
+
for (const [fieldName, value] of Object.entries(data.customFields)) {
|
|
274
|
+
if (!invalidFieldNames.has(fieldName)) {
|
|
275
|
+
result[fieldName] = value;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
};
|
|
280
|
+
//# sourceMappingURL=CustomFieldValueValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldValueValidator.js","sourceRoot":"","sources":["../../../../Types/CustomField/CustomFieldValueValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import SubscriberNotificationTemplateCompiler from "../StatusPage/SubscriberNotificationTemplateCompiler";
|
|
2
|
+
/*
|
|
3
|
+
* The key an incident custom field is reached by in a template:
|
|
4
|
+
* {{customFields.<key>}} in a status page's custom subscriber email, and in
|
|
5
|
+
* anything else that fills placeholders the same way.
|
|
6
|
+
*
|
|
7
|
+
* Why a key at all, rather than the field's name: values are stored under
|
|
8
|
+
* the display name ("Expected Resolution"), and a name can hold anything a
|
|
9
|
+
* person types - spaces, hyphens, accents, emoji, other scripts. The template
|
|
10
|
+
* compiler only fills placeholders made of ASCII word characters and dots
|
|
11
|
+
* (SubscriberNotificationTemplateCompiler.isPlaceholderName), so a name is
|
|
12
|
+
* not something a template can refer to. The key is made from the name once,
|
|
13
|
+
* when the field is created, and never changes: renaming a field must not
|
|
14
|
+
* silently break every template that already uses it.
|
|
15
|
+
*
|
|
16
|
+
* Why not Slug.getSlug: it joins words with hyphens and appends a random
|
|
17
|
+
* number, and a hyphen is exactly what the placeholder pattern rejects.
|
|
18
|
+
*
|
|
19
|
+
* The shape: lowercase letters and digits in runs joined by single
|
|
20
|
+
* underscores - "expected_resolution" - with no leading, trailing or doubled
|
|
21
|
+
* underscore. A key already taken in the project gets "_2", "_3", ... in the
|
|
22
|
+
* order fields were created.
|
|
23
|
+
*
|
|
24
|
+
* Pure, and free of database and React imports, so the service that stamps a
|
|
25
|
+
* new field, the migration that stamps the existing ones and the dashboard
|
|
26
|
+
* all produce the same key from the same name.
|
|
27
|
+
*/
|
|
28
|
+
/*
|
|
29
|
+
* Long enough for any name a person would give a field, short enough to type
|
|
30
|
+
* into a template. The column itself allows 100 characters.
|
|
31
|
+
*/
|
|
32
|
+
export const CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH = 64;
|
|
33
|
+
/*
|
|
34
|
+
* What a name with nothing usable in it becomes: "!!!", or a name written
|
|
35
|
+
* wholly in a script with no Latin transliteration here, such as "影响" or
|
|
36
|
+
* "Влияние". Numbered like any other collision, so a project with several
|
|
37
|
+
* gets field, field_2, field_3.
|
|
38
|
+
*/
|
|
39
|
+
export const CUSTOM_FIELD_VARIABLE_KEY_FALLBACK = "field";
|
|
40
|
+
/*
|
|
41
|
+
* Where a key sits among the template variables: {{customFields.<key>}}.
|
|
42
|
+
*/
|
|
43
|
+
export const CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX = "customFields.";
|
|
44
|
+
const VARIABLE_KEY_PATTERN = /^[a-z0-9]+(?:_[a-z0-9]+)*$/;
|
|
45
|
+
/*
|
|
46
|
+
* Latin letters that Unicode normalisation does not take apart into a base
|
|
47
|
+
* letter and an accent, so stripping accents alone would lose them: "Größe"
|
|
48
|
+
* would become "gr_e". Everything else accented (é, ñ, å, ç, ő, ...) is
|
|
49
|
+
* handled by NFKD below.
|
|
50
|
+
*/
|
|
51
|
+
const LETTERS_WITHOUT_DECOMPOSITION = {
|
|
52
|
+
ß: "ss",
|
|
53
|
+
ẞ: "ss",
|
|
54
|
+
æ: "ae",
|
|
55
|
+
Æ: "ae",
|
|
56
|
+
œ: "oe",
|
|
57
|
+
Œ: "oe",
|
|
58
|
+
ø: "o",
|
|
59
|
+
Ø: "o",
|
|
60
|
+
đ: "d",
|
|
61
|
+
Đ: "d",
|
|
62
|
+
ð: "d",
|
|
63
|
+
Ð: "d",
|
|
64
|
+
þ: "th",
|
|
65
|
+
Þ: "th",
|
|
66
|
+
ł: "l",
|
|
67
|
+
Ł: "l",
|
|
68
|
+
ı: "i",
|
|
69
|
+
ħ: "h",
|
|
70
|
+
Ħ: "h",
|
|
71
|
+
ŧ: "t",
|
|
72
|
+
Ŧ: "t",
|
|
73
|
+
ŋ: "ng",
|
|
74
|
+
Ŋ: "ng",
|
|
75
|
+
ĸ: "k",
|
|
76
|
+
};
|
|
77
|
+
const transliterate = (name) => {
|
|
78
|
+
var _a;
|
|
79
|
+
let result = "";
|
|
80
|
+
for (const character of name) {
|
|
81
|
+
result += (_a = LETTERS_WITHOUT_DECOMPOSITION[character]) !== null && _a !== void 0 ? _a : character;
|
|
82
|
+
}
|
|
83
|
+
/*
|
|
84
|
+
* NFKD splits "é" into "e" plus a combining accent, and also folds
|
|
85
|
+
* compatibility forms: full-width "A" to "A", "fi" to "fi", "①" to "1".
|
|
86
|
+
* Dropping the combining marks then leaves the base letters.
|
|
87
|
+
*/
|
|
88
|
+
return result.normalize("NFKD").replace(/\p{M}/gu, "");
|
|
89
|
+
};
|
|
90
|
+
// Cut to a length without leaving a dangling underscore at the end.
|
|
91
|
+
const trimToLength = (key, maxLength) => {
|
|
92
|
+
return key.slice(0, Math.max(maxLength, 0)).replace(/_+$/, "");
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The key a field of this name gets when nothing else in the project has it.
|
|
96
|
+
* Never empty, and always a valid key.
|
|
97
|
+
*/
|
|
98
|
+
export const getCustomFieldVariableKeyBase = (name) => {
|
|
99
|
+
const words = transliterate(typeof name === "string" ? name : "")
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
102
|
+
.replace(/^_+|_+$/g, "");
|
|
103
|
+
const key = trimToLength(words, CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH);
|
|
104
|
+
return key || CUSTOM_FIELD_VARIABLE_KEY_FALLBACK;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Whether a key has the generator's shape and can be used as
|
|
108
|
+
* {{customFields.<key>}} in a template the compiler fills.
|
|
109
|
+
*/
|
|
110
|
+
export const isValidCustomFieldVariableKey = (key) => {
|
|
111
|
+
return (typeof key === "string" &&
|
|
112
|
+
key.length > 0 &&
|
|
113
|
+
key.length <= CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH &&
|
|
114
|
+
VARIABLE_KEY_PATTERN.test(key) &&
|
|
115
|
+
SubscriberNotificationTemplateCompiler.isPlaceholderName(`${CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX}${key}`));
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* The key for a new field: its name's key, or the first of name_2, name_3,
|
|
119
|
+
* ... that no other field in the project holds.
|
|
120
|
+
*/
|
|
121
|
+
export const generateCustomFieldVariableKey = (data) => {
|
|
122
|
+
const taken = new Set();
|
|
123
|
+
for (const key of data.existingKeys) {
|
|
124
|
+
if (typeof key === "string" && key.length > 0) {
|
|
125
|
+
taken.add(key);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const base = getCustomFieldVariableKeyBase(data.name);
|
|
129
|
+
if (!taken.has(base)) {
|
|
130
|
+
return base;
|
|
131
|
+
}
|
|
132
|
+
/*
|
|
133
|
+
* One more than there are keys is always enough: at most that many of the
|
|
134
|
+
* candidates can be taken.
|
|
135
|
+
*/
|
|
136
|
+
for (let suffix = 2; suffix <= taken.size + 2; suffix++) {
|
|
137
|
+
const ending = `_${suffix}`;
|
|
138
|
+
const candidate = `${trimToLength(base, CUSTOM_FIELD_VARIABLE_KEY_MAX_LENGTH - ending.length)}${ending}`;
|
|
139
|
+
if (!taken.has(candidate)) {
|
|
140
|
+
return candidate;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Unreachable (see the loop bound); kept so the function always returns.
|
|
144
|
+
return `${CUSTOM_FIELD_VARIABLE_KEY_FALLBACK}_${taken.size + 2}`;
|
|
145
|
+
};
|
|
146
|
+
/** "expected_resolution" -> "customFields.expected_resolution". */
|
|
147
|
+
export const getCustomFieldTemplateVariableName = (key) => {
|
|
148
|
+
return `${CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX}${key}`;
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=CustomFieldVariableKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldVariableKey.js","sourceRoot":"","sources":["../../../../Types/CustomField/CustomFieldVariableKey.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A value that is ALREADY HTML and may go into an HTML document as written.
|
|
3
|
+
*
|
|
4
|
+
* Status page subscriber emails are filled from two kinds of value, and a
|
|
5
|
+
* plain string cannot say which one it holds:
|
|
6
|
+
*
|
|
7
|
+
* - plain text: an incident, episode, maintenance or announcement title, a
|
|
8
|
+
* severity or state name, a status page or resource name, a URL. Project
|
|
9
|
+
* members type these, and a title is exactly what someone outside the
|
|
10
|
+
* team gets to influence (an incident created from a monitor, an API
|
|
11
|
+
* call, a Slack command). Put into HTML as written, a title such as
|
|
12
|
+
* `<a href="https://evil.example">Reset your password</a>` becomes a live
|
|
13
|
+
* link in an email the recipient trusts.
|
|
14
|
+
* - HTML this code produced itself: Markdown.convertToHTML output (which
|
|
15
|
+
* escapes the raw HTML an author typed and keeps only http, https and
|
|
16
|
+
* mailto links), the date helper's multi-timezone HTML, and the resource
|
|
17
|
+
* list StatusPageResourceUtil.getResourcesGroupedByGroupName builds from
|
|
18
|
+
* escaped names.
|
|
19
|
+
*
|
|
20
|
+
* So the HTML ones travel wrapped in this type, and anything that is still a
|
|
21
|
+
* string is treated as plain text and escaped where it meets HTML (see
|
|
22
|
+
* SubscriberNotificationTemplateCompiler.compileEmailBodyTemplate). A value
|
|
23
|
+
* nobody thought about is therefore escaped, which at worst shows the reader
|
|
24
|
+
* an "&" too many; it can never become markup by accident.
|
|
25
|
+
*
|
|
26
|
+
* Kept free of server dependencies so both the server and the dashboard can
|
|
27
|
+
* use it.
|
|
28
|
+
*/
|
|
29
|
+
var _a;
|
|
30
|
+
/*
|
|
31
|
+
* Marks instances without instanceof, which a second copy of this module (a
|
|
32
|
+
* test's isolated module registry, a duplicated bundle) would defeat.
|
|
33
|
+
*/
|
|
34
|
+
const SAFE_HTML_BRAND = Symbol.for("OneUptime.SafeHtml");
|
|
35
|
+
class SafeHtml {
|
|
36
|
+
constructor(html) {
|
|
37
|
+
// Read by isSafeHtml; a symbol key, so JSON.stringify leaves it out.
|
|
38
|
+
this[_a] = true;
|
|
39
|
+
this.html = html;
|
|
40
|
+
}
|
|
41
|
+
/*
|
|
42
|
+
* Wraps markup this code produced from values it had already made safe:
|
|
43
|
+
* Markdown.convertToHTML(..., MarkdownContentType.Email) output,
|
|
44
|
+
* OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones output, and
|
|
45
|
+
* StatusPageResourceUtil.getResourcesGroupedByGroupName output. Never a
|
|
46
|
+
* value a user typed.
|
|
47
|
+
*/
|
|
48
|
+
static fromTrustedHtml(html) {
|
|
49
|
+
return new SafeHtml(html || "");
|
|
50
|
+
}
|
|
51
|
+
// Plain text, escaped so it reads the same in HTML.
|
|
52
|
+
static fromPlainText(text) {
|
|
53
|
+
return new SafeHtml(SafeHtml.escape(text || ""));
|
|
54
|
+
}
|
|
55
|
+
/*
|
|
56
|
+
* Escapes the five characters that can break out of HTML text or a quoted
|
|
57
|
+
* attribute value. The ampersand goes first, so the entities the other
|
|
58
|
+
* four become are not escaped a second time.
|
|
59
|
+
*
|
|
60
|
+
* This is the one implementation: Markdown.escapeHtml calls it.
|
|
61
|
+
*/
|
|
62
|
+
static escape(text) {
|
|
63
|
+
return String(text)
|
|
64
|
+
.replace(/&/g, "&")
|
|
65
|
+
.replace(/</g, "<")
|
|
66
|
+
.replace(/>/g, ">")
|
|
67
|
+
.replace(/"/g, """)
|
|
68
|
+
.replace(/'/g, "'");
|
|
69
|
+
}
|
|
70
|
+
static isSafeHtml(value) {
|
|
71
|
+
return (typeof value === "object" &&
|
|
72
|
+
value !== null &&
|
|
73
|
+
value[SAFE_HTML_BRAND] === true);
|
|
74
|
+
}
|
|
75
|
+
toHtml() {
|
|
76
|
+
return this.html;
|
|
77
|
+
}
|
|
78
|
+
toString() {
|
|
79
|
+
return this.html;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
_a = SAFE_HTML_BRAND;
|
|
83
|
+
export default SafeHtml;
|
|
84
|
+
//# sourceMappingURL=SafeHtml.js.map
|