@capillarytech/creatives-library 8.0.259 → 8.0.260-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +1 -2
  4. package/initialReducer.js +0 -2
  5. package/package.json +1 -1
  6. package/services/api.js +0 -10
  7. package/services/tests/api.test.js +0 -34
  8. package/translations/en.json +3 -4
  9. package/utils/common.js +0 -12
  10. package/utils/commonUtils.js +5 -28
  11. package/utils/tests/commonUtil.test.js +0 -224
  12. package/utils/transformTemplateConfig.js +10 -0
  13. package/v2Components/CapDeviceContent/index.js +56 -61
  14. package/v2Components/CapTagList/index.js +1 -6
  15. package/v2Components/CapTagListWithInput/index.js +1 -5
  16. package/v2Components/CapTagListWithInput/messages.js +1 -1
  17. package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
  18. package/v2Components/ErrorInfoNote/index.js +72 -457
  19. package/v2Components/ErrorInfoNote/messages.js +6 -36
  20. package/v2Components/ErrorInfoNote/style.scss +6 -282
  21. package/v2Components/FormBuilder/index.js +4 -4
  22. package/v2Components/FormBuilder/tests/index.test.js +4 -13
  23. package/v2Components/HtmlEditor/HTMLEditor.js +94 -547
  24. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1441
  25. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
  26. package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
  27. package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
  28. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
  29. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
  30. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
  31. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  32. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +0 -9
  33. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +4 -4
  34. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  39. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
  40. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +6 -3
  41. package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
  42. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  43. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
  44. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -1
  45. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -49
  46. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -50
  47. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +41 -70
  48. package/v2Components/HtmlEditor/constants.js +20 -42
  49. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
  50. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -120
  51. package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
  52. package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
  53. package/v2Components/HtmlEditor/hooks/useValidation.js +53 -189
  54. package/v2Components/HtmlEditor/index.js +1 -1
  55. package/v2Components/HtmlEditor/messages.js +94 -92
  56. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -94
  57. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
  58. package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
  59. package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
  60. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +102 -134
  61. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
  62. package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
  63. package/v2Components/MobilePushPreviewV2/index.js +7 -32
  64. package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
  65. package/v2Components/TemplatePreview/index.js +32 -47
  66. package/v2Components/TemplatePreview/messages.js +0 -4
  67. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
  68. package/v2Containers/BeeEditor/index.js +90 -172
  69. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +3 -4
  70. package/v2Containers/CreativesContainer/SlideBoxContent.js +52 -128
  71. package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
  72. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -2
  73. package/v2Containers/CreativesContainer/constants.js +0 -1
  74. package/v2Containers/CreativesContainer/index.js +46 -240
  75. package/v2Containers/CreativesContainer/messages.js +0 -8
  76. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
  77. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -38
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -106
  79. package/v2Containers/Email/actions.js +0 -7
  80. package/v2Containers/Email/constants.js +1 -5
  81. package/v2Containers/Email/index.js +30 -239
  82. package/v2Containers/Email/messages.js +0 -32
  83. package/v2Containers/Email/reducer.js +1 -12
  84. package/v2Containers/Email/sagas.js +7 -61
  85. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
  86. package/v2Containers/Email/tests/reducer.test.js +0 -46
  87. package/v2Containers/Email/tests/sagas.test.js +29 -320
  88. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
  89. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
  90. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
  91. package/v2Containers/EmailWrapper/constants.js +0 -2
  92. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
  93. package/v2Containers/EmailWrapper/index.js +23 -103
  94. package/v2Containers/EmailWrapper/messages.js +1 -65
  95. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
  96. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
  97. package/v2Containers/InApp/actions.js +0 -7
  98. package/v2Containers/InApp/constants.js +4 -20
  99. package/v2Containers/InApp/index.js +360 -804
  100. package/v2Containers/InApp/index.scss +3 -4
  101. package/v2Containers/InApp/messages.js +3 -7
  102. package/v2Containers/InApp/reducer.js +3 -21
  103. package/v2Containers/InApp/sagas.js +9 -29
  104. package/v2Containers/InApp/selectors.js +5 -25
  105. package/v2Containers/InApp/tests/index.test.js +71 -152
  106. package/v2Containers/InApp/tests/reducer.test.js +0 -34
  107. package/v2Containers/InApp/tests/sagas.test.js +9 -61
  108. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +12 -39
  109. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +6 -10
  110. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +75 -102
  111. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +54 -81
  112. package/v2Containers/MobilePushNew/index.js +2 -3
  113. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +178 -262
  114. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +12 -16
  115. package/v2Containers/SmsTrai/Edit/index.js +1 -2
  116. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +111 -468
  117. package/v2Containers/TagList/index.js +19 -62
  118. package/v2Containers/Templates/_templates.scss +1 -60
  119. package/v2Containers/Templates/index.js +4 -89
  120. package/v2Containers/Templates/messages.js +0 -4
  121. package/v2Containers/WebPush/Create/messages.js +8 -0
  122. package/v2Containers/WebPush/Create/preview/PreviewControls.js +2 -2
  123. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +3 -1
  124. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +5 -1
  125. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +5 -1
  126. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +5 -1
  127. package/v2Containers/WebPush/Create/preview/preview.scss +7 -0
  128. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +734 -1306
  129. package/v2Components/ErrorInfoNote/constants.js +0 -1
  130. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -874
  131. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
  132. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -255
  133. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -364
  134. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
  135. package/v2Components/HtmlEditor/utils/validationConstants.js +0 -40
  136. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
  137. package/v2Containers/BeePopupEditor/constants.js +0 -10
  138. package/v2Containers/BeePopupEditor/index.js +0 -194
  139. package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
  140. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1285
  141. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -1880
  142. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
  143. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -643
  144. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
  145. package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
  146. package/v2Containers/InApp/tests/selectors.test.js +0 -612
  147. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
  148. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
  149. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
  150. package/v2Containers/InAppWrapper/constants.js +0 -16
  151. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
  152. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
  153. package/v2Containers/InAppWrapper/index.js +0 -148
  154. package/v2Containers/InAppWrapper/messages.js +0 -49
  155. package/v2Containers/InappAdvance/index.js +0 -1099
  156. package/v2Containers/InappAdvance/index.scss +0 -10
  157. package/v2Containers/InappAdvance/tests/index.test.js +0 -448
