@capillarytech/creatives-library 8.0.263 → 8.0.265
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/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +1 -3
- package/initialReducer.js +0 -2
- package/package.json +1 -1
- package/services/api.js +0 -15
- package/services/tests/api.test.js +0 -34
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +35 -17
- package/tests/integration/TemplateCreation/api-response.js +1 -31
- package/tests/integration/TemplateCreation/msw-handler.js +0 -2
- package/utils/common.js +0 -11
- package/utils/commonUtils.js +5 -28
- package/utils/tests/commonUtil.test.js +0 -224
- package/utils/tests/transformerUtils.test.js +0 -297
- package/utils/transformTemplateConfig.js +10 -0
- package/utils/transformerUtils.js +0 -40
- package/v2Components/CapDeviceContent/index.js +56 -61
- package/v2Components/CapImageUpload/constants.js +0 -2
- package/v2Components/CapImageUpload/index.js +16 -65
- package/v2Components/CapImageUpload/index.scss +1 -4
- package/v2Components/CapImageUpload/messages.js +1 -5
- package/v2Components/CapTagList/index.js +1 -6
- package/v2Components/CapTagListWithInput/index.js +1 -5
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
- package/v2Components/ErrorInfoNote/index.js +72 -402
- package/v2Components/ErrorInfoNote/messages.js +6 -32
- package/v2Components/ErrorInfoNote/style.scss +6 -278
- package/v2Components/FormBuilder/tests/index.test.js +4 -13
- package/v2Components/HtmlEditor/HTMLEditor.js +99 -418
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1882
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
- package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -9
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +6 -31
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +10 -7
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -18
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -36
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -46
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +46 -52
- package/v2Components/HtmlEditor/constants.js +20 -45
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -351
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
- package/v2Components/HtmlEditor/hooks/useValidation.js +56 -213
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +94 -102
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -214
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
- package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +124 -158
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
- package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
- package/v2Components/MobilePushPreviewV2/index.js +7 -33
- package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
- package/v2Components/TemplatePreview/index.js +32 -47
- package/v2Components/TemplatePreview/messages.js +0 -4
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/BeeEditor/index.js +90 -172
- package/v2Containers/CreativesContainer/SlideBoxContent.js +53 -184
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -3
- package/v2Containers/CreativesContainer/constants.js +0 -4
- package/v2Containers/CreativesContainer/index.js +46 -408
- package/v2Containers/CreativesContainer/messages.js +0 -12
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -342
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -103
- package/v2Containers/Email/actions.js +0 -7
- package/v2Containers/Email/constants.js +1 -5
- package/v2Containers/Email/index.js +36 -237
- package/v2Containers/Email/messages.js +0 -32
- package/v2Containers/Email/reducer.js +1 -12
- package/v2Containers/Email/sagas.js +7 -61
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
- package/v2Containers/Email/tests/reducer.test.js +0 -46
- package/v2Containers/Email/tests/sagas.test.js +29 -320
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
- package/v2Containers/EmailWrapper/constants.js +0 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
- package/v2Containers/EmailWrapper/index.js +23 -103
- package/v2Containers/EmailWrapper/messages.js +1 -65
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
- package/v2Containers/InApp/actions.js +0 -7
- package/v2Containers/InApp/constants.js +4 -20
- package/v2Containers/InApp/index.js +359 -802
- package/v2Containers/InApp/index.scss +3 -4
- package/v2Containers/InApp/messages.js +3 -7
- package/v2Containers/InApp/reducer.js +3 -21
- package/v2Containers/InApp/sagas.js +9 -29
- package/v2Containers/InApp/selectors.js +5 -25
- package/v2Containers/InApp/tests/index.test.js +50 -154
- package/v2Containers/InApp/tests/reducer.test.js +0 -34
- package/v2Containers/InApp/tests/sagas.test.js +9 -61
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
- package/v2Containers/TagList/index.js +19 -62
- package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
- package/v2Containers/Templates/_templates.scss +1 -265
- package/v2Containers/Templates/actions.js +1 -2
- package/v2Containers/Templates/constants.js +0 -1
- package/v2Containers/Templates/index.js +38 -363
- package/v2Containers/Templates/messages.js +0 -28
- package/v2Containers/Templates/reducer.js +0 -2
- package/v2Containers/Templates/tests/index.test.js +0 -10
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +2 -4
- package/v2Containers/TemplatesV2/index.js +7 -15
- package/v2Containers/TemplatesV2/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -34
- package/utils/imageUrlUpload.js +0 -141
- package/v2Components/CapImageUrlUpload/constants.js +0 -26
- package/v2Components/CapImageUrlUpload/index.js +0 -365
- package/v2Components/CapImageUrlUpload/index.scss +0 -35
- package/v2Components/CapImageUrlUpload/messages.js +0 -47
- package/v2Components/ErrorInfoNote/constants.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -870
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -281
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -295
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
- package/v2Components/HtmlEditor/utils/validationConstants.js +0 -38
- package/v2Components/MobilePushPreviewV2/constants.js +0 -6
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
- package/v2Containers/BeePopupEditor/constants.js +0 -10
- package/v2Containers/BeePopupEditor/index.js +0 -194
- package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1246
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -2472
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -956
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
- package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
- package/v2Containers/InApp/tests/selectors.test.js +0 -612
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
- package/v2Containers/InAppWrapper/constants.js +0 -16
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
- package/v2Containers/InAppWrapper/index.js +0 -148
- package/v2Containers/InAppWrapper/messages.js +0 -49
- package/v2Containers/InappAdvance/index.js +0 -1099
- package/v2Containers/InappAdvance/index.scss +0 -10
- package/v2Containers/InappAdvance/tests/index.test.js +0 -448
- package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
- package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
- package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
- package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
- package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
- package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
- package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
- package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
- package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
- package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
- package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
- package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
- package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
- package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
- package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
- package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
- package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
- package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
- package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
- package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
- package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
- package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
- package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
- package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
- package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
- package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
- package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
- package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
- package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
- package/v2Containers/WebPush/Create/index.js +0 -1148
- package/v2Containers/WebPush/Create/index.scss +0 -134
- package/v2Containers/WebPush/Create/messages.js +0 -211
- package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
- package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
- package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
- package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
- package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
- package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
- package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
- package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
- package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
- package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
- package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
- package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
- package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
- package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
- package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
- package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
- package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
- package/v2Containers/WebPush/Create/preview/constants.js +0 -637
- package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
- package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
- package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
- package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
- package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
- package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
- package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
- package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
- package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
- package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
- package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
- package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
- package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
- package/v2Containers/WebPush/Create/utils/validation.js +0 -75
- package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
- package/v2Containers/WebPush/actions.js +0 -60
- package/v2Containers/WebPush/constants.js +0 -132
- package/v2Containers/WebPush/index.js +0 -2
- package/v2Containers/WebPush/reducer.js +0 -104
- package/v2Containers/WebPush/sagas.js +0 -119
- package/v2Containers/WebPush/selectors.js +0 -65
- package/v2Containers/WebPush/tests/reducer.test.js +0 -863
- package/v2Containers/WebPush/tests/sagas.test.js +0 -566
- package/v2Containers/WebPush/tests/selectors.test.js +0 -960
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validation Hook for HTML Editor
|
|
3
|
-
* Manages real-time validation and error display
|
|
4
|
-
* UI gating: only Rule Group #1 (Input & Sanitization) blocks Save/Update/Preview/Test.
|
|
5
|
-
* All other rules are warnings for backward compatibility with CKEditor legacy templates.
|
|
3
|
+
* Manages real-time validation and error display
|
|
6
4
|
*/
|
|
7
5
|
|
|
8
|
-
import {
|
|
9
|
-
useState, useEffect, useCallback, useRef,
|
|
10
|
-
} from 'react';
|
|
6
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
11
7
|
import { validateHTML, extractAndValidateCSS } from '../utils/htmlValidator';
|
|
12
8
|
import { sanitizeHTML, isContentSafe, findUnsafeContent } from '../utils/contentSanitizer';
|
|
13
|
-
import { BLOCKING_ERROR_RULE_IDS, VALIDATION_SEVERITY } from '../constants';
|
|
14
|
-
import { ISSUE_SOURCES } from '../utils/validationConstants';
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
11
|
* Custom hook for managing HTML/CSS validation
|
|
@@ -22,61 +16,12 @@ import { ISSUE_SOURCES } from '../utils/validationConstants';
|
|
|
22
16
|
* @param {Function} formatValidatorMessage - Message formatter function for validator internationalization
|
|
23
17
|
* @returns {Object} Validation state and methods
|
|
24
18
|
*/
|
|
25
|
-
/**
|
|
26
|
-
* Get line number for a character position in text
|
|
27
|
-
*/
|
|
28
|
-
const getLineNumberFromPosition = (text, position) => {
|
|
29
|
-
if (position === undefined || position < 0) return 1;
|
|
30
|
-
return text.substring(0, position).split('\n').length;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Get 1-based line and column from a character position in text
|
|
35
|
-
*/
|
|
36
|
-
const getLineAndColumnFromPosition = (text, position) => {
|
|
37
|
-
if (!text || position === undefined || position < 0) {
|
|
38
|
-
return { line: 1, column: 1 };
|
|
39
|
-
}
|
|
40
|
-
const before = text.substring(0, position);
|
|
41
|
-
const lines = before.split('\n');
|
|
42
|
-
const line = lines.length;
|
|
43
|
-
const lastLine = lines[lines.length - 1] || '';
|
|
44
|
-
const column = lastLine.length + 1;
|
|
45
|
-
return { line, column };
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Find line number for a tag or pattern in content
|
|
50
|
-
* Used for API errors to locate where the error occurs
|
|
51
|
-
*/
|
|
52
|
-
const findLineNumberForTag = (content, tagName) => {
|
|
53
|
-
if (!content || !tagName) return null;
|
|
54
|
-
|
|
55
|
-
// Try to find the tag in the content
|
|
56
|
-
// Look for patterns like {{ tagName }}, {{tagName}}, etc.
|
|
57
|
-
const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
58
|
-
const patterns = [
|
|
59
|
-
new RegExp(`\\{\\{\\s*${escapedTagName}\\s*\\}\\}`, 'g'),
|
|
60
|
-
new RegExp(`\\{%[^%]*${escapedTagName}[^%]*%\\}`, 'g'),
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
for (const pattern of patterns) {
|
|
64
|
-
const match = pattern.exec(content);
|
|
65
|
-
if (match) {
|
|
66
|
-
return getLineNumberFromPosition(content, match.index);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return null;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
19
|
export const useValidation = (content, variant = 'email', options = {}, formatSanitizerMessage = null, formatValidatorMessage = null) => {
|
|
74
20
|
const {
|
|
75
21
|
enableRealTime = true,
|
|
76
22
|
debounceMs = 500,
|
|
77
23
|
enableSanitization = true,
|
|
78
|
-
securityLevel = 'standard'
|
|
79
|
-
apiValidationErrors = null, // API validation errors from validateLiquidTemplateContent
|
|
24
|
+
securityLevel = 'standard'
|
|
80
25
|
} = options;
|
|
81
26
|
|
|
82
27
|
// Validation state
|
|
@@ -97,8 +42,8 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
97
42
|
totalErrors: 0,
|
|
98
43
|
totalWarnings: 0,
|
|
99
44
|
totalInfo: 0,
|
|
100
|
-
hasSecurityIssues: false
|
|
101
|
-
}
|
|
45
|
+
hasSecurityIssues: false
|
|
46
|
+
}
|
|
102
47
|
});
|
|
103
48
|
|
|
104
49
|
// Refs for debouncing
|
|
@@ -110,7 +55,7 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
110
55
|
*/
|
|
111
56
|
const performValidation = useCallback(async (htmlContent) => {
|
|
112
57
|
if (!htmlContent) {
|
|
113
|
-
setValidationState(
|
|
58
|
+
setValidationState(prev => ({
|
|
114
59
|
...prev,
|
|
115
60
|
isValidating: false,
|
|
116
61
|
htmlErrors: [],
|
|
@@ -127,13 +72,13 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
127
72
|
totalErrors: 0,
|
|
128
73
|
totalWarnings: 0,
|
|
129
74
|
totalInfo: 0,
|
|
130
|
-
hasSecurityIssues: false
|
|
131
|
-
}
|
|
75
|
+
hasSecurityIssues: false
|
|
76
|
+
}
|
|
132
77
|
}));
|
|
133
78
|
return;
|
|
134
79
|
}
|
|
135
80
|
|
|
136
|
-
setValidationState(
|
|
81
|
+
setValidationState(prev => ({ ...prev, isValidating: true }));
|
|
137
82
|
|
|
138
83
|
try {
|
|
139
84
|
// 1. HTML Validation
|
|
@@ -146,21 +91,15 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
146
91
|
const isSecure = isContentSafe(htmlContent);
|
|
147
92
|
const securityIssues = isSecure ? [] : findUnsafeContent(htmlContent);
|
|
148
93
|
|
|
149
|
-
// 4. Sanitization (if enabled)
|
|
94
|
+
// 4. Sanitization (if enabled)
|
|
150
95
|
let sanitizationResult = null;
|
|
151
96
|
if (enableSanitization) {
|
|
152
97
|
sanitizationResult = sanitizeHTML(htmlContent, variant, securityLevel, formatSanitizerMessage);
|
|
153
98
|
}
|
|
154
99
|
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
// Summary: totalErrors/totalWarnings are for display; blocking count is for gating
|
|
160
|
-
const totalErrors = htmlValidation.errors.length + cssValidation.errors.length + blockingSanitizerCount + protocolSecurityCount;
|
|
161
|
-
const totalWarnings = htmlValidation.warnings.length + cssValidation.warnings.length
|
|
162
|
-
+ (sanitizationWarnings.length - blockingSanitizerCount)
|
|
163
|
-
+ (securityIssues.length - protocolSecurityCount);
|
|
100
|
+
// Calculate summary
|
|
101
|
+
const totalErrors = htmlValidation.errors.length + cssValidation.errors.length;
|
|
102
|
+
const totalWarnings = htmlValidation.warnings.length + cssValidation.warnings.length;
|
|
164
103
|
const totalInfo = htmlValidation.info.length + cssValidation.info.length;
|
|
165
104
|
const hasSecurityIssues = securityIssues.length > 0;
|
|
166
105
|
|
|
@@ -175,19 +114,20 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
175
114
|
cssWarnings: cssValidation.warnings,
|
|
176
115
|
cssInfo: cssValidation.info,
|
|
177
116
|
securityIssues,
|
|
178
|
-
sanitizationWarnings,
|
|
117
|
+
sanitizationWarnings: sanitizationResult?.warnings || [],
|
|
179
118
|
isValid: htmlValidation.isValid && cssValidation.isValid,
|
|
180
119
|
isSecure,
|
|
181
120
|
summary: {
|
|
182
121
|
totalErrors,
|
|
183
122
|
totalWarnings,
|
|
184
123
|
totalInfo,
|
|
185
|
-
hasSecurityIssues
|
|
186
|
-
}
|
|
124
|
+
hasSecurityIssues
|
|
125
|
+
}
|
|
187
126
|
});
|
|
127
|
+
|
|
188
128
|
} catch (error) {
|
|
189
129
|
console.error('Validation error:', error);
|
|
190
|
-
setValidationState(
|
|
130
|
+
setValidationState(prev => ({
|
|
191
131
|
...prev,
|
|
192
132
|
isValidating: false,
|
|
193
133
|
htmlErrors: [{
|
|
@@ -197,13 +137,13 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
197
137
|
column: 1,
|
|
198
138
|
rule: 'validation-error',
|
|
199
139
|
severity: 'error',
|
|
200
|
-
source: 'validator'
|
|
140
|
+
source: 'validator'
|
|
201
141
|
}],
|
|
202
142
|
isValid: false,
|
|
203
143
|
summary: {
|
|
204
144
|
...prev.summary,
|
|
205
|
-
totalErrors: 1
|
|
206
|
-
}
|
|
145
|
+
totalErrors: 1
|
|
146
|
+
}
|
|
207
147
|
}));
|
|
208
148
|
}
|
|
209
149
|
}, [variant, enableSanitization, securityLevel, formatSanitizerMessage, formatValidatorMessage]);
|
|
@@ -268,8 +208,8 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
268
208
|
totalErrors: 0,
|
|
269
209
|
totalWarnings: 0,
|
|
270
210
|
totalInfo: 0,
|
|
271
|
-
hasSecurityIssues: false
|
|
272
|
-
}
|
|
211
|
+
hasSecurityIssues: false
|
|
212
|
+
}
|
|
273
213
|
});
|
|
274
214
|
}, []);
|
|
275
215
|
|
|
@@ -283,10 +223,10 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
283
223
|
...validationState.htmlInfo,
|
|
284
224
|
...validationState.cssErrors,
|
|
285
225
|
...validationState.cssWarnings,
|
|
286
|
-
...validationState.cssInfo
|
|
226
|
+
...validationState.cssInfo
|
|
287
227
|
];
|
|
288
228
|
|
|
289
|
-
return allErrors.filter(
|
|
229
|
+
return allErrors.filter(error => error.severity === severity);
|
|
290
230
|
}, [validationState]);
|
|
291
231
|
|
|
292
232
|
/**
|
|
@@ -299,116 +239,32 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
299
239
|
...validationState.htmlInfo,
|
|
300
240
|
...validationState.cssErrors,
|
|
301
241
|
...validationState.cssWarnings,
|
|
302
|
-
...validationState.cssInfo
|
|
242
|
+
...validationState.cssInfo
|
|
303
243
|
];
|
|
304
244
|
|
|
305
|
-
return allErrors.filter(
|
|
245
|
+
return allErrors.filter(error => error.source === source);
|
|
306
246
|
}, [validationState]);
|
|
307
247
|
|
|
308
|
-
/**
|
|
309
|
-
* Extract line number from error message if present
|
|
310
|
-
* API errors might contain line numbers in messages like "Error at line 5" or "Line 10: error"
|
|
311
|
-
* Also tries to find line number by searching for the problematic tag in content
|
|
312
|
-
*/
|
|
313
|
-
const extractLineNumberFromMessage = useCallback((message) => {
|
|
314
|
-
if (!message || typeof message !== 'string') {
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
// Try to match patterns like "line 5", "Line 10", "at line 15", "line: 20", etc.
|
|
318
|
-
const lineMatch = message.match(/\b(?:line|Line|LINE)\s*:?\s*(\d+)\b/i);
|
|
319
|
-
if (lineMatch && lineMatch[1]) {
|
|
320
|
-
return parseInt(lineMatch[1], 10);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Try to extract tag name from error message (e.g., "Unsupported tags: test" -> "test")
|
|
324
|
-
const tagMatch = message.match(/(?:tag|tags|Tag|Tags)[\s:]+([a-zA-Z_][a-zA-Z0-9_.]*)/i);
|
|
325
|
-
if (tagMatch && tagMatch[1] && content) {
|
|
326
|
-
const tagName = tagMatch[1];
|
|
327
|
-
const lineNumber = findLineNumberForTag(content, tagName);
|
|
328
|
-
if (lineNumber) {
|
|
329
|
-
return lineNumber;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return null;
|
|
334
|
-
}, [content]);
|
|
335
|
-
|
|
336
248
|
/**
|
|
337
249
|
* Get all errors and warnings combined
|
|
338
|
-
* Includes both client-side validation errors and API validation errors
|
|
339
250
|
*/
|
|
340
251
|
const getAllIssues = useCallback(() => {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
severity: VALIDATION_SEVERITY.ERROR,
|
|
351
|
-
source: ISSUE_SOURCES.LIQUID,
|
|
352
|
-
};
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
const apiStandardErrors = (apiValidationErrors?.standardErrors || []).map((errorMessage) => {
|
|
356
|
-
const extractedLine = extractLineNumberFromMessage(errorMessage);
|
|
357
|
-
return {
|
|
358
|
-
type: VALIDATION_SEVERITY.ERROR,
|
|
359
|
-
message: errorMessage,
|
|
360
|
-
line: extractedLine,
|
|
361
|
-
column: null,
|
|
362
|
-
rule: 'standard-api-validation',
|
|
363
|
-
severity: VALIDATION_SEVERITY.ERROR,
|
|
364
|
-
source: 'api-validator',
|
|
365
|
-
};
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// Security: protocol types are Rule Group #1 (blocking); others are warnings
|
|
369
|
-
// Use issue.position (from findUnsafeContent) to show real line/column when available
|
|
370
|
-
const PROTOCOL_TYPES = ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'];
|
|
371
|
-
const contentStr = typeof content === 'string' ? content : '';
|
|
372
|
-
const securityAsIssues = (validationState.securityIssues || []).map((issue) => {
|
|
373
|
-
const isBlocking = PROTOCOL_TYPES.includes(issue?.type);
|
|
374
|
-
const { line, column } = (issue?.position !== undefined && contentStr)
|
|
375
|
-
? getLineAndColumnFromPosition(contentStr, issue.position)
|
|
376
|
-
: { line: 1, column: 1 };
|
|
377
|
-
return {
|
|
378
|
-
type: isBlocking ? VALIDATION_SEVERITY.ERROR : VALIDATION_SEVERITY.WARNING,
|
|
252
|
+
return [
|
|
253
|
+
...validationState.htmlErrors,
|
|
254
|
+
...validationState.htmlWarnings,
|
|
255
|
+
...validationState.htmlInfo,
|
|
256
|
+
...validationState.cssErrors,
|
|
257
|
+
...validationState.cssWarnings,
|
|
258
|
+
...validationState.cssInfo,
|
|
259
|
+
...validationState.securityIssues.map(issue => ({
|
|
260
|
+
type: 'error',
|
|
379
261
|
message: `Security issue: ${issue.type}`,
|
|
380
|
-
line,
|
|
381
|
-
column,
|
|
382
|
-
rule:
|
|
383
|
-
severity:
|
|
384
|
-
source: 'security'
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// Sanitization warnings (Rule Group #1 entries have rule set by contentSanitizer)
|
|
389
|
-
const sanitizationAsIssues = (validationState.sanitizationWarnings || []).map((w) => {
|
|
390
|
-
const sev = BLOCKING_ERROR_RULE_IDS.includes(w.rule) ? VALIDATION_SEVERITY.ERROR : VALIDATION_SEVERITY.WARNING;
|
|
391
|
-
return {
|
|
392
|
-
...w,
|
|
393
|
-
severity: sev,
|
|
394
|
-
rule: w.rule || 'sanitizer.unknown',
|
|
395
|
-
line: w.line ?? 1,
|
|
396
|
-
column: w.column ?? 1,
|
|
397
|
-
source: w.source || 'sanitizer',
|
|
398
|
-
};
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
const allIssues = [
|
|
402
|
-
...(validationState.htmlErrors || []),
|
|
403
|
-
...(validationState.htmlWarnings || []),
|
|
404
|
-
...(validationState.htmlInfo || []),
|
|
405
|
-
...(validationState.cssErrors || []),
|
|
406
|
-
...(validationState.cssWarnings || []),
|
|
407
|
-
...(validationState.cssInfo || []),
|
|
408
|
-
...securityAsIssues,
|
|
409
|
-
...sanitizationAsIssues,
|
|
410
|
-
...apiLiquidErrors,
|
|
411
|
-
...apiStandardErrors,
|
|
262
|
+
line: 1,
|
|
263
|
+
column: 1,
|
|
264
|
+
rule: 'security-violation',
|
|
265
|
+
severity: 'error',
|
|
266
|
+
source: 'security'
|
|
267
|
+
}))
|
|
412
268
|
].sort((a, b) => {
|
|
413
269
|
// Sort by severity (error > warning > info) then by line number
|
|
414
270
|
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
@@ -417,22 +273,16 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
417
273
|
}
|
|
418
274
|
return (a.line || 0) - (b.line || 0);
|
|
419
275
|
});
|
|
420
|
-
|
|
421
|
-
// Ensure we always return an array
|
|
422
|
-
return Array.isArray(allIssues) ? allIssues : [];
|
|
423
|
-
}, [validationState, apiValidationErrors, extractLineNumberFromMessage, content]);
|
|
276
|
+
}, [validationState]);
|
|
424
277
|
|
|
425
278
|
/**
|
|
426
279
|
* Check if validation is clean (no errors or warnings)
|
|
427
|
-
* Includes API validation errors in the check
|
|
428
280
|
*/
|
|
429
281
|
const isClean = useCallback(() => {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
&& !hasApiErrors;
|
|
435
|
-
}, [validationState.summary, apiValidationErrors]);
|
|
282
|
+
return validationState.summary.totalErrors === 0 &&
|
|
283
|
+
validationState.summary.totalWarnings === 0 &&
|
|
284
|
+
!validationState.summary.hasSecurityIssues;
|
|
285
|
+
}, [validationState.summary]);
|
|
436
286
|
|
|
437
287
|
// Effect to validate content when it changes
|
|
438
288
|
useEffect(() => {
|
|
@@ -442,19 +292,14 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
442
292
|
}, [content, validateContent, enableRealTime]);
|
|
443
293
|
|
|
444
294
|
// Cleanup on unmount
|
|
445
|
-
useEffect(() =>
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
return () => {
|
|
297
|
+
if (debounceRef.current) {
|
|
298
|
+
clearTimeout(debounceRef.current);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
449
301
|
}, []);
|
|
450
302
|
|
|
451
|
-
const hasApiErrors = (apiValidationErrors?.liquidErrors?.length || 0) + (apiValidationErrors?.standardErrors?.length || 0) > 0;
|
|
452
|
-
|
|
453
|
-
const protocolTypes = ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'];
|
|
454
|
-
// Client-side Liquid validation errors are blocking (genuine syntax errors)
|
|
455
|
-
const hasClientSideLiquidErrors = (validationState.htmlErrors || []).some((e) => e.source === ISSUE_SOURCES.LIQUID && e.severity === VALIDATION_SEVERITY.ERROR);
|
|
456
|
-
const hasBlockingErrors = (validationState.sanitizationWarnings || []).some((w) => BLOCKING_ERROR_RULE_IDS.includes(w.rule)) || (validationState.securityIssues || []).some((s) => protocolTypes.includes(s?.type)) || hasApiErrors || hasClientSideLiquidErrors;
|
|
457
|
-
|
|
458
303
|
return {
|
|
459
304
|
// Validation state
|
|
460
305
|
...validationState,
|
|
@@ -471,12 +316,10 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
|
|
|
471
316
|
isClean,
|
|
472
317
|
|
|
473
318
|
// Computed properties
|
|
474
|
-
hasErrors: validationState.summary.totalErrors > 0
|
|
319
|
+
hasErrors: validationState.summary.totalErrors > 0,
|
|
475
320
|
hasWarnings: validationState.summary.totalWarnings > 0,
|
|
476
|
-
hasSecurityIssues: validationState.summary.hasSecurityIssues
|
|
477
|
-
/** True only when Rule Group #1 (Input & Sanitization) issues exist. Use for UI gating. */
|
|
478
|
-
hasBlockingErrors,
|
|
321
|
+
hasSecurityIssues: validationState.summary.hasSecurityIssues
|
|
479
322
|
};
|
|
480
323
|
};
|
|
481
324
|
|
|
482
|
-
export default useValidation;
|
|
325
|
+
export default useValidation;
|