@capillarytech/creatives-library 8.0.249 → 8.0.250-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 (136) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/services/api.js +10 -0
  7. package/services/tests/api.test.js +18 -0
  8. package/utils/common.js +5 -0
  9. package/utils/commonUtils.js +28 -5
  10. package/utils/tests/commonUtil.test.js +224 -0
  11. package/utils/transformTemplateConfig.js +0 -10
  12. package/v2Components/CapDeviceContent/index.js +61 -56
  13. package/v2Components/CapTagList/index.js +6 -1
  14. package/v2Components/CapTagListWithInput/index.js +5 -1
  15. package/v2Components/CapTagListWithInput/messages.js +1 -1
  16. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  17. package/v2Components/ErrorInfoNote/index.js +452 -72
  18. package/v2Components/ErrorInfoNote/messages.js +22 -0
  19. package/v2Components/ErrorInfoNote/style.scss +280 -4
  20. package/v2Components/FormBuilder/tests/index.test.js +13 -4
  21. package/v2Components/HtmlEditor/HTMLEditor.js +640 -94
  22. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +874 -0
  23. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1167 -133
  24. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +27 -16
  25. package/v2Components/HtmlEditor/_htmlEditor.scss +108 -45
  26. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  27. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  28. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  29. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  30. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  31. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  32. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  33. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  34. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  35. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  36. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  37. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  38. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  39. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  40. package/v2Components/HtmlEditor/components/PreviewPane/index.js +11 -13
  41. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  42. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  43. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +68 -39
  44. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  45. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +391 -0
  46. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  47. package/v2Components/HtmlEditor/constants.js +42 -20
  48. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  49. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.apiErrors.test.js +795 -0
  50. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  51. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  52. package/v2Components/HtmlEditor/hooks/useValidation.js +189 -53
  53. package/v2Components/HtmlEditor/index.js +1 -1
  54. package/v2Components/HtmlEditor/messages.js +95 -85
  55. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +94 -45
  56. package/v2Components/HtmlEditor/utils/contentSanitizer.js +40 -41
  57. package/v2Components/HtmlEditor/utils/htmlValidator.js +71 -72
  58. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +134 -102
  59. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  60. package/v2Components/HtmlEditor/utils/validationAdapter.js +66 -41
  61. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  62. package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
  63. package/v2Components/TemplatePreview/index.js +47 -32
  64. package/v2Components/TemplatePreview/messages.js +4 -0
  65. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +1 -0
  66. package/v2Containers/BeeEditor/index.js +172 -90
  67. package/v2Containers/BeePopupEditor/constants.js +10 -0
  68. package/v2Containers/BeePopupEditor/index.js +193 -0
  69. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  70. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -51
  71. package/v2Containers/CreativesContainer/SlideBoxFooter.js +163 -13
  72. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
  73. package/v2Containers/CreativesContainer/constants.js +1 -0
  74. package/v2Containers/CreativesContainer/index.js +239 -46
  75. package/v2Containers/CreativesContainer/messages.js +8 -0
  76. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +11 -2
  77. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +106 -0
  79. package/v2Containers/Email/actions.js +7 -0
  80. package/v2Containers/Email/constants.js +5 -1
  81. package/v2Containers/Email/index.js +222 -27
  82. package/v2Containers/Email/messages.js +32 -0
  83. package/v2Containers/Email/reducer.js +12 -1
  84. package/v2Containers/Email/sagas.js +61 -7
  85. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  86. package/v2Containers/Email/tests/sagas.test.js +320 -29
  87. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1321 -0
  88. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +210 -15
  89. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  90. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +1749 -0
  91. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +520 -0
  92. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  93. package/v2Containers/EmailWrapper/constants.js +2 -0
  94. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +629 -77
  95. package/v2Containers/EmailWrapper/index.js +103 -23
  96. package/v2Containers/EmailWrapper/messages.js +61 -1
  97. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +643 -0
  98. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +594 -77
  99. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  100. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  101. package/v2Containers/InApp/actions.js +7 -0
  102. package/v2Containers/InApp/constants.js +20 -4
  103. package/v2Containers/InApp/index.js +802 -359
  104. package/v2Containers/InApp/index.scss +4 -3
  105. package/v2Containers/InApp/messages.js +7 -3
  106. package/v2Containers/InApp/reducer.js +21 -3
  107. package/v2Containers/InApp/sagas.js +29 -9
  108. package/v2Containers/InApp/selectors.js +25 -5
  109. package/v2Containers/InApp/tests/index.test.js +154 -50
  110. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  111. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  112. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  113. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  114. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  115. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  116. package/v2Containers/InAppWrapper/constants.js +16 -0
  117. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  118. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  119. package/v2Containers/InAppWrapper/index.js +148 -0
  120. package/v2Containers/InAppWrapper/messages.js +49 -0
  121. package/v2Containers/InappAdvance/index.js +1099 -0
  122. package/v2Containers/InappAdvance/index.scss +10 -0
  123. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  124. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
  125. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
  126. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
  127. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
  128. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
  129. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
  130. package/v2Containers/TagList/index.js +62 -19
  131. package/v2Containers/Templates/_templates.scss +60 -1
  132. package/v2Containers/Templates/index.js +89 -4
  133. package/v2Containers/Templates/messages.js +4 -0
  134. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -0
  135. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +0 -152
  136. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