@@ -1,16 +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 } from '../constants';
14
9
 
15
10
  /**
16
11
  * Custom hook for managing HTML/CSS validation
@@ -21,46 +16,12 @@ import { BLOCKING_ERROR_RULE_IDS } from '../constants';
21
16
  * @param {Function} formatValidatorMessage - Message formatter function for validator internationalization
22
17
  * @returns {Object} Validation state and methods
23
18
  */
24
- /**
25
- * Get line number for a character position in text
26
- */
27
- const getLineNumberFromPosition = (text, position) => {
28
- if (position === undefined || position < 0) return 1;
29
- return text.substring(0, position).split('\n').length;
30
- };
31
-
32
- /**
33
- * Find line number for a tag or pattern in content
34
- * Used for API errors to locate where the error occurs
35
- */
36
- const findLineNumberForTag = (content, tagName) => {
37
- if (!content || !tagName) return null;
38
-
39
- // Try to find the tag in the content
40
- // Look for patterns like {{ tagName }}, {{tagName}}, etc.
41
- const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
42
- const patterns = [
43
- new RegExp(`\\{\\{\\s*${escapedTagName}\\s*\\}\\}`, 'g'),
44
- new RegExp(`\\{%[^%]*${escapedTagName}[^%]*%\\}`, 'g'),
45
- ];
46
-
47
- for (const pattern of patterns) {
48
- const match = pattern.exec(content);
49
- if (match) {
50
- return getLineNumberFromPosition(content, match.index);
51
- }
52
- }
53
-
54
- return null;
55
- };
56
-
57
19
  export const useValidation = (content, variant = 'email', options = {}, formatSanitizerMessage = null, formatValidatorMessage = null) => {
58
20
  const {
59
21
  enableRealTime = true,
60
22
  debounceMs = 500,
61
23
  enableSanitization = true,
62
- securityLevel = 'standard',
63
- apiValidationErrors = null, // API validation errors from validateLiquidTemplateContent
24
+ securityLevel = 'standard'
64
25
  } = options;
65
26
 
66
27
  // Validation state
@@ -81,8 +42,8 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
81
42
  totalErrors: 0,
82
43
  totalWarnings: 0,
83
44
  totalInfo: 0,
84
- hasSecurityIssues: false,
85
- },
45
+ hasSecurityIssues: false
46
+ }
86
47
  });
87
48
 
88
49
  // Refs for debouncing
@@ -94,7 +55,7 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
94
55
  */
