cdp-edge 2.0.0 → 2.0.2
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/contracts/api-versions.json +12 -8
- package/dist/commands/install.js +1 -2
- package/dist/commands/setup.js +1 -2
- package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +23 -23
- package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +172 -72
- package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +20 -0
- package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +48 -16
- package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +7 -7
- package/extracted-skill/tracking-events-generator/agents/database-agent.md +8 -8
- package/extracted-skill/tracking-events-generator/agents/debug-agent.md +13 -13
- package/extracted-skill/tracking-events-generator/agents/devops-agent.md +31 -7
- package/extracted-skill/tracking-events-generator/agents/email-agent.md +27 -0
- package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +205 -0
- package/extracted-skill/tracking-events-generator/agents/google-agent.md +118 -0
- package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +90 -4
- package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +8 -641
- package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +108 -0
- package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +1 -1
- package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +68 -8
- package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +61 -18
- package/extracted-skill/tracking-events-generator/agents/memory-agent.md +98 -0
- package/extracted-skill/tracking-events-generator/agents/performance-agent.md +29 -19
- package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +11 -1
- package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +137 -28
- package/extracted-skill/tracking-events-generator/agents/server-tracking.md +7 -8
- package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +63 -0
- package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +100 -5
- package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +100 -0
- package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +58 -5
- package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +16 -16
- package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +140 -25
- package/extracted-skill/tracking-events-generator/contracts/api-versions.json +12 -8
- package/package.json +2 -2
- package/server-edge-tracker/worker.js +53 -8
|
@@ -212,10 +212,14 @@
|
|
|
212
212
|
]
|
|
213
213
|
},
|
|
214
214
|
"conversions_api": {
|
|
215
|
-
"current": "
|
|
216
|
-
"minimum_supported": "
|
|
217
|
-
"recommended": "
|
|
218
|
-
"endpoint_pattern": "https://api.linkedin.com/rest/
|
|
215
|
+
"current": "202401",
|
|
216
|
+
"minimum_supported": "202401",
|
|
217
|
+
"recommended": "202401",
|
|
218
|
+
"endpoint_pattern": "https://api.linkedin.com/rest/conversionEvents",
|
|
219
|
+
"required_headers": {
|
|
220
|
+
"LinkedIn-Version": "202401",
|
|
221
|
+
"X-Restli-Protocol-Version": "2.0.0"
|
|
222
|
+
},
|
|
219
223
|
"authentication": "Bearer token (LINKEDIN_ACCESS_TOKEN)",
|
|
220
224
|
"rate_limits": {
|
|
221
225
|
"requests_per_second": 10,
|
|
@@ -359,10 +363,10 @@
|
|
|
359
363
|
},
|
|
360
364
|
|
|
361
365
|
"last_updated_by": {
|
|
362
|
-
"agent": "
|
|
363
|
-
"session_id": "CDP_2026-
|
|
364
|
-
"timestamp": "2026-
|
|
366
|
+
"agent": "Audit — CDP Edge v2.0",
|
|
367
|
+
"session_id": "CDP_2026-04-10_audit",
|
|
368
|
+
"timestamp": "2026-04-10T00:00:00.000Z"
|
|
365
369
|
},
|
|
366
370
|
|
|
367
|
-
"next_review_date": "2026-
|
|
371
|
+
"next_review_date": "2026-05-10T00:00:00.000Z"
|
|
368
372
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -32,8 +32,7 @@ function printBanner() {
|
|
|
32
32
|
console.log(chalk.cyan('╚██████╗██████╔╝██║ ███████╗██████╔╝╚██████╔╝███████╗'));
|
|
33
33
|
console.log(chalk.cyan(' ╚═════╝╚═════╝ ╚═╝ ╚══════╝╚═════╝ ╚═════╝╚══════╝'));
|
|
34
34
|
console.log('');
|
|
35
|
-
console.log(chalk.gray(' Customer Data Platform on the Edge ·
|
|
36
|
-
console.log(chalk.gray(` Installer v2.0.0`));
|
|
35
|
+
console.log(chalk.gray(' Customer Data Platform on the Edge · Global Edge Tracking · v2.0.2'));
|
|
37
36
|
console.log('');
|
|
38
37
|
console.log(chalk.gray('═'.repeat(68)));
|
|
39
38
|
console.log('');
|
package/dist/commands/setup.js
CHANGED
|
@@ -19,8 +19,7 @@ function printBanner() {
|
|
|
19
19
|
console.log(chalk.cyan('╚██████╗██████╔╝██║ ███████╗██████╔╝╚██████╔╝███████╗'));
|
|
20
20
|
console.log(chalk.cyan(' ╚═════╝╚═════╝ ╚═╝ ╚══════╝╚═════╝ ╚═════╝╚══════╝'));
|
|
21
21
|
console.log('');
|
|
22
|
-
console.log(chalk.gray(' Customer Data Platform on the Edge ·
|
|
23
|
-
console.log(chalk.gray(' Setup Wizard v2.0.0'));
|
|
22
|
+
console.log(chalk.gray(' Customer Data Platform on the Edge · Global Edge Tracking · v2.0.2'));
|
|
24
23
|
console.log('');
|
|
25
24
|
console.log(chalk.gray('═'.repeat(68)));
|
|
26
25
|
console.log('');
|
|
@@ -264,7 +264,7 @@ function wShapeAttribution(touchpoints) {
|
|
|
264
264
|
|
|
265
265
|
```javascript
|
|
266
266
|
// Modelo Data-Driven simplificado
|
|
267
|
-
async function dataDrivenAttribution(touchpoints, userJourneyHistory) {
|
|
267
|
+
async function dataDrivenAttribution(touchpoints, userJourneyHistory, env) {
|
|
268
268
|
if (!touchpoints || touchpoints.length === 0) return [];
|
|
269
269
|
|
|
270
270
|
// 1. Calcular pesos baseados em dados históricos
|
|
@@ -297,14 +297,14 @@ async function dataDrivenAttribution(touchpoints, userJourneyHistory) {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
// Calcular peso de canal baseado em conversão histórica
|
|
300
|
-
async function calculateChannelWeights(touchpoints, history) {
|
|
300
|
+
async function calculateChannelWeights(touchpoints, history, env) {
|
|
301
301
|
const weights = {};
|
|
302
302
|
|
|
303
303
|
for (const tp of touchpoints) {
|
|
304
304
|
const channel = tp.utm_source;
|
|
305
305
|
|
|
306
306
|
// Buscar conversões históricas deste canal
|
|
307
|
-
const historicalConversions = await DB.prepare(`
|
|
307
|
+
const historicalConversions = await env.DB.prepare(`
|
|
308
308
|
SELECT
|
|
309
309
|
COUNT(*) as total_conversions,
|
|
310
310
|
AVG(value) as avg_value
|
|
@@ -325,14 +325,14 @@ async function calculateChannelWeights(touchpoints, history) {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
// Calcular peso de posição baseado em conversão histórica
|
|
328
|
-
async function calculatePositionWeights(touchpoints, history) {
|
|
328
|
+
async function calculatePositionWeights(touchpoints, history, env) {
|
|
329
329
|
const weights = {};
|
|
330
330
|
|
|
331
331
|
for (const tp of touchpoints) {
|
|
332
332
|
const position = tp.position; // 0 = first, 1 = second, etc.
|
|
333
333
|
|
|
334
334
|
// Buscar conversões históricas nesta posição
|
|
335
|
-
const historicalConversions = await DB.prepare(`
|
|
335
|
+
const historicalConversions = await env.DB.prepare(`
|
|
336
336
|
SELECT
|
|
337
337
|
COUNT(*) as total_conversions
|
|
338
338
|
FROM multi_touch_attribution
|
|
@@ -458,7 +458,7 @@ CREATE INDEX IF NOT EXISTS idx_channel_model ON channel_performance(attribution_
|
|
|
458
458
|
|
|
459
459
|
```javascript
|
|
460
460
|
// Capturar touchpoint da jornada
|
|
461
|
-
export async function captureTouchpoint(eventData, request) {
|
|
461
|
+
export async function captureTouchpoint(eventData, request, env) {
|
|
462
462
|
const {
|
|
463
463
|
user_id,
|
|
464
464
|
session_id,
|
|
@@ -485,7 +485,7 @@ export async function captureTouchpoint(eventData, request) {
|
|
|
485
485
|
const position = await calculateJourneyPosition(user_id, event_timestamp);
|
|
486
486
|
|
|
487
487
|
// Persistir touchpoint no D1
|
|
488
|
-
await DB.prepare(`
|
|
488
|
+
await env.DB.prepare(`
|
|
489
489
|
INSERT INTO user_journeys
|
|
490
490
|
(user_id, session_id, email, event_id, event_name,
|
|
491
491
|
utm_source, utm_medium, utm_campaign, utm_content, utm_term,
|
|
@@ -512,8 +512,8 @@ export async function captureTouchpoint(eventData, request) {
|
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
// Calcular posição na jornada
|
|
515
|
-
async function calculateJourneyPosition(userId, eventTimestamp) {
|
|
516
|
-
const result = await DB.prepare(`
|
|
515
|
+
async function calculateJourneyPosition(userId, eventTimestamp, env) {
|
|
516
|
+
const result = await env.DB.prepare(`
|
|
517
517
|
SELECT COUNT(*) as position
|
|
518
518
|
FROM user_journeys
|
|
519
519
|
WHERE user_id = ? AND event_timestamp < ?
|
|
@@ -523,7 +523,7 @@ async function calculateJourneyPosition(userId, eventTimestamp) {
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
// Agendar cálculo de atribuição (via Cloudflare Queue)
|
|
526
|
-
async function scheduleAttributionCalculation(email, conversionId, eventName) {
|
|
526
|
+
async function scheduleAttributionCalculation(email, conversionId, eventName, env) {
|
|
527
527
|
await QUEUE.send('cdp-edge-attribution', {
|
|
528
528
|
type: 'CALCULATE_ATTRIBUTION',
|
|
529
529
|
email,
|
|
@@ -538,7 +538,7 @@ async function scheduleAttributionCalculation(email, conversionId, eventName) {
|
|
|
538
538
|
|
|
539
539
|
```javascript
|
|
540
540
|
// Calcular atribuição multi-touch
|
|
541
|
-
export async function calculateMultiTouchAttribution(conversionData) {
|
|
541
|
+
export async function calculateMultiTouchAttribution(conversionData, env) {
|
|
542
542
|
const {
|
|
543
543
|
email,
|
|
544
544
|
conversion_id,
|
|
@@ -548,7 +548,7 @@ export async function calculateMultiTouchAttribution(conversionData) {
|
|
|
548
548
|
} = conversionData;
|
|
549
549
|
|
|
550
550
|
// 1. Buscar jornada completa do usuário
|
|
551
|
-
const journey = await DB.prepare(`
|
|
551
|
+
const journey = await env.DB.prepare(`
|
|
552
552
|
SELECT
|
|
553
553
|
user_id,
|
|
554
554
|
session_id,
|
|
@@ -594,7 +594,7 @@ export async function calculateMultiTouchAttribution(conversionData) {
|
|
|
594
594
|
// 3. Persistir atribuição para cada modelo
|
|
595
595
|
for (const [modelName, attribution] of Object.entries(attributionModels)) {
|
|
596
596
|
for (const touchpoint of attribution) {
|
|
597
|
-
await DB.prepare(`
|
|
597
|
+
await env.DB.prepare(`
|
|
598
598
|
INSERT OR REPLACE INTO multi_touch_attribution
|
|
599
599
|
(conversion_id, user_id, email, attribution_model, touchpoint_index,
|
|
600
600
|
utm_source, utm_medium, utm_campaign, event_name, event_timestamp,
|
|
@@ -628,7 +628,7 @@ export async function calculateMultiTouchAttribution(conversionData) {
|
|
|
628
628
|
}
|
|
629
629
|
|
|
630
630
|
// Atualizar performance de canal
|
|
631
|
-
async function updateChannelPerformance(attributionModels, value, currency) {
|
|
631
|
+
async function updateChannelPerformance(attributionModels, value, currency, env) {
|
|
632
632
|
const today = new Date().toISOString().split('T')[0];
|
|
633
633
|
|
|
634
634
|
for (const [modelName, attribution] of Object.entries(attributionModels)) {
|
|
@@ -660,7 +660,7 @@ async function updateChannelPerformance(attributionModels, value, currency) {
|
|
|
660
660
|
|
|
661
661
|
// Atualizar tabela de performance
|
|
662
662
|
for (const perf of Object.values(channelPerformance)) {
|
|
663
|
-
await DB.prepare(`
|
|
663
|
+
await env.DB.prepare(`
|
|
664
664
|
INSERT OR REPLACE INTO channel_performance
|
|
665
665
|
(utm_source, utm_medium, utm_campaign, attribution_model,
|
|
666
666
|
total_attribution, total_conversions, total_value, avg_conversion_value, date)
|
|
@@ -685,7 +685,7 @@ async function updateChannelPerformance(attributionModels, value, currency) {
|
|
|
685
685
|
|
|
686
686
|
```javascript
|
|
687
687
|
// Enviar Purchase com atribuição calculada
|
|
688
|
-
export async function sendPurchaseWithAttribution(conversionData, attributionModel = 'U_SHAPE') {
|
|
688
|
+
export async function sendPurchaseWithAttribution(conversionData, env, attributionModel = 'U_SHAPE') {
|
|
689
689
|
const {
|
|
690
690
|
email,
|
|
691
691
|
conversion_id,
|
|
@@ -695,7 +695,7 @@ export async function sendPurchaseWithAttribution(conversionData, attributionMod
|
|
|
695
695
|
} = conversionData;
|
|
696
696
|
|
|
697
697
|
// 1. Buscar atribuição calculada
|
|
698
|
-
const attribution = await DB.prepare(`
|
|
698
|
+
const attribution = await env.DB.prepare(`
|
|
699
699
|
SELECT
|
|
700
700
|
utm_source,
|
|
701
701
|
utm_medium,
|
|
@@ -773,7 +773,7 @@ async function sendMetaPurchaseWithAttribution(purchaseData, attribution) {
|
|
|
773
773
|
const response = await fetch('https://graph.facebook.com/v22.0/events', {
|
|
774
774
|
method: 'POST',
|
|
775
775
|
headers: {
|
|
776
|
-
'Authorization': `Bearer ${META_ACCESS_TOKEN}`,
|
|
776
|
+
'Authorization': `Bearer ${env.META_ACCESS_TOKEN}`,
|
|
777
777
|
'Content-Type': 'application/json'
|
|
778
778
|
},
|
|
779
779
|
body: JSON.stringify({ data: [payload] })
|
|
@@ -829,7 +829,7 @@ async function sendTikTokPurchaseWithAttribution(purchaseData, attribution) {
|
|
|
829
829
|
const response = await fetch('https://business-api.tiktok.com/open_api/v1.3/pixel/conversion/', {
|
|
830
830
|
method: 'POST',
|
|
831
831
|
headers: {
|
|
832
|
-
'Authorization': `Bearer ${TIKTOK_ACCESS_TOKEN}`,
|
|
832
|
+
'Authorization': `Bearer ${env.TIKTOK_ACCESS_TOKEN}`,
|
|
833
833
|
'Content-Type': 'application/json'
|
|
834
834
|
},
|
|
835
835
|
body: JSON.stringify(payload)
|
|
@@ -1036,7 +1036,7 @@ export async function getAttributionForConversion(request, env) {
|
|
|
1036
1036
|
}
|
|
1037
1037
|
|
|
1038
1038
|
// Buscar atribuição calculada
|
|
1039
|
-
const attribution = await DB.prepare(`
|
|
1039
|
+
const attribution = await env.DB.prepare(`
|
|
1040
1040
|
SELECT
|
|
1041
1041
|
utm_source,
|
|
1042
1042
|
utm_medium,
|
|
@@ -1052,7 +1052,7 @@ export async function getAttributionForConversion(request, env) {
|
|
|
1052
1052
|
`).bind(conversionId, model).all();
|
|
1053
1053
|
|
|
1054
1054
|
// Buscar dados da conversão
|
|
1055
|
-
const conversion = await DB.prepare(`
|
|
1055
|
+
const conversion = await env.DB.prepare(`
|
|
1056
1056
|
SELECT
|
|
1057
1057
|
value,
|
|
1058
1058
|
currency,
|
|
@@ -1096,7 +1096,7 @@ export async function compareAttributionModels(request, env) {
|
|
|
1096
1096
|
const comparison = {};
|
|
1097
1097
|
|
|
1098
1098
|
for (const model of ATTRIBUTION_CONFIG.available_models) {
|
|
1099
|
-
const attribution = await DB.prepare(`
|
|
1099
|
+
const attribution = await env.DB.prepare(`
|
|
1100
1100
|
SELECT
|
|
1101
1101
|
utm_source,
|
|
1102
1102
|
credit_percentage
|
|
@@ -1136,7 +1136,7 @@ export async function getChannelPerformance(request, env) {
|
|
|
1136
1136
|
const days = parseInt(url.searchParams.get('days') || '30');
|
|
1137
1137
|
const groupBy = url.searchParams.get('group_by') || 'source'; // 'source' ou 'campaign'
|
|
1138
1138
|
|
|
1139
|
-
const performance = await DB.prepare(`
|
|
1139
|
+
const performance = await env.DB.prepare(`
|
|
1140
1140
|
SELECT
|
|
1141
1141
|
${groupBy === 'source' ? 'utm_source' : 'utm_campaign'} as group_by,
|
|
1142
1142
|
SUM(total_attribution) as total_attribution,
|
|
@@ -109,99 +109,199 @@ Implementado via `anti-blocking.js`:
|
|
|
109
109
|
|
|
110
110
|
## 💻 EXEMPLO DE CÓDIGO GERADO
|
|
111
111
|
|
|
112
|
-
### `cdpTrack.js` (SDK Principal)
|
|
112
|
+
### `cdpTrack.js` (SDK Principal — Padrão Multi-Plataforma)
|
|
113
|
+
|
|
114
|
+
> Este é o padrão canônico do cdpTrack SDK para Meta, TikTok e GA4.
|
|
115
|
+
> Cada agente de plataforma injeta seus eventos neste SDK via Browser Tracking Agent.
|
|
113
116
|
|
|
114
117
|
```javascript
|
|
115
118
|
/**
|
|
116
119
|
* cdpTrack SDK - CDP Edge Quantum Tier
|
|
117
120
|
* Browser Tracking SDK Principal
|
|
121
|
+
* Suporta: Meta Pixel, TikTok Pixel, GA4, Pinterest Tag, Reddit Pixel, Spotify Pixel
|
|
118
122
|
*/
|
|
119
123
|
|
|
120
|
-
(function(w
|
|
121
|
-
|
|
122
|
-
w._pbq.push = w._pbq.push || [];
|
|
123
|
-
w._spotify = w._spotify || {};
|
|
124
|
-
|
|
125
|
-
// Carregar configuração
|
|
126
|
-
const config = window.cdpTrack?.config || {};
|
|
124
|
+
(function(w) {
|
|
125
|
+
'use strict';
|
|
127
126
|
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
// ──────────────────────────────────────────────
|
|
128
|
+
// CORE: Geração de event_id único (deduplicação)
|
|
129
|
+
// O mesmo event_id deve ser usado no browser E no servidor (CAPI)
|
|
130
|
+
// ──────────────────────────────────────────────
|
|
131
|
+
function generateEventId() {
|
|
132
|
+
return 'evt_' + Date.now() + '_' + Math.random().toString(36).substring(2, 11);
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
135
|
+
// ──────────────────────────────────────────────
|
|
136
|
+
// CORE: Envio para Cloudflare Worker (same-domain)
|
|
137
|
+
// Usa /track no mesmo domínio — imune a ad-blockers
|
|
138
|
+
// ──────────────────────────────────────────────
|
|
139
|
+
async function sendToWorker(eventName, payload) {
|
|
140
|
+
const eventId = generateEventId();
|
|
141
|
+
|
|
142
|
+
const body = {
|
|
143
|
+
event: eventName,
|
|
144
|
+
event_id: eventId, // CRÍTICO: mesmo ID usado nas CAPIs
|
|
145
|
+
url: window.location.href,
|
|
146
|
+
referrer: document.referrer,
|
|
147
|
+
timestamp: Date.now(),
|
|
148
|
+
...payload
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Tentativa primária: fetch
|
|
153
|
+
await fetch('/track', {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
body: JSON.stringify(body),
|
|
157
|
+
keepalive: true
|
|
152
158
|
});
|
|
159
|
+
} catch (_) {
|
|
160
|
+
// Fallback: Beacon API (funciona mesmo no unload da página)
|
|
161
|
+
navigator.sendBeacon('/track', JSON.stringify(body));
|
|
153
162
|
}
|
|
154
|
-
};
|
|
155
163
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
w._spotify.trackEvent('ViewContent', {
|
|
159
|
-
content_name: contentName,
|
|
160
|
-
content_id: contentId,
|
|
161
|
-
...params
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
+
return eventId;
|
|
165
|
+
}
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
};
|
|
167
|
+
// ──────────────────────────────────────────────
|
|
168
|
+
// CORE: Captura de cookies first-party
|
|
169
|
+
// ──────────────────────────────────────────────
|
|
170
|
+
function getCookie(name) {
|
|
171
|
+
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
|
172
|
+
return match ? decodeURIComponent(match[2]) : null;
|
|
173
|
+
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
// ──────────────────────────────────────────────
|
|
176
|
+
// CORE: Captura de click IDs da URL (Meta, TikTok, Google)
|
|
177
|
+
// ──────────────────────────────────────────────
|
|
178
|
+
function getClickIds() {
|
|
179
|
+
const params = new URLSearchParams(window.location.search);
|
|
180
|
+
return {
|
|
181
|
+
fbclid: params.get('fbclid') || getCookie('fbclid') || null,
|
|
182
|
+
ttclid: params.get('ttclid') || getCookie('ttclid') || null,
|
|
183
|
+
gclid: params.get('gclid') || null,
|
|
184
|
+
gbraid: params.get('gbraid') || null,
|
|
185
|
+
wbraid: params.get('wbraid') || null,
|
|
186
|
+
fbp: getCookie('_fbp') || null,
|
|
187
|
+
fbc: getCookie('_fbc') || null,
|
|
188
|
+
ttp: getCookie('_ttp') || null,
|
|
189
|
+
uid: getCookie('_cdp_uid') || null // Identity Graph first-party cookie
|
|
190
|
+
};
|
|
191
|
+
}
|
|
183
192
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
// ──────────────────────────────────────────────
|
|
194
|
+
// API PÚBLICA
|
|
195
|
+
// ──────────────────────────────────────────────
|
|
196
|
+
const cdpTrack = {
|
|
197
|
+
generateEventId,
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Rastrear evento genérico — enviado para o Worker
|
|
201
|
+
* O Worker despacha para Meta CAPI, GA4 MP, TikTok Events API etc.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} eventName - Nome do evento (ex: 'Lead', 'Purchase', 'PageView')
|
|
204
|
+
* @param {Object} params - Parâmetros adicionais do evento
|
|
205
|
+
*/
|
|
206
|
+
track(eventName, params = {}) {
|
|
207
|
+
const clickIds = getClickIds();
|
|
208
|
+
return sendToWorker(eventName, { ...clickIds, ...params });
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Rastrear Lead (captura de formulário)
|
|
213
|
+
* Enviar dados PII crus — o Worker faz SHA-256 no servidor
|
|
214
|
+
*/
|
|
215
|
+
trackLead(userData = {}) {
|
|
216
|
+
const clickIds = getClickIds();
|
|
217
|
+
return sendToWorker('Lead', {
|
|
218
|
+
...clickIds,
|
|
219
|
+
email: userData.email || null, // Worker aplica SHA-256
|
|
220
|
+
phone: userData.phone || null, // Worker aplica E.164 + SHA-256
|
|
221
|
+
first_name: userData.first_name || null,
|
|
222
|
+
last_name: userData.last_name || null,
|
|
223
|
+
city: userData.city || null,
|
|
224
|
+
state: userData.state || null,
|
|
225
|
+
zip: userData.zip || null,
|
|
226
|
+
country: userData.country || 'BR'
|
|
227
|
+
});
|
|
228
|
+
},
|
|
192
229
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Rastrear Purchase (confirmação de compra)
|
|
232
|
+
*/
|
|
233
|
+
trackPurchase(orderData = {}) {
|
|
234
|
+
const clickIds = getClickIds();
|
|
235
|
+
return sendToWorker('Purchase', {
|
|
236
|
+
...clickIds,
|
|
237
|
+
value: orderData.value || 0,
|
|
238
|
+
currency: orderData.currency || 'BRL',
|
|
239
|
+
order_id: orderData.order_id || null,
|
|
240
|
+
content_name: orderData.product || null
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Rastrear PageView — chamar no load da página
|
|
246
|
+
*/
|
|
247
|
+
trackPageView() {
|
|
248
|
+
const clickIds = getClickIds();
|
|
249
|
+
return sendToWorker('PageView', { ...clickIds });
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Rastrear InitiateCheckout
|
|
254
|
+
*/
|
|
255
|
+
trackInitiateCheckout(checkoutData = {}) {
|
|
256
|
+
const clickIds = getClickIds();
|
|
257
|
+
return sendToWorker('InitiateCheckout', {
|
|
258
|
+
...clickIds,
|
|
259
|
+
value: checkoutData.value || 0,
|
|
260
|
+
currency: checkoutData.currency || 'BRL'
|
|
261
|
+
});
|
|
262
|
+
}
|
|
200
263
|
};
|
|
201
264
|
|
|
202
|
-
|
|
265
|
+
// Expor no window
|
|
266
|
+
w.cdpTrack = cdpTrack;
|
|
267
|
+
|
|
268
|
+
// Auto page_view no load
|
|
269
|
+
if (document.readyState === 'loading') {
|
|
270
|
+
document.addEventListener('DOMContentLoaded', () => cdpTrack.trackPageView());
|
|
271
|
+
} else {
|
|
272
|
+
cdpTrack.trackPageView();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
})(window);
|
|
203
276
|
```
|
|
204
277
|
|
|
278
|
+
### Uso típico no HTML do cliente
|
|
279
|
+
|
|
280
|
+
```html
|
|
281
|
+
<!-- 1. Carregar o SDK -->
|
|
282
|
+
<script src="/tracking/cdpTrack.js"></script>
|
|
283
|
+
|
|
284
|
+
<!-- 2. Rastrear lead ao submeter formulário -->
|
|
285
|
+
<script>
|
|
286
|
+
document.getElementById('lead-form').addEventListener('submit', function(e) {
|
|
287
|
+
cdpTrack.trackLead({
|
|
288
|
+
email: document.getElementById('email').value,
|
|
289
|
+
phone: document.getElementById('phone').value,
|
|
290
|
+
first_name: document.getElementById('name').value
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
</script>
|
|
294
|
+
|
|
295
|
+
<!-- 3. Rastrear checkout (botão de compra) -->
|
|
296
|
+
<script>
|
|
297
|
+
document.getElementById('buy-btn').addEventListener('click', function() {
|
|
298
|
+
cdpTrack.trackInitiateCheckout({ value: 97.00, currency: 'BRL' });
|
|
299
|
+
});
|
|
300
|
+
</script>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
> **Nota:** O Worker recebe os dados crus, aplica SHA-256, e despacha para Meta CAPI v22.0, GA4 MP, TikTok Events API v1.3 e demais plataformas configuradas — tudo em paralelo via `Promise.allSettled`.
|
|
304
|
+
|
|
205
305
|
---
|
|
206
306
|
|
|
207
307
|
## 🔧 INTEGRAÇÃO COM OUTROS AGENTES
|
|
@@ -2044,6 +2044,26 @@ async function handleGetCurrentPolicy(request, env) {
|
|
|
2044
2044
|
- **security-enterprise-agent.md**: Usa encryption de PII para LGPD/GDPR
|
|
2045
2045
|
- **attribution-agent.md**: Respeita consentimento para analytics e marketing
|
|
2046
2046
|
- **master-orchestrator.md**: Implementa middleware de consentimento antes de tracking
|
|
2047
|
+
- **validator-agent.md**: O Compliance Agent DEVE ser consultado pelo Validator Agent para verificar conformidade de PII. Fluxo:
|
|
2048
|
+
|
|
2049
|
+
```
|
|
2050
|
+
validator-agent (auditoria do código gerado)
|
|
2051
|
+
└─► checkComplianceCompliance(generatedCode)
|
|
2052
|
+
├─ Verificar: SHA-256 aplicado em todos os campos PII (em, ph, fn, ln)?
|
|
2053
|
+
├─ Verificar: Google Consent Mode v2 implementado (ad_storage, analytics_storage)?
|
|
2054
|
+
├─ Verificar: url_passthrough: true ativo?
|
|
2055
|
+
├─ Verificar: PII nunca logada em plaintext?
|
|
2056
|
+
└─ Verificar: opt-in explícito antes de tracking de marketing?
|
|
2057
|
+
|
|
2058
|
+
// No validator-agent, chamar:
|
|
2059
|
+
const complianceCheck = await validateComplianceRequirements(generatedBrowserCode, generatedWorkerCode);
|
|
2060
|
+
if (!complianceCheck.passed) {
|
|
2061
|
+
// Bloquear entrega e reportar issues ao Master Orchestrator
|
|
2062
|
+
return { status: 'BLOCKED', issues: complianceCheck.issues };
|
|
2063
|
+
}
|
|
2064
|
+
```
|
|
2065
|
+
|
|
2066
|
+
- **google-agent.md**: Consent Mode v2 gerado por este agente valida conformidade detectada pelo Compliance Agent
|
|
2047
2067
|
|
|
2048
2068
|
---
|
|
2049
2069
|
|