Setting the file. One moment. Content Boundary · Wix Headless Replatform · wix/skills · Skills Docs28.10
Workflow
scripts/lib/content-boundary.mjs
JavaScript·61 lines·3 KB
/
i
],
5 ["javascript-function-body", /\bfunction\s+[A-Za-z_$][\w$]*\s*\([^)]*\)\s*\{|\([^)]*\)\s*=>\s*\{/],
6 ["javascript-browser-constructor", /\bnew\s+(?:ResizeObserver|IntersectionObserver|MutationObserver|CustomEvent)\s*\(/],
7 ["stylesheet-rule", /(?:^|\s)(?:[#.][\w-]+|[a-z][\w-]*(?:\[[^\]]+\])?)(?:\s*[>+~]\s*(?:\*|[#.a-z][\w-]*))*\s*\{\s*[\w-]+\s*:\s*[^{};]+;?/i],
8 ["stylesheet-at-rule", /@(?:media|supports|keyframes|font-face)\b[^{}]*\{/i],
9 ["embedded-source-markup", /(?:<|<)(?:script|style|link|template)\b/i],
10];
11
12export function detectContentContamination(value, { allowVisibleCode = false } = {}) {
13 if (allowVisibleCode) return { contaminated: false, reasons: [] };
14 const text = String(value ?? "").replace(/\s+/g, " ").trim();
15 if (!text) return { contaminated: false, reasons: [] };
16 const reasons = STRONG_CONTAMINATION_SIGNALS
17 .filter(([, pattern]) => pattern.test(text))
18 .map(([reason]) => reason);
19 return { contaminated: reasons.length > 0, reasons };
20}
21
22export function inspectContentObject(value, options = {}) {
23 const findings = [];
24 visitContentStrings(value, [], (text, path) => {
25 const result = detectContentContamination(text, options);
26 if (result.contaminated) findings.push({ path: path.join("."), reasons: result.reasons });
27 });
28 return {
29 contaminated: findings.length > 0,
30 reasons: [...new Set(findings.flatMap((finding) => finding.reasons))],
31 findings,
32 };
33}
34
35export function removeContaminatedContent(value, options = {}) {
36 if (Array.isArray(value)) return value.map((item) => removeContaminatedContent(item, options));
37 if (!value || typeof value !== "object") return value;
38 return Object.fromEntries(Object.entries(value).map(([key, item]) => {
39 if (typeof item === "string" && isContentBearingKey(key)) {
40 return [key, detectContentContamination(item, options).contaminated ? "" : item];
41 }
42 return [key, removeContaminatedContent(item, options)];
43 }));
44}
45
46function visitContentStrings(value, path, visit) {
47 if (Array.isArray(value)) {
48 value.forEach((item, index) => visitContentStrings(item, [...path, String(index)], visit));
49 return;
50 }
51 if (!value || typeof value !== "object") return;
52 for (const [key, item] of Object.entries(value)) {
53 const nextPath = [...path, key];
54 if (typeof item === "string" && isContentBearingKey(key)) visit(item, nextPath);
55 else visitContentStrings(item, nextPath, visit);
56 }
57}
58
59function isContentBearingKey(key) {
60 return /^(?:text|heading|label|title|description|paragraphs?|content|copy|accessibleName|ariaLabel|legalText)$/i.test(key);
61}