95
56
  const performValidation = useCallback(async (htmlContent) => {
96
57
  if (!htmlContent) {
97
- setValidationState((prev) => ({
58
+ setValidationState(prev => ({
98
59
  ...prev,
99
60
  isValidating: false,
100
61
  htmlErrors: [],
@@ -111,13 +72,13 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
111
72
  totalErrors: 0,
112
73
  totalWarnings: 0,
113
74
  totalInfo: 0,
114
- hasSecurityIssues: false,
115
- },
75
+ hasSecurityIssues: false
76
+ }
116
77
  }));
117
78
  return;
118
79
  }
119
80
 
120
- setValidationState((prev) => ({ ...prev, isValidating: true }));
81
+ setValidationState(prev => ({ ...prev, isValidating: true }));
121
82
 
122
83
  try {
123
84
  // 1. HTML Validation
@@ -130,21 +91,15 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
130
91
  const isSecure = isContentSafe(htmlContent);
131
92
  const securityIssues = isSecure ? [] : findUnsafeContent(htmlContent);
132
93
 
133
- // 4. Sanitization (if enabled) – Rule Group #1 issues come from here
94
+ // 4. Sanitization (if enabled)
134
95
  let sanitizationResult = null;
135
96
  if (enableSanitization) {
136
97
  sanitizationResult = sanitizeHTML(htmlContent, variant, securityLevel, formatSanitizerMessage);
137
98
  }
138
99
 
139
- const sanitizationWarnings = sanitizationResult?.warnings || [];
140
- const blockingSanitizerCount = sanitizationWarnings.filter((w) => BLOCKING_ERROR_RULE_IDS.includes(w.rule)).length;
141
- const protocolSecurityCount = (securityIssues || []).filter((s) => ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'].includes(s?.type)).length;
142
-
143
- // Summary: totalErrors/totalWarnings are for display; blocking count is for gating
144
- const totalErrors = htmlValidation.errors.length + cssValidation.errors.length + blockingSanitizerCount + protocolSecurityCount;
145
- const totalWarnings = htmlValidation.warnings.length + cssValidation.warnings.length
146
- + (sanitizationWarnings.length - blockingSanitizerCount)
147
- + (securityIssues.length - protocolSecurityCount);
100
+ // Calculate summary
101
+ const totalErrors = htmlValidation.errors.length + cssValidation.errors.length;
102
+ const totalWarnings = htmlValidation.warnings.length + cssValidation.warnings.length;
148
103
  const totalInfo = htmlValidation.info.length + cssValidation.info.length;
149
104
  const hasSecurityIssues = securityIssues.length > 0;
150
105
 
@@ -159,19 +114,20 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
159
114
  cssWarnings: cssValidation.warnings,
160
115
  cssInfo: cssValidation.info,
161
116
  securityIssues,
162
- sanitizationWarnings,
117
+ sanitizationWarnings: sanitizationResult?.warnings || [],
163
118
  isValid: htmlValidation.isValid && cssValidation.isValid,
164
119
  isSecure,
165
120
  summary: {
166
121
  totalErrors,
167
122
  totalWarnings,
168
123
  totalInfo,
169
- hasSecurityIssues,
170
- },
124
+ hasSecurityIssues
125
+ }
171
126
  });
127
+
172
128
  } catch (error) {
173
129
  console.error('Validation error:', error);
174
- setValidationState((prev) => ({
130
+ setValidationState(prev => ({
175
131
  ...prev,
176
132
  isValidating: false,
177
133
  htmlErrors: [{
@@ -181,13 +137,13 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
181
137
  column: 1,
182
138
  rule: 'validation-error',
183
139
  severity: 'error',
184
- source: 'validator',
140
+ source: 'validator'
185
141
  }],
186
142
  isValid: false,
187
143
  summary: {
188
144
  ...prev.summary,
189
- totalErrors: 1,
190
- },
145
+ totalErrors: 1
146
+ }
191
147
  }));
192
148
  }
193
149
  }, [variant, enableSanitization, securityLevel, formatSanitizerMessage, formatValidatorMessage]);
@@ -252,8 +208,8 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
252
208
  totalErrors: 0,
253
209
  totalWarnings: 0,
254
210
  totalInfo: 0,
255
- hasSecurityIssues: false,
256
- },
211
+ hasSecurityIssues: false
212
+ }
257
213
  });
258
214
  }, []);
259
215
 
@@ -267,10 +223,10 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
267
223
  ...validationState.htmlInfo,
268
224
  ...validationState.cssErrors,
269
225
  ...validationState.cssWarnings,
270
- ...validationState.cssInfo,
226
+ ...validationState.cssInfo
271
227
  ];
272
228
 
273
- return allErrors.filter((error) => error.severity === severity);
229
+ return allErrors.filter(error => error.severity === severity);
274
230
  }, [validationState]);
