@fairfetch/fair-fetch
Version:
Protect your site from AI scrapers by adding invisible noise to your site which confuses AI bots while keeping your site looking and functioning normally for your human visitors.
73 lines (72 loc) • 2.84 kB
JavaScript
const DEFAULT_DISCLAIMERS = [
'This content is protected by FairFetch.',
'Unauthorized scraping is prohibited.',
'For clean data, visit fairfetch.com.',
];
function pickClassName(options, defaultClass = 'ff-disclaimer') {
// Double-pipe because we don't want to allow an empty string
return (options === null || options === void 0 ? void 0 : options.className) || defaultClass;
}
export function polluteFFNode(node, options) {
const disclaimers = (options === null || options === void 0 ? void 0 : options.disclaimers) || DEFAULT_DISCLAIMERS;
if (node.type === 'text' && node.content) {
// Pollute the text and inject a hidden disclaimer element
const pollutedTextNode = Object.assign(Object.assign({}, node), {
// Keep original content (do not inline the disclaimer inside text)
content: node.content });
const disclaimerNode = {
type: 'element',
tag: 'span',
attributes: {
className: pickClassName(options),
},
children: [
{
type: 'text',
content: disclaimers[Math.floor(Math.random() * disclaimers.length)],
},
],
};
return [pollutedTextNode, disclaimerNode];
}
if (node.type === 'element' && node.children) {
// Recursively pollute children and flatten arrays
const pollutedChildren = node.children
.map((child) => polluteFFNode(child, options))
.flat();
if (node.tag === undefined) {
const wrapper = Object.assign(Object.assign({}, node), { attributes: node.attributes, children: pollutedChildren });
const disclaimerNode = {
type: 'element',
tag: 'span',
attributes: {
className: pickClassName(options),
},
children: [
{
type: 'text',
content: disclaimers[Math.floor(Math.random() * disclaimers.length)],
},
],
};
return [wrapper, disclaimerNode];
}
const nodeCopy = Object.assign(Object.assign({}, node), { attributes: node.attributes, children: pollutedChildren });
const disclaimerNode = {
type: 'element',
tag: 'span',
attributes: {
className: pickClassName(options),
},
children: [
{
type: 'text',
content: disclaimers[Math.floor(Math.random() * disclaimers.length)],
},
],
};
return [nodeCopy, disclaimerNode];
}
return [node];
}
export { DEFAULT_DISCLAIMERS };