@capillarytech/creatives-library 8.0.223-alpha.0 → 8.0.224

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.223-alpha.0",
4
+ "version": "8.0.224",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -19,7 +19,6 @@ const SUBTAGS = 'subtags';
19
19
  * @param {Object} tagObject - The tagLookupMap.
20
20
  */
21
21
  export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [], isLiquidFlow = false, forwardedTags = {}) => {
22
-
23
22
  const supportedList = [];
24
23
  // Verifies the presence of the tag in the 'Add Labels' section.
25
24
  // Incase of journey event context the tags won't be available in the tagObject(tagLookupMap).
@@ -40,7 +39,7 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
40
39
  let updatedChildName = childName;
41
40
  let updatedWithoutDotChildName = childName;
42
41
  if (childName?.includes(".")) {
43
- updatedChildName = "." + childName?.split(".")?.[1];
42
+ updatedChildName = `.${childName?.split(".")?.[1]}`;
44
43
  updatedWithoutDotChildName = childName?.split(".")?.[1];
45
44
  }
46
45
  if (tagObject?.[parentTag]) {
@@ -71,7 +70,6 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
71
70
  if (item?.children?.length) {
72
71
  processChildren(item?.name, item?.children);
73
72
  }
74
-
75
73
  }
76
74
 
77
75
 
@@ -80,19 +78,19 @@ export const checkSupport = (response = {}, tagObject = {}, eventContextTags = [
80
78
 
81
79
  const handleForwardedTags = (forwardedTags) => {
82
80
  const result = [];
83
- Object.keys(forwardedTags).forEach(key => {
81
+ Object.keys(forwardedTags).forEach((key) => {
84
82
  result.push(key); // Add the main key to the result array
85
-
83
+
86
84
  // Check if there are subtags for the current key
87
85
  if (forwardedTags[key].subtags) {
88
86
  // If subtags exist, add all subtag keys to the result array
89
- Object.keys(forwardedTags[key].subtags).forEach(subkey => {
90
- result.push(subkey);
87
+ Object.keys(forwardedTags[key].subtags).forEach((subkey) => {
88
+ result.push(subkey);
91
89
  });
92
90
  }
93
91
  });
94
92
  return result;
95
- }
93
+ };
96
94
 
97
95
  /**
98
96
  * Extracts the names from the given data.
@@ -155,7 +153,7 @@ export const validateTags = ({
155
153
  unsupportedTags: [],
156
154
  isBraceError: false,
157
155
  };
158
- if(tags && tags.length) {
156
+ if (tags && tags.length) {
159
157
  lodashForEach(tags, ({
160
158
  definition: {
161
159
  supportedModules,
@@ -216,7 +214,7 @@ export const validateTags = ({
216
214
  // validations (eg button) are handled on valid property coming from the response.
217
215
  response.isBraceError ? response.valid = false : response.valid = true;
218
216
  return response;
219
- }
217
+ };
220
218
 
221
219
  /**
222
220
  * Checks if the given tag is supported based on the injected tags.
@@ -233,25 +231,25 @@ export const checkIfSupportedTag = (checkingTag, injectedTags) => {
233
231
  result = true;
234
232
  }
235
233
  });
236
-
234
+
237
235
  return result;
238
- }
236
+ };
239
237
 
240
238
  const indexOfEnd = (targetString, string) => {
241
- let io = targetString.indexOf(string);
239
+ const io = targetString.indexOf(string);
242
240
  return io == -1 ? -1 : io + string.length;
243
- }
241
+ };
244
242
 
245
243
  export const skipTags = (tag) => {
246
244
  // If the tag contains the word "entryTrigger.", then it's an event context tag and should not be skipped.
247
245
  if (tag?.match(ENTRY_TRIGGER_TAG_REGEX)) {
248
246
  return false;
249
247
  }
250
- const regexGroups = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)","Link_to_[a-zA-z]","SURVEY.*.TOKEN", "^[A-Za-z].*\\([a-zA-Z\\d]*\\)"];
248
+ const regexGroups = ["dynamic_expiry_date_after_\\d+_days.FORMAT_\\d", "unsubscribe\\(#[a-zA-Z\\d]{6}\\)", "Link_to_[a-zA-z]", "SURVEY.*.TOKEN", "^[A-Za-z].*\\([a-zA-Z\\d]*\\)"];
251
249
  let skipped = false;
252
250
  lodashForEach(regexGroups, (group) => {
253
251
  const groupRegex = new RegExp(group, "g");
254
- let match = groupRegex.exec(tag);
252
+ const match = groupRegex.exec(tag);
255
253
  if (match !== null ) {
256
254
  skipped = true;
257
255
  return true;
@@ -259,7 +257,7 @@ export const skipTags = (tag) => {
259
257
  return true;
260
258
  });
261
259
  return skipped;
262
- }
260
+ };
263
261
 
264
262
  export const transformInjectedTags = (tags) => {
265
263
  lodashForEach(tags, (tag) => {
@@ -274,35 +272,100 @@ export const transformInjectedTags = (tags) => {
274
272
  if (subKey !== '') {
275
273
  temp['tag-header'] = true;
276
274
  if (subKey !== SUBTAGS) {
277
- temp.subtags =lodashCloneDeep(temp[subKey]);
275
+ temp.subtags = lodashCloneDeep(temp[subKey]);
278
276
  delete temp[subKey];
279
277
  }
280
278
  temp.subtags = transformInjectedTags(temp.subtags);
281
279
  }
282
280
  });
283
281
  return tags;
284
- }
282
+ };
285
283
 
286
284
  //checks if the opening curly brackets have corresponding closing brackets
287
285
  export const validateIfTagClosed = (value) => {
288
286
  if (value.includes("{{{{") || value.includes("}}}}")) {
289
287
  return false;
290
288
  }
291
- let regex1 = /{{.*?}}/g;
292
- let regex2 = /{{/g;
293
- let regex3 = /}}/g;
289
+ const regex1 = /{{.*?}}/g;
290
+ const regex2 = /{{/g;
291
+ const regex3 = /}}/g;
294
292
 
295
- let l1 = value.match(regex1)?.length;
296
- let l2 = value.match(regex2)?.length;
297
- let l3 = value.match(regex3)?.length;
293
+ const l1 = value.match(regex1)?.length;
294
+ const l2 = value.match(regex2)?.length;
295
+ const l3 = value.match(regex3)?.length;
298
296
 
299
297
  return (l1 == l2 && l2 == l3 && l1 == l3);
300
-
298
+ };
299
+
300
+ /**
301
+ * Validates tag format: ensures tags are in format {{tag_name}} and checks for invalid patterns
302
+ * Validates against:
303
+ * - Single braces like {tag} (must be {{tag}})
304
+ * - Invalid patterns like {{first or first}}, {{first and first}}
305
+ * - Empty tag names
306
+ * - Unclosed single braces within tag names
307
+ * @param {string} textContent - The text content to validate
308
+ * @returns {boolean} - True if all tags have valid format, false otherwise
309
+ */
310
+ export const validateTagFormat = (textContent) => {
311
+ // Find all potential tag patterns {{tag_name}}
312
+ const tagPattern = /{{[^}]*}}/g;
313
+ const matches = textContent.match(tagPattern) || [];
314
+
315
+ // Remove all valid {{tag}} patterns from content to check for invalid braces
316
+ let contentWithoutValidTags = textContent;
317
+ matches.forEach((match) => {
318
+ contentWithoutValidTags = contentWithoutValidTags.replace(match, '');
319
+ });
320
+
321
+ // Check if there are any remaining braces (single braces or unclosed braces)
322
+ // These would be invalid patterns like {tag}, {first, first}, etc.
323
+ if (contentWithoutValidTags.includes('{') || contentWithoutValidTags.includes('}')) {
324
+ return false;
325
+ }
326
+
327
+ // Check each tag for valid format
328
+ const allTagsValid = matches.every((match) => {
329
+ // Valid tag format: {{tag_name}} - must start with {{ and end with }}
330
+ if (!match.startsWith('{{') || !match.endsWith('}}')) {
331
+ return false;
332
+ }
333
+
334
+ // Extract tag name (content between {{ and }})
335
+ const tagName = match.slice(2, -2).trim();
336
+
337
+ // Tag name should not be empty
338
+ if (!tagName) {
339
+ return false;
340
+ }
341
+
342
+ // Check for invalid patterns in tag name
343
+ // Invalid patterns: "first or first", "first and first", etc.
344
+ const invalidPatterns = [
345
+ /\s+or\s+/i, // " or " as separate word (e.g., "first or first")
346
+ /\s+and\s+/i, // " and " as separate word
347
+ ];
348
+
349
+ const hasInvalidPattern = invalidPatterns.some((pattern) => pattern.test(tagName));
350
+ if (hasInvalidPattern) {
351
+ return false;
352
+ }
353
+
354
+ // Check for unclosed single braces in tag name (e.g., {{first{name}})
355
+ const singleOpenBraces = (tagName.match(/{/g) || []).length;
356
+ const singleCloseBraces = (tagName.match(/}/g) || []).length;
357
+ if (singleOpenBraces !== singleCloseBraces) {
358
+ return false;
359
+ }
360
+
361
+ return true;
362
+ });
363
+
364
+ return allTagsValid;
301
365
  };
302
366
 
303
367
  //replaces encoded string with their respective characters
304
368
  export const preprocessHtml = (content) => {
305
-
306
369
  const replacements = {
307
370
  "'": "'",
308
371
  """: "'",
@@ -310,7 +373,7 @@ export const preprocessHtml = (content) => {
310
373
  "&": "&",
311
374
  "&lt;": "<",
312
375
  "&gt;": ">",
313
- "\n": "", // Handling newlines by replacing them with an empty string
376
+ "\n": "", // Handling newlines by replacing them with an empty string
314
377
  };
315
378
 
316
379
 
@@ -324,28 +387,22 @@ export const preprocessHtml = (content) => {
324
387
  });
325
388
 
326
389
  // Step 2: Perform the standard replacements on the entire content
327
- return contentWithStyleFixes?.replace(/&#39;|&quot;|&amp;|&lt;|&gt;|"|\n/g, match => replacements[match]);
390
+ return contentWithStyleFixes?.replace(/&#39;|&quot;|&amp;|&lt;|&gt;|"|\n/g, (match) => replacements[match]);
328
391
  };
329
392
 
330
393
  //this is used to get the subtags from custom or extended tags
331
- export const getTagMapValue = (object = {}) => {
332
- return Object.values(
333
- object
334
- ).reduce((acc, current) => {
335
- return { ...acc, ...current?.subtags ?? {} };
336
- }, {});
337
- };
338
-
339
- export const getLoyaltyTagsMapValue = (object = {}) => {
340
- return Object.entries(object).reduce((acc, [key, current]) => {
341
- if (current?.subtags && Object.keys(current.subtags).length > 0) {
342
- // If subtags exist → merge them
343
- return { ...acc, ...(current.subtags ?? {}) };
344
- }
345
- // If no subtags → keep the tag itself
346
- return { ...acc, [key]: current };
347
- }, {});
348
- };
394
+ export const getTagMapValue = (object = {}) => Object.values(
395
+ object
396
+ ).reduce((acc, current) => ({ ...acc, ...current?.subtags ?? {} }), {});
397
+
398
+ export const getLoyaltyTagsMapValue = (object = {}) => Object.entries(object).reduce((acc, [key, current]) => {
399
+ if (current?.subtags && Object.keys(current.subtags).length > 0) {
400
+ // If subtags exist → merge them
401
+ return { ...acc, ...(current.subtags ?? {}) };
402
+ }
403
+ // If no subtags keep the tag itself
404
+ return { ...acc, [key]: current };
405
+ }, {});
349
406
 
350
407
 
351
408
  /**
@@ -354,27 +411,25 @@ export const getLoyaltyTagsMapValue = (object = {}) => {
354
411
  * @param {Object} object - The input object containing top-level keys with optional subtags.
355
412
  * @returns {Object} - A flat map containing all top-level keys and their subtags.
356
413
  */
357
- export const getForwardedMapValues = (object = {}) => {
358
- return Object?.entries(object)?.reduce((acc, [key, current]) => {
359
- // Check if current has 'subtags' and it's an object
360
- if (current && current?.subtags && typeof current?.subtags === 'object') {
361
- // Add the top-level key with its 'name' and 'desc'
362
- acc[key] = {
363
- name: current?.name,
364
- desc: current?.desc,
365
- };
366
-
367
- // Merge the subtags into the accumulator
368
- acc = { ...acc, ...current?.subtags };
369
- } else if (current && typeof current === 'object') {
370
- // If no 'subtags', add the top-level key with its 'name' and 'desc'
371
- acc[key] = {
372
- name: current?.name,
373
- desc: current?.desc,
374
- };
375
- }
376
-
377
- // If the current entry is not an object or lacks 'name'/'desc', skip it
378
- return acc;
379
- }, {});
380
- };
414
+ export const getForwardedMapValues = (object = {}) => Object?.entries(object)?.reduce((acc, [key, current]) => {
415
+ // Check if current has 'subtags' and it's an object
416
+ if (current && current?.subtags && typeof current?.subtags === 'object') {
417
+ // Add the top-level key with its 'name' and 'desc'
418
+ acc[key] = {
419
+ name: current?.name,
420
+ desc: current?.desc,
421
+ };
422
+
423
+ // Merge the subtags into the accumulator
424
+ acc = { ...acc, ...current?.subtags };
425
+ } else if (current && typeof current === 'object') {
426
+ // If no 'subtags', add the top-level key with its 'name' and 'desc'
427
+ acc[key] = {
428
+ name: current?.name,
429
+ desc: current?.desc,
430
+ };
431
+ }
432
+
433
+ // If the current entry is not an object or lacks 'name'/'desc', skip it
434
+ return acc;
435
+ }, {});
@@ -52,7 +52,7 @@ import {
52
52
  INITIAL_PAYLOAD, EMAIL, TEST, DESKTOP, ACTIVE, MOBILE,
53
53
  } from './constants';
54
54
  import { GLOBAL_CONVERT_OPTIONS } from '../FormBuilder/constants';
55
- import { validateIfTagClosed } from '../../utils/tagValidations';
55
+ import { validateIfTagClosed, validateTagFormat } from '../../utils/tagValidations';
56
56
 
57
57
  const TestAndPreviewSlidebox = (props) => {
58
58
  const {
@@ -110,7 +110,12 @@ const TestAndPreviewSlidebox = (props) => {
110
110
  requiredTags.some((tag) => !customValues[tag.fullPath]) || !isContentValid
111
111
  ), [requiredTags, customValues, isContentValid]);
112
112
 
113
- // Function to validate tags in content
113
+ /**
114
+ * Validates tags in content: checks for proper tag format and balanced braces
115
+ * Uses validateIfTagClosed and validateTagFormat from utils/tagValidations
116
+ * @param {string} content - The HTML content to validate
117
+ * @returns {boolean} - True if content is valid, false otherwise
118
+ */
114
119
  const validateContentTags = (content) => {
115
120
  if (!content) return true;
116
121
 
@@ -127,66 +132,14 @@ const TestAndPreviewSlidebox = (props) => {
127
132
  return true;
128
133
  }
129
134
 
130
- // First check if tags are properly closed using the utility function
135
+ // First check if tags are properly closed using the utility function from tagValidations
131
136
  // This validates that all opening braces have corresponding closing braces
132
137
  if (!validateIfTagClosed(textContent)) {
133
138
  return false;
134
139
  }
135
140
 
136
141
  // Now validate tag format: tags must be in format {{tag_name}}
137
- // Find all valid tag patterns {{tag_name}}
138
- const tagPattern = /{{[^}]*}}/g;
139
- const matches = textContent.match(tagPattern) || [];
140
-
141
- // Remove all valid {{tag}} patterns from content to check for invalid braces
142
- let contentWithoutValidTags = textContent;
143
- matches.forEach((match) => {
144
- contentWithoutValidTags = contentWithoutValidTags.replace(match, '');
145
- });
146
-
147
- // Check if there are any remaining braces (single braces or unclosed braces)
148
- // These would be invalid patterns like {tag}, {first, first}, etc.
149
- if (contentWithoutValidTags.includes('{') || contentWithoutValidTags.includes('}')) {
150
- return false;
151
- }
152
-
153
- // Check each tag for valid format
154
- for (const match of matches) {
155
- // Valid tag format: {{tag_name}} - must start with {{ and end with }}
156
- if (!match.startsWith('{{') || !match.endsWith('}}')) {
157
- return false;
158
- }
159
-
160
- // Extract tag name (content between {{ and }})
161
- const tagName = match.slice(2, -2).trim();
162
-
163
- // Tag name should not be empty
164
- if (!tagName) {
165
- return false;
166
- }
167
-
168
- // Check for invalid patterns in tag name
169
- // Invalid patterns: "first or first", "first and first", etc.
170
- const invalidPatterns = [
171
- /\s+or\s+/i, // " or " as separate word (e.g., "first or first")
172
- /\s+and\s+/i, // " and " as separate word
173
- ];
174
-
175
- for (const pattern of invalidPatterns) {
176
- if (pattern.test(tagName)) {
177
- return false;
178
- }
179
- }
180
-
181
- // Check for unclosed single braces in tag name (e.g., {{first{name}})
182
- const singleOpenBraces = (tagName.match(/{/g) || []).length;
183
- const singleCloseBraces = (tagName.match(/}/g) || []).length;
184
- if (singleOpenBraces !== singleCloseBraces) {
185
- return false;
186
- }
187
- }
188
-
189
- return true;
142
+ return validateTagFormat(textContent);
190
143
  } catch (error) {
191
144
  // If conversion fails, fall back to validating the original content
192
145
  console.warn('Error converting content for validation:', error);
@@ -200,49 +153,7 @@ const TestAndPreviewSlidebox = (props) => {
200
153
  return false;
201
154
  }
202
155
 
203
- const tagPattern = /{{[^}]*}}/g;
204
- const matches = content.match(tagPattern) || [];
205
-
206
- // Remove all valid {{tag}} patterns from content to check for invalid braces
207
- let contentWithoutValidTags = content;
208
- matches.forEach((match) => {
209
- contentWithoutValidTags = contentWithoutValidTags.replace(match, '');
210
- });
211
-
212
- // Check if there are any remaining braces (single braces or unclosed braces)
213
- if (contentWithoutValidTags.includes('{') || contentWithoutValidTags.includes('}')) {
214
- return false;
215
- }
216
-
217
- for (const match of matches) {
218
- if (!match.startsWith('{{') || !match.endsWith('}}')) {
219
- return false;
220
- }
221
-
222
- const tagName = match.slice(2, -2).trim();
223
- if (!tagName) {
224
- return false;
225
- }
226
-
227
- const invalidPatterns = [
228
- /\s+or\s+/i,
229
- /\s+and\s+/i,
230
- ];
231
-
232
- for (const pattern of invalidPatterns) {
233
- if (pattern.test(tagName)) {
234
- return false;
235
- }
236
- }
237
-
238
- const singleOpenBraces = (tagName.match(/{/g) || []).length;
239
- const singleCloseBraces = (tagName.match(/}/g) || []).length;
240
- if (singleOpenBraces !== singleCloseBraces) {
241
- return false;
242
- }
243
- }
244
-
245
- return true;
156
+ return validateTagFormat(content);
246
157
  }
247
158
  };
248
159