275
231
 
276
232
  /**
@@ -283,111 +239,32 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
283
239
  ...validationState.htmlInfo,
284
240
  ...validationState.cssErrors,
285
241
  ...validationState.cssWarnings,
286
- ...validationState.cssInfo,
242
+ ...validationState.cssInfo
287
243
  ];
288
244
 
289
- return allErrors.filter((error) => error.source === source);
245
+ return allErrors.filter(error => error.source === source);
290
246
  }, [validationState]);
291
247
 
292
- /**
293
- * Extract line number from error message if present
294
- * API errors might contain line numbers in messages like "Error at line 5" or "Line 10: error"
295
- * Also tries to find line number by searching for the problematic tag in content
296
- */
297
- const extractLineNumberFromMessage = useCallback((message) => {
298
- if (!message || typeof message !== 'string') {
299
- return null;
300
- }
301
- // Try to match patterns like "line 5", "Line 10", "at line 15", "line: 20", etc.
302
- const lineMatch = message.match(/\b(?:line|Line|LINE)\s*:?\s*(\d+)\b/i);
303
- if (lineMatch && lineMatch[1]) {
304
- return parseInt(lineMatch[1], 10);
305
- }
306
-
307
- // Try to extract tag name from error message (e.g., "Unsupported tags: test" -> "test")
308
- const tagMatch = message.match(/(?:tag|tags|Tag|Tags)[\s:]+([a-zA-Z_][a-zA-Z0-9_.]*)/i);
309
- if (tagMatch && tagMatch[1] && content) {
310
- const tagName = tagMatch[1];
311
- const lineNumber = findLineNumberForTag(content, tagName);
312
- if (lineNumber) {
313
- return lineNumber;
314
- }
315
- }
316
-
317
- return null;
318
- }, [content]);
319
-
320
248
  /**
321
249
  * Get all errors and warnings combined
322
- * Includes both client-side validation errors and API validation errors
323
250
  */
324
251
  const getAllIssues = useCallback(() => {
325
- // API errors (liquid + standard) are blocking – they block Save/Update/Preview/Test
326
- const apiLiquidErrors = (apiValidationErrors?.liquidErrors || []).map((errorMessage) => {
327
- const extractedLine = extractLineNumberFromMessage(errorMessage);
328
- return {
329
- type: 'error',
330
- message: errorMessage,
331
- line: extractedLine,
332
- column: null,
333
- rule: 'liquid-api-validation',
334
- severity: 'error',
335
- source: 'liquid-validator',
336
- };
337
- });
338
-
339
- const apiStandardErrors = (apiValidationErrors?.standardErrors || []).map((errorMessage) => {
340
- const extractedLine = extractLineNumberFromMessage(errorMessage);
341
- return {
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 => ({
342
260
  type: 'error',
343
- message: errorMessage,
344
- line: extractedLine,
345
- column: null,
346
- rule: 'standard-api-validation',
347
- severity: 'error',
348
- source: 'api-validator',
349
- };
350
- });
351
-
352
- // Security: protocol types are Rule Group #1 (blocking); others are warnings
353
- const PROTOCOL_TYPES = ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'];
354
- const securityAsIssues = (validationState.securityIssues || []).map((issue) => {
355
- const isBlocking = PROTOCOL_TYPES.includes(issue?.type);
356
- return {
357
- type: isBlocking ? 'error' : 'warning',
358
261
  message: `Security issue: ${issue.type}`,
359
262
  line: 1,
360
263
  column: 1,
361
- rule: isBlocking ? 'sanitizer.dangerousProtocolDetected' : 'security-violation',
362
- severity: isBlocking ? 'error' : 'warning',
363
- source: 'security',
364
- };
365
- });
366
-
367
- // Sanitization warnings (Rule Group #1 entries have rule set by contentSanitizer)
368
- const sanitizationAsIssues = (validationState.sanitizationWarnings || []).map((w) => {
369
- const sev = BLOCKING_ERROR_RULE_IDS.includes(w.rule) ? 'error' : 'warning';
370
- return {
371
- ...w,
372
- severity: sev,
373
- rule: w.rule || 'sanitizer.unknown',
374
- line: w.line ?? 1,
375
- column: w.column ?? 1,
376
- source: w.source || 'sanitizer',
377
- };
378
- });
379
-
380
- const allIssues = [
381
- ...(validationState.htmlErrors || []),
382
- ...(validationState.htmlWarnings || []),
383
- ...(validationState.htmlInfo || []),
384
- ...(validationState.cssErrors || []),
385
- ...(validationState.cssWarnings || []),
386
- ...(validationState.cssInfo || []),
387
- ...securityAsIssues,
388
- ...sanitizationAsIssues,
389
- ...apiLiquidErrors,
390
- ...apiStandardErrors,
264
+ rule: 'security-violation',
265
+ severity: 'error',
266
+ source: 'security'
267
+ }))
391
268
  ].sort((a, b) => {
392
269
  // Sort by severity (error > warning > info) then by line number
393
270
  const severityOrder = { error: 0, warning: 1, info: 2 };
@@ -396,22 +273,16 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
396
273
  }
397
274
  return (a.line || 0) - (b.line || 0);
398
275
  });