@@ -20,7 +20,7 @@ const defaultMessageFormatter = (messageKey, values = {}) => {
20
20
  'validator.largeImageDetected': 'Large image dimensions detected - consider mobile optimization',
21
21
  'validator.unclosedCssRule': 'Unclosed CSS rule detected',
22
22
  'validator.emptyCssRule': 'Empty CSS rule detected',
23
- 'validator.cssValidationFailed': `CSS validation failed: ${values.error || 'Unknown error'}`
23
+ 'validator.cssValidationFailed': `CSS validation failed: ${values.error || 'Unknown error'}`,
24
24
  };
25
25
 
26
26
  return fallbackMessages[messageKey] || messageKey;
@@ -49,7 +49,7 @@ const HTML_RULES = {
49
49
  'space-tab-mixed-disabled': 'space',
50
50
  'id-class-ad-disabled': false,
51
51
  'href-abs-or-rel': false,
52
- 'attr-unsafe-chars': true
52
+ 'attr-unsafe-chars': true,
53
53
  };
54
54
 
55
55
  // Additional custom validation rules
@@ -66,7 +66,7 @@ const CUSTOM_VALIDATIONS = {
66
66
 
67
67
  // InApp-specific validations
68
68
  MOBILE_INCOMPATIBLE: /<(object|embed|applet)\b/gi,
69
- LARGE_IMAGES: /width\s*:\s*[5-9]\d{2,}px|height\s*:\s*[5-9]\d{2,}px/gi
69
+ LARGE_IMAGES: /width\s*:\s*[5-9]\d{2,}px|height\s*:\s*[5-9]\d{2,}px/gi,
70
70
  };
71
71
 
