cdp-edge 1.18.0 → 2.0.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.
- package/README.md +308 -308
- package/bin/cdp-edge.js +61 -61
- package/dist/commands/analyze.js +52 -52
- package/dist/commands/infra.js +54 -54
- package/dist/commands/install.js +187 -0
- package/dist/commands/server.js +174 -174
- package/dist/commands/setup.js +19 -1
- package/dist/commands/validate.js +84 -84
- package/dist/index.js +12 -12
- package/extracted-skill/tracking-events-generator/advanced-matching.js +364 -364
- package/extracted-skill/tracking-events-generator/anti-blocking.js +285 -285
- package/extracted-skill/tracking-events-generator/cdpTrack.js +641 -641
- package/extracted-skill/tracking-events-generator/engagement-scoring.js +226 -226
- package/extracted-skill/tracking-events-generator/evals/evals.json +235 -235
- package/extracted-skill/tracking-events-generator/integration-test.js +497 -497
- package/extracted-skill/tracking-events-generator/micro-events.js +992 -992
- package/extracted-skill/tracking-events-generator/models/pinterest/conversions-api-template.js +144 -144
- package/extracted-skill/tracking-events-generator/models/pinterest/event-mappings.json +48 -48
- package/extracted-skill/tracking-events-generator/models/pinterest/tag-template.js +28 -28
- package/extracted-skill/tracking-events-generator/models/reddit/conversions-api-template.js +205 -205
- package/extracted-skill/tracking-events-generator/models/reddit/event-mappings.json +56 -56
- package/extracted-skill/tracking-events-generator/models/reddit/pixel-template.js +19 -19
- package/extracted-skill/tracking-events-generator/models/scenarios/behavior-engine.js +425 -425
- package/package.json +76 -76
- package/server-edge-tracker/schema.sql +265 -265
- package/server-edge-tracker/worker.js +4160 -4160
- package/server-edge-tracker/wrangler.toml +103 -103
- package/templates/pinterest/conversions-api-template.js +144 -144
- package/templates/pinterest/event-mappings.json +48 -48
- package/templates/pinterest/tag-template.js +28 -28
- package/templates/reddit/conversions-api-template.js +205 -205
- package/templates/reddit/event-mappings.json +56 -56
- package/templates/reddit/pixel-template.js +19 -19
- package/templates/scenarios/behavior-engine.js +425 -425
|
@@ -1,285 +1,285 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ANTI-BLOCKING STRATEGY - CDP Edge (Quantum Tier)
|
|
3
|
-
*
|
|
4
|
-
* Sistema para maximizar resiliência contra ad-blockers e garantir
|
|
5
|
-
* que o tracking funcione mesmo em ambientes hostis.
|
|
6
|
-
*
|
|
7
|
-
* @version 1.0.0
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// ── Guards ────────────────────────────────────────────────
|
|
11
|
-
const isBrowser = typeof window !== 'undefined';
|
|
12
|
-
|
|
13
|
-
// ── Configurações de Anti-Blocking ─────────────────────
|
|
14
|
-
|
|
15
|
-
const ANTI_BLOCKING_CONFIG = {
|
|
16
|
-
// Mesmo domínio evita bloqueios de CORS e ad-blockers
|
|
17
|
-
endpoint: '/api/tracking',
|
|
18
|
-
|
|
19
|
-
// Retries com exponential backoff
|
|
20
|
-
maxRetries: 3,
|
|
21
|
-
retryDelays: [1000, 3000, 6000], // 1s, 3s, 6s
|
|
22
|
-
|
|
23
|
-
// Fallback para Beacon API (quando fetch falha)
|
|
24
|
-
useBeaconFallback: true,
|
|
25
|
-
|
|
26
|
-
// First-party cookies (ad-block proof)
|
|
27
|
-
cookieDuration: 60 * 60 * 24 * 365, // 365 dias
|
|
28
|
-
cookieDomain: '', // Será definido dinamicamente
|
|
29
|
-
|
|
30
|
-
// Detectar ad-blockers
|
|
31
|
-
detectAdBlocker: true,
|
|
32
|
-
adBlockerBaitClass: 'adsbox adbanner pub_300x250',
|
|
33
|
-
|
|
34
|
-
// Lightweight code (evitar patterns de bloqueio)
|
|
35
|
-
minify: false, // Opcional: usar código minificado em produção
|
|
36
|
-
noConsoleLogs: false, // Opcional: remover console.logs em produção
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// ── Detecção de Ad-Blocker ─────────────────────────
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Detecta se um ad-blocker está ativo
|
|
43
|
-
*
|
|
44
|
-
* @returns {boolean} True se ad-blocker detectado
|
|
45
|
-
*/
|
|
46
|
-
function detectAdBlocker() {
|
|
47
|
-
if (!isBrowser || !ANTI_BLOCKING_CONFIG.detectAdBlocker) return false;
|
|
48
|
-
|
|
49
|
-
// Método 1: Criar elemento com classe comum de ads
|
|
50
|
-
const baitElement = document.createElement('div');
|
|
51
|
-
baitElement.innerHTML = ' ';
|
|
52
|
-
baitElement.className = ANTI_BLOCKING_CONFIG.adBlockerBaitClass;
|
|
53
|
-
baitElement.style.cssText = 'position: absolute; top: -1000px; left: -1000px;';
|
|
54
|
-
document.body.appendChild(baitElement);
|
|
55
|
-
|
|
56
|
-
const isBlocked = getComputedStyle(baitElement).display === 'none';
|
|
57
|
-
|
|
58
|
-
document.body.removeChild(baitElement);
|
|
59
|
-
|
|
60
|
-
// Método 2: Verificar se bloqueia requests de tracking
|
|
61
|
-
try {
|
|
62
|
-
const testPixel = new Image();
|
|
63
|
-
testPixel.src = '/pixel-test.png?t=' + Date.now();
|
|
64
|
-
testPixel.onload = () => console.log('✅ Pixel não bloqueado');
|
|
65
|
-
testPixel.onerror = () => console.warn('⚠️ Pixel pode estar bloqueado');
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.warn('⚠️ Erro ao testar pixel:', error);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return isBlocked;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ── Resiliência de Envio ─────────────────────────────
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Envia dados com retry automático (exponential backoff)
|
|
77
|
-
*
|
|
78
|
-
* @param {object} data - Dados para enviar
|
|
79
|
-
* @param {string} endpoint - Endpoint de destino
|
|
80
|
-
* @returns {Promise} Promise com resultado
|
|
81
|
-
*/
|
|
82
|
-
async function sendWithRetry(data, endpoint = ANTI_BLOCKING_CONFIG.endpoint) {
|
|
83
|
-
if (!isBrowser) return { success: false, error: 'Not in browser' };
|
|
84
|
-
|
|
85
|
-
let lastError = null;
|
|
86
|
-
|
|
87
|
-
for (let attempt = 0; attempt < ANTI_BLOCKING_CONFIG.maxRetries; attempt++) {
|
|
88
|
-
try {
|
|
89
|
-
// Tenta enviar via fetch
|
|
90
|
-
const response = await fetch(endpoint, {
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers: {
|
|
93
|
-
'Content-Type': 'application/json',
|
|
94
|
-
},
|
|
95
|
-
body: JSON.stringify(data),
|
|
96
|
-
keepalive: true, // Garante envio mesmo se usuário fechar aba
|
|
97
|
-
credentials: 'same-origin', // First-party cookies
|
|
98
|
-
cache: 'no-cache' // Evitar cache de requests de tracking
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (response.ok) {
|
|
102
|
-
console.log(`✅ Envio bem-sucedido (tentativa ${attempt + 1})`);
|
|
103
|
-
return await response.json();
|
|
104
|
-
} else {
|
|
105
|
-
const errorText = await response.text();
|
|
106
|
-
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
107
|
-
}
|
|
108
|
-
} catch (error) {
|
|
109
|
-
lastError = error;
|
|
110
|
-
console.warn(`⚠️ Tentativa ${attempt + 1} falhou:`, error.message);
|
|
111
|
-
|
|
112
|
-
// Se não for a última tentativa, aguarda antes de retry
|
|
113
|
-
if (attempt < ANTI_BLOCKING_CONFIG.maxRetries - 1) {
|
|
114
|
-
const delay = ANTI_BLOCKING_CONFIG.retryDelays[attempt];
|
|
115
|
-
console.log(`⏳ Aguardando ${delay}ms antes de retry...`);
|
|
116
|
-
await sleep(delay);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Todas as tentativas falharam
|
|
122
|
-
console.error('❌ Todas as tentativas de envio falharam:', lastError);
|
|
123
|
-
|
|
124
|
-
// Fallback: Beacon API
|
|
125
|
-
if (ANTI_BLOCKING_CONFIG.useBeaconFallback && navigator.sendBeacon) {
|
|
126
|
-
console.log('🔄 Tentando Beacon API como fallback...');
|
|
127
|
-
const beaconSuccess = navigator.sendBeacon(endpoint, JSON.stringify(data));
|
|
128
|
-
|
|
129
|
-
if (beaconSuccess) {
|
|
130
|
-
console.log('✅ Beacon API bem-sucedido');
|
|
131
|
-
return { success: true, method: 'beacon' };
|
|
132
|
-
} else {
|
|
133
|
-
console.error('❌ Beacon API também falhou');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return { success: false, error: lastError?.message, attempts: ANTI_BLOCKING_CONFIG.maxRetries };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Função utilitária de delay
|
|
142
|
-
*/
|
|
143
|
-
function sleep(ms) {
|
|
144
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ── First-Party Cookies (Ad-Block Proof) ───────────────
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Define cookie first-party (não bloqueado por ad-blockers)
|
|
151
|
-
*
|
|
152
|
-
* @param {string} name - Nome do cookie
|
|
153
|
-
* @param {string} value - Valor do cookie
|
|
154
|
-
* @param {number} maxAge - Tempo de vida em segundos
|
|
155
|
-
*/
|
|
156
|
-
function setFirstPartyCookie(name, value, maxAge = ANTI_BLOCKING_CONFIG.cookieDuration) {
|
|
157
|
-
if (!isBrowser) return;
|
|
158
|
-
|
|
159
|
-
// Extrair domínio atual (para umbrella domain)
|
|
160
|
-
const currentDomain = window.location.hostname;
|
|
161
|
-
const rootDomain = currentDomain.split('.').slice(-2).join('.'); // ex: example.com
|
|
162
|
-
|
|
163
|
-
const cookieOptions = [
|
|
164
|
-
`${name}=${value}`,
|
|
165
|
-
`max-age=${maxAge}`,
|
|
166
|
-
'path=/',
|
|
167
|
-
`domain=.${rootDomain}`, // Umbrella domain para subdomínios
|
|
168
|
-
'SameSite=Lax',
|
|
169
|
-
'Secure'
|
|
170
|
-
].join('; ');
|
|
171
|
-
|
|
172
|
-
document.cookie = cookieOptions;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Obtém cookie first-party
|
|
177
|
-
*
|
|
178
|
-
* @param {string} name - Nome do cookie
|
|
179
|
-
* @returns {string|null} Valor do cookie ou null
|
|
180
|
-
*/
|
|
181
|
-
function getFirstPartyCookie(name) {
|
|
182
|
-
if (!isBrowser) return null;
|
|
183
|
-
|
|
184
|
-
const value = `; ${document.cookie}`;
|
|
185
|
-
const parts = value.split(`; ${name}=`);
|
|
186
|
-
|
|
187
|
-
if (parts.length === 2) {
|
|
188
|
-
return parts.pop().split(';').shift();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// ── Lightweight Code (Evitar Patterns de Bloqueio) ─────────
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Verifica se código deve ser minificado (evitar palavras-chave de ad-blockers)
|
|
198
|
-
*
|
|
199
|
-
* @returns {boolean} True se deve minificar
|
|
200
|
-
*/
|
|
201
|
-
function shouldMinifyCode() {
|
|
202
|
-
return ANTI_BLOCKING_CONFIG.minify && isBrowser;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Remove console.logs se configurado (evitar detecção)
|
|
207
|
-
*
|
|
208
|
-
* @param {boolean} remove - Remove console.logs?
|
|
209
|
-
*/
|
|
210
|
-
function configureConsoleLogs(remove = ANTI_BLOCKING_CONFIG.noConsoleLogs) {
|
|
211
|
-
if (!isBrowser || !remove) return;
|
|
212
|
-
|
|
213
|
-
// Sobrescrever console com funções vazias
|
|
214
|
-
const noop = () => {};
|
|
215
|
-
console.log = noop;
|
|
216
|
-
console.warn = noop;
|
|
217
|
-
console.error = noop;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// ── Same-Domain Protocol (Anti-Adblock) ─────────────
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Verifica se endpoint está no mesmo domínio
|
|
224
|
-
*
|
|
225
|
-
* @param {string} endpoint - Endpoint para verificar
|
|
226
|
-
* @returns {boolean} True se mesmo domínio
|
|
227
|
-
*/
|
|
228
|
-
function isSameDomain(endpoint) {
|
|
229
|
-
if (!isBrowser) return true;
|
|
230
|
-
|
|
231
|
-
const endpointUrl = new URL(endpoint, window.location.href);
|
|
232
|
-
return endpointUrl.hostname === window.location.hostname;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ── Inicialização ────────────────────────────────────────────
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Inicializa sistema de anti-blocking
|
|
239
|
-
*/
|
|
240
|
-
function initAntiBlocking() {
|
|
241
|
-
if (!isBrowser) return;
|
|
242
|
-
|
|
243
|
-
console.log('🛡️ Inicializando Anti-Blocking System...');
|
|
244
|
-
|
|
245
|
-
// 1. Detectar ad-blocker
|
|
246
|
-
const adBlockerActive = detectAdBlocker();
|
|
247
|
-
if (adBlockerActive) {
|
|
248
|
-
console.warn('⚠️ Ad-Blocker detectado - usando estratégias de resiliência');
|
|
249
|
-
// Enviar evento de ad-blocker detectado
|
|
250
|
-
if (typeof cdpTrack !== 'undefined' && cdpTrack.track) {
|
|
251
|
-
cdpTrack.track('adblocker_detected', {
|
|
252
|
-
user_agent: navigator.userAgent,
|
|
253
|
-
timestamp: Date.now()
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// 2. Configurar first-party cookies
|
|
259
|
-
const userId = getFirstPartyCookie('_cdp_uid');
|
|
260
|
-
if (!userId) {
|
|
261
|
-
const newUserId = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
262
|
-
setFirstPartyCookie('_cdp_uid', newUserId);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// 3. Configurar console logs (se necessário)
|
|
266
|
-
if (ANTI_BLOCKING_CONFIG.noConsoleLogs) {
|
|
267
|
-
configureConsoleLogs(true);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
console.log('✅ Anti-Blocking System inicializado');
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ── Exportações ────────────────────────────────────────────────
|
|
274
|
-
|
|
275
|
-
export {
|
|
276
|
-
sendWithRetry,
|
|
277
|
-
detectAdBlocker,
|
|
278
|
-
setFirstPartyCookie,
|
|
279
|
-
getFirstPartyCookie,
|
|
280
|
-
isSameDomain,
|
|
281
|
-
configureConsoleLogs,
|
|
282
|
-
shouldMinifyCode,
|
|
283
|
-
ANTI_BLOCKING_CONFIG,
|
|
284
|
-
initAntiBlocking
|
|
285
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* ANTI-BLOCKING STRATEGY - CDP Edge (Quantum Tier)
|
|
3
|
+
*
|
|
4
|
+
* Sistema para maximizar resiliência contra ad-blockers e garantir
|
|
5
|
+
* que o tracking funcione mesmo em ambientes hostis.
|
|
6
|
+
*
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ── Guards ────────────────────────────────────────────────
|
|
11
|
+
const isBrowser = typeof window !== 'undefined';
|
|
12
|
+
|
|
13
|
+
// ── Configurações de Anti-Blocking ─────────────────────
|
|
14
|
+
|
|
15
|
+
const ANTI_BLOCKING_CONFIG = {
|
|
16
|
+
// Mesmo domínio evita bloqueios de CORS e ad-blockers
|
|
17
|
+
endpoint: '/api/tracking',
|
|
18
|
+
|
|
19
|
+
// Retries com exponential backoff
|
|
20
|
+
maxRetries: 3,
|
|
21
|
+
retryDelays: [1000, 3000, 6000], // 1s, 3s, 6s
|
|
22
|
+
|
|
23
|
+
// Fallback para Beacon API (quando fetch falha)
|
|
24
|
+
useBeaconFallback: true,
|
|
25
|
+
|
|
26
|
+
// First-party cookies (ad-block proof)
|
|
27
|
+
cookieDuration: 60 * 60 * 24 * 365, // 365 dias
|
|
28
|
+
cookieDomain: '', // Será definido dinamicamente
|
|
29
|
+
|
|
30
|
+
// Detectar ad-blockers
|
|
31
|
+
detectAdBlocker: true,
|
|
32
|
+
adBlockerBaitClass: 'adsbox adbanner pub_300x250',
|
|
33
|
+
|
|
34
|
+
// Lightweight code (evitar patterns de bloqueio)
|
|
35
|
+
minify: false, // Opcional: usar código minificado em produção
|
|
36
|
+
noConsoleLogs: false, // Opcional: remover console.logs em produção
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// ── Detecção de Ad-Blocker ─────────────────────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Detecta se um ad-blocker está ativo
|
|
43
|
+
*
|
|
44
|
+
* @returns {boolean} True se ad-blocker detectado
|
|
45
|
+
*/
|
|
46
|
+
function detectAdBlocker() {
|
|
47
|
+
if (!isBrowser || !ANTI_BLOCKING_CONFIG.detectAdBlocker) return false;
|
|
48
|
+
|
|
49
|
+
// Método 1: Criar elemento com classe comum de ads
|
|
50
|
+
const baitElement = document.createElement('div');
|
|
51
|
+
baitElement.innerHTML = ' ';
|
|
52
|
+
baitElement.className = ANTI_BLOCKING_CONFIG.adBlockerBaitClass;
|
|
53
|
+
baitElement.style.cssText = 'position: absolute; top: -1000px; left: -1000px;';
|
|
54
|
+
document.body.appendChild(baitElement);
|
|
55
|
+
|
|
56
|
+
const isBlocked = getComputedStyle(baitElement).display === 'none';
|
|
57
|
+
|
|
58
|
+
document.body.removeChild(baitElement);
|
|
59
|
+
|
|
60
|
+
// Método 2: Verificar se bloqueia requests de tracking
|
|
61
|
+
try {
|
|
62
|
+
const testPixel = new Image();
|
|
63
|
+
testPixel.src = '/pixel-test.png?t=' + Date.now();
|
|
64
|
+
testPixel.onload = () => console.log('✅ Pixel não bloqueado');
|
|
65
|
+
testPixel.onerror = () => console.warn('⚠️ Pixel pode estar bloqueado');
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.warn('⚠️ Erro ao testar pixel:', error);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return isBlocked;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Resiliência de Envio ─────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Envia dados com retry automático (exponential backoff)
|
|
77
|
+
*
|
|
78
|
+
* @param {object} data - Dados para enviar
|
|
79
|
+
* @param {string} endpoint - Endpoint de destino
|
|
80
|
+
* @returns {Promise} Promise com resultado
|
|
81
|
+
*/
|
|
82
|
+
async function sendWithRetry(data, endpoint = ANTI_BLOCKING_CONFIG.endpoint) {
|
|
83
|
+
if (!isBrowser) return { success: false, error: 'Not in browser' };
|
|
84
|
+
|
|
85
|
+
let lastError = null;
|
|
86
|
+
|
|
87
|
+
for (let attempt = 0; attempt < ANTI_BLOCKING_CONFIG.maxRetries; attempt++) {
|
|
88
|
+
try {
|
|
89
|
+
// Tenta enviar via fetch
|
|
90
|
+
const response = await fetch(endpoint, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify(data),
|
|
96
|
+
keepalive: true, // Garante envio mesmo se usuário fechar aba
|
|
97
|
+
credentials: 'same-origin', // First-party cookies
|
|
98
|
+
cache: 'no-cache' // Evitar cache de requests de tracking
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (response.ok) {
|
|
102
|
+
console.log(`✅ Envio bem-sucedido (tentativa ${attempt + 1})`);
|
|
103
|
+
return await response.json();
|
|
104
|
+
} else {
|
|
105
|
+
const errorText = await response.text();
|
|
106
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
lastError = error;
|
|
110
|
+
console.warn(`⚠️ Tentativa ${attempt + 1} falhou:`, error.message);
|
|
111
|
+
|
|
112
|
+
// Se não for a última tentativa, aguarda antes de retry
|
|
113
|
+
if (attempt < ANTI_BLOCKING_CONFIG.maxRetries - 1) {
|
|
114
|
+
const delay = ANTI_BLOCKING_CONFIG.retryDelays[attempt];
|
|
115
|
+
console.log(`⏳ Aguardando ${delay}ms antes de retry...`);
|
|
116
|
+
await sleep(delay);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Todas as tentativas falharam
|
|
122
|
+
console.error('❌ Todas as tentativas de envio falharam:', lastError);
|
|
123
|
+
|
|
124
|
+
// Fallback: Beacon API
|
|
125
|
+
if (ANTI_BLOCKING_CONFIG.useBeaconFallback && navigator.sendBeacon) {
|
|
126
|
+
console.log('🔄 Tentando Beacon API como fallback...');
|
|
127
|
+
const beaconSuccess = navigator.sendBeacon(endpoint, JSON.stringify(data));
|
|
128
|
+
|
|
129
|
+
if (beaconSuccess) {
|
|
130
|
+
console.log('✅ Beacon API bem-sucedido');
|
|
131
|
+
return { success: true, method: 'beacon' };
|
|
132
|
+
} else {
|
|
133
|
+
console.error('❌ Beacon API também falhou');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { success: false, error: lastError?.message, attempts: ANTI_BLOCKING_CONFIG.maxRetries };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Função utilitária de delay
|
|
142
|
+
*/
|
|
143
|
+
function sleep(ms) {
|
|
144
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── First-Party Cookies (Ad-Block Proof) ───────────────
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Define cookie first-party (não bloqueado por ad-blockers)
|
|
151
|
+
*
|
|
152
|
+
* @param {string} name - Nome do cookie
|
|
153
|
+
* @param {string} value - Valor do cookie
|
|
154
|
+
* @param {number} maxAge - Tempo de vida em segundos
|
|
155
|
+
*/
|
|
156
|
+
function setFirstPartyCookie(name, value, maxAge = ANTI_BLOCKING_CONFIG.cookieDuration) {
|
|
157
|
+
if (!isBrowser) return;
|
|
158
|
+
|
|
159
|
+
// Extrair domínio atual (para umbrella domain)
|
|
160
|
+
const currentDomain = window.location.hostname;
|
|
161
|
+
const rootDomain = currentDomain.split('.').slice(-2).join('.'); // ex: example.com
|
|
162
|
+
|
|
163
|
+
const cookieOptions = [
|
|
164
|
+
`${name}=${value}`,
|
|
165
|
+
`max-age=${maxAge}`,
|
|
166
|
+
'path=/',
|
|
167
|
+
`domain=.${rootDomain}`, // Umbrella domain para subdomínios
|
|
168
|
+
'SameSite=Lax',
|
|
169
|
+
'Secure'
|
|
170
|
+
].join('; ');
|
|
171
|
+
|
|
172
|
+
document.cookie = cookieOptions;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Obtém cookie first-party
|
|
177
|
+
*
|
|
178
|
+
* @param {string} name - Nome do cookie
|
|
179
|
+
* @returns {string|null} Valor do cookie ou null
|
|
180
|
+
*/
|
|
181
|
+
function getFirstPartyCookie(name) {
|
|
182
|
+
if (!isBrowser) return null;
|
|
183
|
+
|
|
184
|
+
const value = `; ${document.cookie}`;
|
|
185
|
+
const parts = value.split(`; ${name}=`);
|
|
186
|
+
|
|
187
|
+
if (parts.length === 2) {
|
|
188
|
+
return parts.pop().split(';').shift();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ── Lightweight Code (Evitar Patterns de Bloqueio) ─────────
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Verifica se código deve ser minificado (evitar palavras-chave de ad-blockers)
|
|
198
|
+
*
|
|
199
|
+
* @returns {boolean} True se deve minificar
|
|
200
|
+
*/
|
|
201
|
+
function shouldMinifyCode() {
|
|
202
|
+
return ANTI_BLOCKING_CONFIG.minify && isBrowser;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Remove console.logs se configurado (evitar detecção)
|
|
207
|
+
*
|
|
208
|
+
* @param {boolean} remove - Remove console.logs?
|
|
209
|
+
*/
|
|
210
|
+
function configureConsoleLogs(remove = ANTI_BLOCKING_CONFIG.noConsoleLogs) {
|
|
211
|
+
if (!isBrowser || !remove) return;
|
|
212
|
+
|
|
213
|
+
// Sobrescrever console com funções vazias
|
|
214
|
+
const noop = () => {};
|
|
215
|
+
console.log = noop;
|
|
216
|
+
console.warn = noop;
|
|
217
|
+
console.error = noop;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── Same-Domain Protocol (Anti-Adblock) ─────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Verifica se endpoint está no mesmo domínio
|
|
224
|
+
*
|
|
225
|
+
* @param {string} endpoint - Endpoint para verificar
|
|
226
|
+
* @returns {boolean} True se mesmo domínio
|
|
227
|
+
*/
|
|
228
|
+
function isSameDomain(endpoint) {
|
|
229
|
+
if (!isBrowser) return true;
|
|
230
|
+
|
|
231
|
+
const endpointUrl = new URL(endpoint, window.location.href);
|
|
232
|
+
return endpointUrl.hostname === window.location.hostname;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Inicialização ────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Inicializa sistema de anti-blocking
|
|
239
|
+
*/
|
|
240
|
+
function initAntiBlocking() {
|
|
241
|
+
if (!isBrowser) return;
|
|
242
|
+
|
|
243
|
+
console.log('🛡️ Inicializando Anti-Blocking System...');
|
|
244
|
+
|
|
245
|
+
// 1. Detectar ad-blocker
|
|
246
|
+
const adBlockerActive = detectAdBlocker();
|
|
247
|
+
if (adBlockerActive) {
|
|
248
|
+
console.warn('⚠️ Ad-Blocker detectado - usando estratégias de resiliência');
|
|
249
|
+
// Enviar evento de ad-blocker detectado
|
|
250
|
+
if (typeof cdpTrack !== 'undefined' && cdpTrack.track) {
|
|
251
|
+
cdpTrack.track('adblocker_detected', {
|
|
252
|
+
user_agent: navigator.userAgent,
|
|
253
|
+
timestamp: Date.now()
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 2. Configurar first-party cookies
|
|
259
|
+
const userId = getFirstPartyCookie('_cdp_uid');
|
|
260
|
+
if (!userId) {
|
|
261
|
+
const newUserId = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
262
|
+
setFirstPartyCookie('_cdp_uid', newUserId);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 3. Configurar console logs (se necessário)
|
|
266
|
+
if (ANTI_BLOCKING_CONFIG.noConsoleLogs) {
|
|
267
|
+
configureConsoleLogs(true);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log('✅ Anti-Blocking System inicializado');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── Exportações ────────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
export {
|
|
276
|
+
sendWithRetry,
|
|
277
|
+
detectAdBlocker,
|
|
278
|
+
setFirstPartyCookie,
|
|
279
|
+
getFirstPartyCookie,
|
|
280
|
+
isSameDomain,
|
|
281
|
+
configureConsoleLogs,
|
|
282
|
+
shouldMinifyCode,
|
|
283
|
+
ANTI_BLOCKING_CONFIG,
|
|
284
|
+
initAntiBlocking
|
|
285
|
+
};
|