399
-
400
- // Ensure we always return an array
401
- return Array.isArray(allIssues) ? allIssues : [];
402
- }, [validationState, apiValidationErrors, extractLineNumberFromMessage]);
276
+ }, [validationState]);
403
277
 
404
278
  /**
405
279
  * Check if validation is clean (no errors or warnings)
406
- * Includes API validation errors in the check
407
280
  */
408
281
  const isClean = useCallback(() => {
409
- const hasApiErrors = (apiValidationErrors?.liquidErrors?.length || 0) + (apiValidationErrors?.standardErrors?.length || 0) > 0;
410
- return validationState.summary.totalErrors === 0
411
- && validationState.summary.totalWarnings === 0
412
- && !validationState.summary.hasSecurityIssues
413
- && !hasApiErrors;
414
- }, [validationState.summary, apiValidationErrors]);
282
+ return validationState.summary.totalErrors === 0 &&
283
+ validationState.summary.totalWarnings === 0 &&
284
+ !validationState.summary.hasSecurityIssues;
285
+ }, [validationState.summary]);
415
286
 
416
287
  // Effect to validate content when it changes
417
288
  useEffect(() => {
@@ -421,19 +292,14 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
421
292
  }, [content, validateContent, enableRealTime]);
422
293
 
423
294
  // Cleanup on unmount
424
- useEffect(() => () => {
425
- if (debounceRef.current) {
426
- clearTimeout(debounceRef.current);
427
- }
295
+ useEffect(() => {
296
+ return () => {
297
+ if (debounceRef.current) {
298
+ clearTimeout(debounceRef.current);
299
+ }
300
+ };
428
301
  }, []);
429
302
 
430
- const hasApiErrors = (apiValidationErrors?.liquidErrors?.length || 0) + (apiValidationErrors?.standardErrors?.length || 0) > 0;
431
-
432
- const protocolTypes = ['JavaScript Protocol', 'Data URL', 'VBScript Protocol'];
433
- // Client-side Liquid validation errors are blocking (genuine syntax errors)
434
- const hasClientSideLiquidErrors = (validationState.htmlErrors || []).some((e) => e.source === 'liquid-validator' && e.severity === 'error');
435
- const hasBlockingErrors = (validationState.sanitizationWarnings || []).some((w) => BLOCKING_ERROR_RULE_IDS.includes(w.rule)) || (validationState.securityIssues || []).some((s) => protocolTypes.includes(s?.type)) || hasApiErrors || hasClientSideLiquidErrors;
436
-
437
303
  return {
438
304
  // Validation state
439
305
  ...validationState,
@@ -450,12 +316,10 @@ export const useValidation = (content, variant = 'email', options = {}, formatSa
450
316
  isClean,
451
317
 
452
318
  // Computed properties
453
- hasErrors: validationState.summary.totalErrors > 0 || hasApiErrors,
319
+ hasErrors: validationState.summary.totalErrors > 0,
454
320
  hasWarnings: validationState.summary.totalWarnings > 0,
455
- hasSecurityIssues: validationState.summary.hasSecurityIssues,
456
- /** True only when Rule Group #1 (Input & Sanitization) issues exist. Use for UI gating. */
457
- hasBlockingErrors,
321
+ hasSecurityIssues: validationState.summary.hasSecurityIssues
458
322
  };
459
323
  };
460
324
 
461
- export default useValidation;
325
+ export default useValidation;
@@ -26,4 +26,4 @@ export { HTML_EDITOR_VARIANTS, DEVICE_TYPES } from './constants';
26
26
  * - Default export (lazy): ~0KB initial bundle (loads ~400KB when needed)
27
27
  * - HTMLEditorSync: ~400KB added to initial bundle
28
28
  * - Constants: ~1KB added to initial bundle
29
- */
29
+ */