72
72
  /**
@@ -82,7 +82,7 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
82
82
  isValid: true,
83
83
  errors: [],
84
84
  warnings: [],
85
- info: []
85
+ info: [],
86
86
  };
87
87
  }
88
88
 
@@ -90,7 +90,7 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
90
90
  isValid: true,
91
91
  errors: [],
92
92
  warnings: [],
93
- info: []
93
+ info: [],
94
94
  };
95
95
 
96
96
  try {
@@ -98,7 +98,7 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
98
98
  const htmlHintResults = HTMLHint.verify(html, HTML_RULES);
99
99
 
100
100
  // Process HTMLHint results
101
- htmlHintResults.forEach(issue => {
101
+ htmlHintResults.forEach((issue) => {
102
102
  const error = {
103
103
  type: issue.type,
104
104
  message: issue.message,
@@ -106,66 +106,64 @@ export const validateHTML = (html, variant = 'email', formatMessage = defaultMes
106
106
  column: issue.col,
107
107
  rule: issue.rule.id,
108
108
  severity: getSeverityLevel(issue.type, issue.rule.id),
109
- source: 'htmlhint'
109
+ source: 'htmlhint',
110
110
  };
111
111
 
112
- if (error.severity === 'error') {
113
- results.errors.push(error);
114
- results.isValid = false;
115
- } else if (error.severity === 'warning') {
112
+ if (error.severity === 'warning') {
116
113
  results.warnings.push(error);
117
- } else {
114
+ } else if (error.severity === 'info') {
118
115
  results.info.push(error);
116
+ } else {
117
+ results.warnings.push(error);
119
118
  }
120
119
  });
121
-
122
- // Run custom validations
123
- runCustomValidations(html, variant, results, formatMessage);
124
-
125
- // Run Liquid template validation
126
- runLiquidValidation(html, variant, results, formatMessage);
127
-
128
120
  } catch (error) {
129
- results.errors.push({
130
- type: 'error',
121
+ // HTMLHint failed, but we still want to run custom validations and Liquid validation
122
+ results.warnings.push({
123
+ type: 'warning',
131
124
  message: formatMessage('validator.validationFailed', { error: error.message }),
132
125
  line: 1,
133
126
  column: 1,
134
127
  rule: 'validation-error',
135
- severity: 'error',
136
- source: 'validator'
128
+ severity: 'warning',
129
+ source: 'validator',
137
130
  });
138
- results.isValid = false;
139
131
  }
140
132
 
133
+ // Always run custom validations and Liquid validation, even if HTMLHint failed
134
+ // This ensures unsafe protocol detection and other critical validations still run
135
+ runCustomValidations(html, variant, results, formatMessage);
136
+ runLiquidValidation(html, variant, results, formatMessage);
137
+
141
138
  return results;
142
139
  };
143
140
 
144
141
  /**
145
- * Determines severity level based on error type and rule
142
+ * Determines severity level based on error type and rule.
143
+ * ONLY Rule Group #1 (Input & Sanitization) is blocking; that is handled in
144
+ * contentSanitizer/useValidation. All HTML/CSS/Liquid/security rules here are
145
+ * WARNING only for backward compatibility with CKEditor legacy templates.
146
146
  */
147
147
  const getSeverityLevel = (type, ruleId) => {
148
- const errorRules = [
148
+ const warningRules = [
149
149
  'tag-pair',
150
150
  'attr-no-duplication',
151
151
  'id-unique',
152
- 'spec-char-escape'
153
- ];
154
-
155
- const warningRules = [
152
+ 'spec-char-escape',
156
153
  'tagname-lowercase',
157
154
  'attr-lowercase',
158
155
  'attr-value-double-quotes',
159
- 'alt-require'
156
+ 'alt-require',
160
157
  ];
161
158
 
162
- if (type === 'error' || errorRules.includes(ruleId)) {
163
- return 'error';
164
- } else if (warningRules.includes(ruleId)) {
159
+ if (warningRules.includes(ruleId)) {
160
+ return 'warning';
161
+ }
162
+ // Downgrade HTMLHint "error" type to warning (Rule Group #1 is sanitizer-only)
163
+ if (type === 'error') {
165
164
  return 'warning';
166
- } else {
167
- return 'info';
168
165
  }
166
+ return 'info';
169
167
  };
170
168
 
171
169
  /**
@@ -177,6 +175,7 @@ const getSeverityLevel = (type, ruleId) => {
177
175
  */
178
176
  const runCustomValidations = (html, variant, results, formatMessage = defaultMessageFormatter) => {
179
177
  // Check for unsafe protocols using RegExp.exec loop
178
+ // These are BLOCKING ERRORS (Rule Group #1: sanitizer.dangerousProtocolDetected)
180
179
  const unsafeProtocolsRegex = new RegExp(CUSTOM_VALIDATIONS.UNSAFE_PROTOCOLS.source, CUSTOM_VALIDATIONS.UNSAFE_PROTOCOLS.flags);
181
180
  unsafeProtocolsRegex.lastIndex = 0; // Reset lastIndex before running
182
181
  let match;
@@ -186,9 +185,9 @@ const runCustomValidations = (html, variant, results, formatMessage = defaultMes
186
185
  message: formatMessage('validator.unsafeProtocolDetected', { protocol: match[0] }),
187
186
  line: getLineNumber(html, match.index),
188
187
  column: 1,
189
- rule: 'unsafe-protocol',
188
+ rule: 'sanitizer.dangerousProtocolDetected',
190
189
  severity: 'error',
191
- source: 'custom'
190
+ source: 'custom',
192
191
  });
193
192
  results.isValid = false;
194
193
 
@@ -209,7 +208,7 @@ const runCustomValidations = (html, variant, results, formatMessage = defaultMes
209
208
  column: 1,
210
209
  rule: 'script-tag-warning',
211
210
  severity: 'warning',
212
- source: 'custom'
211
+ source: 'custom',
213
212
  });
214
213
 
215
214
  // Guard against zero-length matches to avoid infinite loops
@@ -245,7 +244,7 @@ const validateEmailSpecific = (html, results, formatMessage = defaultMessageForm
245
244
  column: 1,
246
245
  rule: 'outlook-compatibility',
247
246
  severity: 'warning',
248
- source: 'email-specific'
247
+ source: 'email-specific',
249
248
  });
250
249
 
251
250
  // Guard against zero-length matches to avoid infinite loops
@@ -265,7 +264,7 @@ const validateEmailSpecific = (html, results, formatMessage = defaultMessageForm
265
264
  column: 1,
266
265
  rule: 'email-css-compatibility',
267
266
  severity: 'warning',
268
- source: 'email-specific'
267
+ source: 'email-specific',
269
268
  });
270
269
 
271
270
  // Guard against zero-length matches to avoid infinite loops
@@ -294,7 +293,7 @@ const validateInAppSpecific = (html, results, formatMessage = defaultMessageForm
294
293
  column: 1,
295
294
  rule: 'mobile-compatibility',
296
295
  severity: 'warning',
297
- source: 'inapp-specific'
296
+ source: 'inapp-specific',
298
297
  });
299
298
 
300
299
  // Guard against zero-length matches to avoid infinite loops
@@ -314,7 +313,7 @@ const validateInAppSpecific = (html, results, formatMessage = defaultMessageForm
314
313
  column: 1,
315
314
  rule: 'mobile-image-size',
316
315
  severity: 'info',
317
- source: 'inapp-specific'
316
+ source: 'inapp-specific',
318
317
  });
319
318
 
320
319
  // Guard against zero-length matches to avoid infinite loops
@@ -336,8 +335,9 @@ const runLiquidValidation = (html, variant, results, formatMessage = defaultMess
336
335
  const liquidResults = validateLiquidHTML(html, variant);
337
336
 
338
337
  // Merge Liquid validation results
338
+ // Client-side Liquid validation errors are blocking (genuine syntax errors)
339
339
  if (liquidResults.errors) {
340
- results.errors.push(...liquidResults.errors);
340
+ results.errors.push(...liquidResults.errors.map((e) => ({ ...e, severity: 'error' })));
341
341
  if (liquidResults.errors.length > 0) {
342
342
  results.isValid = false;
343
343
  }
@@ -375,7 +375,7 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
375
375
  isValid: true,
376
376
  errors: [],
377
377
  warnings: [],
378
- info: []
378
+ info: [],
379
379
  };
380
380
 
381
381
  if (!css || typeof css !== 'string') {
@@ -388,7 +388,7 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
388
388
  unclosedBraces: /\{[^{}]*$/gm,
389
389
  invalidProperty: /[^;{}]+:\s*[^;{}]*[^;}]/g,
390
390
  missingColon: /[^;{}]+\s+[^;{}:]+;/g,
391
- emptyRule: /[^{}]+\{\s*\}/g
391
+ emptyRule: /[^{}]+\{\s*\}/g,
392
392
  };
393
393
 
394
394
  // Check for unclosed braces using RegExp.exec loop
@@ -396,16 +396,15 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
396
396
  unclosedBracesRegex.lastIndex = 0; // Reset lastIndex before running
397
397
  let match;
398
398
  while ((match = unclosedBracesRegex.exec(css)) !== null) {
399
- results.errors.push({
400
- type: 'error',
399
+ results.warnings.push({
400
+ type: 'warning',
401
401
  message: formatMessage('validator.unclosedCssRule'),
402
402
  line: getLineNumber(css, match.index),
403
403
  column: 1,
404
404
  rule: 'unclosed-brace',
405
- severity: 'error',
406
- source: 'css-validator'
405
+ severity: 'warning',
406
+ source: 'css-validator',
407
407
  });
408
- results.isValid = false;
409
408
 
410
409
  // Guard against zero-length matches to avoid infinite loops
411
410
  if (match[0].length === 0) {
@@ -417,34 +416,31 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
417
416
  const emptyRulesRegex = new RegExp(validationPatterns.emptyRule.source, validationPatterns.emptyRule.flags);
418
417
  emptyRulesRegex.lastIndex = 0; // Reset lastIndex before running
419
418
  while ((match = emptyRulesRegex.exec(css)) !== null) {
420
- results.errors.push({
421
- type: 'error',
419
+ results.warnings.push({
420
+ type: 'warning',
422
421
  message: formatMessage('validator.emptyCssRule'),
423
422
  line: getLineNumber(css, match.index),
424
423
  column: 1,
425
424
  rule: 'empty-rule',
426
- severity: 'error',
427
- source: 'css-validator'
425
+ severity: 'warning',
426
+ source: 'css-validator',
428
427
  });
429
- results.isValid = false;
430
428
 
431
429
  // Guard against zero-length matches to avoid infinite loops
432
430
  if (match[0].length === 0) {
433
431
  emptyRulesRegex.lastIndex++;
434
432
  }
435
433
  }
436
-
437
434
  } catch (error) {
438
- results.errors.push({
439
- type: 'error',
435
+ results.warnings.push({
436
+ type: 'warning',
440
437
  message: formatMessage('validator.cssValidationFailed', { error: error.message }),
441
438
  line: 1,
442
439
  column: 1,
443
440
  rule: 'css-validation-error',
444
- severity: 'error',
445
- source: 'css-validator'
441
+ severity: 'warning',
442
+ source: 'css-validator',
446
443
  });
447
- results.isValid = false;
448
444
  }
449
445
 
450
446
  return results;
@@ -457,16 +453,20 @@ export const validateCSS = (css, formatMessage = defaultMessageFormatter) => {
457
453
  * @returns {Object} Combined validation results from all CSS blocks
458
454
  */
459
455
  export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormatter) => {
460
- if (!html) return { isValid: true, errors: [], warnings: [], info: [] };
456
+ if (!html) {
457
+ return {
458
+ isValid: true, errors: [], warnings: [], info: [],
459
+ };
460
+ }
461
461
 
462
462
  // Extract CSS from style tags
463
463
  const styleTagPattern = /<style[^>]*>([\s\S]*?)<\/style>/gi;
464
464
  let match;
465
- let allResults = {
465
+ const allResults = {
466
466
  isValid: true,
467
467
  errors: [],
468
468
  warnings: [],
469
- info: []
469
+ info: [],
470
470
  };
471
471
 
472
472
  while ((match = styleTagPattern.exec(html)) !== null) {
@@ -483,19 +483,18 @@ export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormat
483
483
  }
484
484
  }
485
485
 
486
- // Check for unclosed style tags
486
+ // Check for unclosed style tags (warning only for CKEditor legacy compatibility)
487
487
  const unclosedStylePattern = /<style[^>]*>(?![\s\S]*?<\/style>)/gi;
488
488
  if (unclosedStylePattern.test(html)) {
489
- allResults.errors.push({
490
- type: 'error',
489
+ allResults.warnings.push({
490
+ type: 'warning',
491
491
  message: formatMessage('validator.unclosedCssRule'),
492
492
  line: 1,
493
493
  column: 1,
494
494
  rule: 'unclosed-style-tag',
495
- severity: 'error',
496
- source: 'css-validator'
495
+ severity: 'warning',
496
+ source: 'css-validator',
497
497
  });
498
- allResults.isValid = false;
499
498
  }
500
499
 
501
500
  return allResults;
@@ -504,5 +503,5 @@ export const extractAndValidateCSS = (html, formatMessage = defaultMessageFormat
504
503
  export default {
505
504
  validateHTML,
506
505
  validateCSS,
507
- extractAndValidateCSS
506
+ extractAndValidateCSS,
508
507
  };