cdp-edge 2.5.9 → 2.6.1
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 +247 -211
- package/bin/cdp-edge.js +1 -0
- package/contracts/agent-versions.json +2 -2
- package/dist/commands/infra.js +1 -1
- package/dist/commands/server.js +38 -33
- package/dist/commands/setup.js +3 -0
- package/dist/commands/validate.js +251 -236
- package/dist/sdk/cdpTrack.js +6 -4
- package/dist/sdk/cdpTrack.min.js +4 -4
- package/dist/sdk/install-snippet.html +1 -1
- package/extracted-skill/tracking-events-generator/INTEGRACAO-COMPLETA.md +4 -4
- package/extracted-skill/tracking-events-generator/Premium-Tracking-Intelligence-Resumo.md +3 -3
- package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +78 -33
- package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +562 -93
- package/extracted-skill/tracking-events-generator/integration-test.js +3 -3
- package/extracted-skill/tracking-events-generator/knowledge-base.md +12 -12
- package/extracted-skill/tracking-events-generator/models/checkout-proprio.md +1 -1
- package/extracted-skill/tracking-events-generator/models/multi-step-checkout.md +4 -4
- package/extracted-skill/tracking-events-generator/models/reddit/conversions-api-template.js +1 -1
- package/extracted-skill/tracking-events-generator/models/scenarios/behavior-engine.js +1 -1
- package/extracted-skill/tracking-events-generator/models/scenarios/sales-page-logic.md +1 -1
- package/extracted-skill/tracking-events-generator/models/trafego-direto.md +7 -7
- package/package.json +2 -2
- package/server-edge-tracker/.client.env.example +5 -0
- package/server-edge-tracker/deploy-client.cjs +47 -31
- package/server-edge-tracker/index.ts +1267 -1204
- package/server-edge-tracker/modules/db.ts +2 -2
- package/server-edge-tracker/modules/dispatch/meta.ts +3 -0
- package/server-edge-tracker/modules/dispatch/tiktok.ts +1 -0
- package/server-edge-tracker/modules/dispatch/whatsapp.ts +5 -2
- package/server-edge-tracker/modules/utils.ts +1 -1
- package/server-edge-tracker/types.ts +3 -0
- package/server-edge-tracker/wrangler.toml +2 -0
- package/templates/checkout-proprio.md +1 -1
- package/templates/install/CLAUDE.md +1 -1
- package/templates/multi-step-checkout.md +4 -4
- package/templates/reddit/conversions-api-template.js +1 -1
- package/templates/scenarios/behavior-engine.js +1 -1
- package/templates/scenarios/sales-page-logic.md +1 -1
- package/templates/trafego-direto.md +7 -7
- package/templates/vsl-page.md +2 -2
- package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +0 -707
- package/extracted-skill/tracking-events-generator/agents/zapman-agent.md +0 -189
- package/server-edge-tracker/.client.env +0 -5
- package/server-edge-tracker/dist-check/README.md +0 -1
- package/server-edge-tracker/dist-check/index.js +0 -5164
- package/server-edge-tracker/dist-check/index.js.map +0 -8
|
@@ -55,7 +55,7 @@ export async function saveLead(env: Env, eventName: string, payload: TrackPayloa
|
|
|
55
55
|
city, state, country,
|
|
56
56
|
fbp, fbc, userId,
|
|
57
57
|
utmSource, utmMedium, utmCampaign, utmContent, utmTerm,
|
|
58
|
-
pageUrl, value, currency, eventId, botScore,
|
|
58
|
+
pageUrl, value, currency, eventId, event_id, botScore,
|
|
59
59
|
engagementScore, intentionLevel, utmRestored,
|
|
60
60
|
} = payload;
|
|
61
61
|
|
|
@@ -69,7 +69,7 @@ export async function saveLead(env: Env, eventName: string, payload: TrackPayloa
|
|
|
69
69
|
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,datetime('now'))
|
|
70
70
|
`).bind(
|
|
71
71
|
eventName,
|
|
72
|
-
eventId || null,
|
|
72
|
+
eventId || event_id || null,
|
|
73
73
|
email || null,
|
|
74
74
|
normalizePhone(phone) || null,
|
|
75
75
|
firstName || null,
|
|
@@ -15,6 +15,9 @@ interface EnrichedPayload {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export async function sendMetaCapi(env: Env, eventName: string, payload: TrackPayload, request: Request | null, ctx: ExecutionContext | null): Promise<any> {
|
|
18
|
+
if (env.DISABLE_EXTERNAL_DISPATCH === '1') return { skipped: 'external dispatch disabled' };
|
|
19
|
+
if (!env.META_ACCESS_TOKEN) return { skipped: 'META_ACCESS_TOKEN not set' };
|
|
20
|
+
|
|
18
21
|
// Auto-enriquecer payload com dados do Identity Graph antes do envio
|
|
19
22
|
let recovered = { email: false, utm: false };
|
|
20
23
|
if (env.DB && payload) {
|
|
@@ -9,6 +9,7 @@ import { Env, TrackPayload } from '../../types.js';
|
|
|
9
9
|
import { ExecutionContext } from '@cloudflare/workers-types';
|
|
10
10
|
|
|
11
11
|
export async function sendTikTokApi(env: Env, eventName: string, payload: TrackPayload, request: Request | null, ctx: ExecutionContext | null): Promise<any> {
|
|
12
|
+
if (env.DISABLE_EXTERNAL_DISPATCH === '1') return { skipped: 'external dispatch disabled' };
|
|
12
13
|
if (!env.TIKTOK_ACCESS_TOKEN) return { skipped: 'TIKTOK_ACCESS_TOKEN not set' };
|
|
13
14
|
|
|
14
15
|
const pixelId = env.TIKTOK_PIXEL_ID;
|
|
@@ -264,6 +264,9 @@ export async function processWhatsAppWebhook(env: Env, body: any, request: Reque
|
|
|
264
264
|
export async function verifyHmac(secret: string, rawBody: string, receivedSignature: string): Promise<boolean> {
|
|
265
265
|
if (!secret || !receivedSignature) return false;
|
|
266
266
|
try {
|
|
267
|
+
const normalizedSignature = receivedSignature.startsWith('sha256=')
|
|
268
|
+
? receivedSignature.slice('sha256='.length)
|
|
269
|
+
: receivedSignature;
|
|
267
270
|
const key = await crypto.subtle.importKey(
|
|
268
271
|
'raw',
|
|
269
272
|
new TextEncoder().encode(secret),
|
|
@@ -273,10 +276,10 @@ export async function verifyHmac(secret: string, rawBody: string, receivedSignat
|
|
|
273
276
|
);
|
|
274
277
|
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(rawBody));
|
|
275
278
|
const computed = Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
276
|
-
if (computed.length !==
|
|
279
|
+
if (computed.length !== normalizedSignature.length) return false;
|
|
277
280
|
let diff = 0;
|
|
278
281
|
for (let i = 0; i < computed.length; i++) {
|
|
279
|
-
diff |= computed.charCodeAt(i) ^
|
|
282
|
+
diff |= computed.charCodeAt(i) ^ normalizedSignature.toLowerCase().charCodeAt(i);
|
|
280
283
|
}
|
|
281
284
|
return diff === 0;
|
|
282
285
|
} catch {
|
|
@@ -36,7 +36,7 @@ export function corsHeaders(origin: string | null, siteDomain: string | null): R
|
|
|
36
36
|
return {
|
|
37
37
|
'Access-Control-Allow-Origin': allowed || '*',
|
|
38
38
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
39
|
-
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With',
|
|
39
|
+
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With, Authorization',
|
|
40
40
|
'Access-Control-Max-Age': '86400',
|
|
41
41
|
};
|
|
42
42
|
}
|
|
@@ -30,11 +30,14 @@ export interface Env {
|
|
|
30
30
|
GA4_MEASUREMENT_ID?: string;
|
|
31
31
|
TIKTOK_PIXEL_ID?: string;
|
|
32
32
|
SITE_DOMAIN?: string;
|
|
33
|
+
DISABLE_EXTERNAL_DISPATCH?: string;
|
|
33
34
|
|
|
34
35
|
// Secrets
|
|
36
|
+
ADMIN_API_TOKEN?: string;
|
|
35
37
|
META_ACCESS_TOKEN?: string;
|
|
36
38
|
GA4_API_SECRET?: string;
|
|
37
39
|
TIKTOK_ACCESS_TOKEN?: string;
|
|
40
|
+
META_APP_SECRET?: string;
|
|
38
41
|
WA_WEBHOOK_VERIFY_TOKEN?: string;
|
|
39
42
|
// WhatsApp Cloud API — nomes canônicos (Meta Cloud API v25.0)
|
|
40
43
|
WHATSAPP_ACCESS_TOKEN?: string; // canonical: Bearer token do System User
|
|
@@ -125,6 +125,7 @@ head_sampling_rate = 1
|
|
|
125
125
|
# Só execute o comando abaixo se o secret ainda não existir:
|
|
126
126
|
#
|
|
127
127
|
# wrangler secret put META_ACCESS_TOKEN ← token Meta CAPI
|
|
128
|
+
# wrangler secret put ADMIN_API_TOKEN ← token Bearer para rotas /api/* administrativas
|
|
128
129
|
# wrangler secret put GA4_API_SECRET ← secret GA4 Measurement Protocol
|
|
129
130
|
# wrangler secret put TIKTOK_ACCESS_TOKEN ← token TikTok Events API (opcional)
|
|
130
131
|
# wrangler secret put META_AD_ACCOUNT_ID ← ID da conta de anúncios Meta
|
|
@@ -134,6 +135,7 @@ head_sampling_rate = 1
|
|
|
134
135
|
# wrangler secret put WA_PHONE_ID ← ID legado do número (compatibilidade)
|
|
135
136
|
# wrangler secret put WA_NOTIFY_NUMBER ← Número que recebe alertas de venda/lead
|
|
136
137
|
# wrangler secret put WA_WEBHOOK_VERIFY_TOKEN ← Token de verificação do webhook WhatsApp
|
|
138
|
+
# wrangler secret put META_APP_SECRET ← App Secret para validar x-hub-signature-256 do WhatsApp
|
|
137
139
|
# wrangler secret put WEBHOOK_SECRET_TICTO ← HMAC-SHA256 Ticto
|
|
138
140
|
# wrangler secret put CALLMEBOT_PHONE ← Número CallMeBot para alertas
|
|
139
141
|
# wrangler secret put CALLMEBOT_APIKEY ← API Key CallMeBot
|
|
@@ -31,7 +31,7 @@ Este script captura dados à medida que o usuário preenche o formulário.
|
|
|
31
31
|
```javascript
|
|
32
32
|
<script>
|
|
33
33
|
(function() {
|
|
34
|
-
const WORKER_URL = 'https://api.seusite.com/
|
|
34
|
+
const WORKER_URL = 'https://api.seusite.com/track';
|
|
35
35
|
|
|
36
36
|
// Atualização de identidade ao sair do campo (Blur Event)
|
|
37
37
|
const inputs = document.querySelectorAll('input[type="email"], input[type="tel"]');
|
|
@@ -58,7 +58,7 @@ cdp-edge/
|
|
|
58
58
|
|
|
59
59
|
## Regras obrigatórias (CDP Edge)
|
|
60
60
|
|
|
61
|
-
- **
|
|
61
|
+
- **Arquitetura Cloudflare Native** — Workers, D1, KV, Queues e rotas same-domain
|
|
62
62
|
- **Same-domain only** — o Worker deve rodar no domínio do funil
|
|
63
63
|
- **SHA256 no Worker** — nunca hashar no browser
|
|
64
64
|
- **event_id idêntico** entre browser e server para deduplicação
|
|
@@ -50,7 +50,7 @@ Step 4: Confirmação / Obrigado
|
|
|
50
50
|
<script src="/js/cdpTrack.js" async></script>
|
|
51
51
|
<script>
|
|
52
52
|
window.cdpConfig = {
|
|
53
|
-
workerUrl: '/
|
|
53
|
+
workerUrl: '/track', // Same-Domain (furtivo)
|
|
54
54
|
metaId: 'SEU_PIXEL_ID',
|
|
55
55
|
ga4Id: 'G-XXXXXXXX',
|
|
56
56
|
tiktokId: 'C4XXXXXXXXXXXXXXX',
|
|
@@ -204,7 +204,7 @@ document.addEventListener('visibilitychange', async () => {
|
|
|
204
204
|
const cdp_uid = document.cookie.match(/cdp_uid=([^;]+)/)?.[1] || '';
|
|
205
205
|
|
|
206
206
|
// Usa navigator.sendBeacon para garantir envio mesmo se browser encerrar
|
|
207
|
-
navigator.sendBeacon('/
|
|
207
|
+
navigator.sendBeacon('/track', JSON.stringify({
|
|
208
208
|
event_name: 'CheckoutAbandonment',
|
|
209
209
|
cdp_uid,
|
|
210
210
|
step: checkoutState.step,
|
|
@@ -236,7 +236,7 @@ export default {
|
|
|
236
236
|
return new Response(null, { headers: cors });
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
if (url.pathname === '/
|
|
239
|
+
if (url.pathname === '/track') {
|
|
240
240
|
return handleTracking(request, env, ctx);
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -589,7 +589,7 @@ CREATE INDEX IF NOT EXISTS idx_events_created ON events_log(created_at);
|
|
|
589
589
|
- [ ] CheckoutAbandonment usa `navigator.sendBeacon` para garantir envio
|
|
590
590
|
|
|
591
591
|
### Cloudflare Worker
|
|
592
|
-
- [ ] Endpoint `/
|
|
592
|
+
- [ ] Endpoint `/track` configurado como Route no Cloudflare
|
|
593
593
|
- [ ] Identity Graph atualizado progressivamente conforme usuário preenche campos
|
|
594
594
|
- [ ] Meta CAPI v25.0 endpoint correto
|
|
595
595
|
- [ ] TikTok Events API v1.3 endpoint correto
|
|
@@ -183,7 +183,7 @@ function generateEventId() {
|
|
|
183
183
|
* @param {Object} data - Dados completos do evento
|
|
184
184
|
*/
|
|
185
185
|
async function sendToServer(eventName, data) {
|
|
186
|
-
const serverUrl = '/
|
|
186
|
+
const serverUrl = '/track'; // ou URL configurada
|
|
187
187
|
|
|
188
188
|
await fetch(serverUrl, {
|
|
189
189
|
method: 'POST',
|
|
@@ -299,7 +299,7 @@ const BehaviorEngine = {
|
|
|
299
299
|
fields_count: formInteracted.size,
|
|
300
300
|
meta_intensity: 'medium',
|
|
301
301
|
});
|
|
302
|
-
navigator.sendBeacon('/
|
|
302
|
+
navigator.sendBeacon('/track', new Blob([data], { type: 'application/json' }));
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
});
|
|
@@ -19,7 +19,7 @@ Mede a eficácia do vídeo de vendas antes do botão de compra aparecer.
|
|
|
19
19
|
* **Ação**: Disparar `ViewContent` (25/50/75) e `AddToCart` (100% ou clique no botão).
|
|
20
20
|
|
|
21
21
|
### 3. Adblock Immunity (Stealth Tracking)
|
|
22
|
-
Utiliza rotas no mesmo domínio (ex: `/
|
|
22
|
+
Utiliza rotas no mesmo domínio (ex: `/track`) para garantir que o script de rastreamento não seja bloqueado por uBlock ou Ghostery.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
┌─────────────────────────────────────────────────────────────┐
|
|
35
35
|
│ Cloudflare Worker (Quantum Tier) │
|
|
36
36
|
│
|
|
37
|
-
│ Endpoint: /
|
|
37
|
+
│ Endpoint: /track (Same-Domain) │
|
|
38
38
|
│ - Recebe eventos do browser │
|
|
39
39
|
│ - Salva sessão no D1 (fbp, fbc, ttp, UTMs) │
|
|
40
40
|
│ - Despacha para APIs: Meta, GA4, TikTok │
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
<script src="/js/cdpTrack.js" async></script>
|
|
79
79
|
<script>
|
|
80
80
|
window.cdpConfig = {
|
|
81
|
-
workerUrl: '/
|
|
81
|
+
workerUrl: '/track', // Same-Domain (furtivo)
|
|
82
82
|
metaId: 'SEU_PIXEL_ID',
|
|
83
83
|
ga4Id: 'G-XXXXXXXX',
|
|
84
84
|
tiktokId: 'C4XXXXXXXXXXXXXXX',
|
|
@@ -182,7 +182,7 @@ export default {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
// Endpoint principal de tracking
|
|
185
|
-
if (url.pathname === '/
|
|
185
|
+
if (url.pathname === '/track') {
|
|
186
186
|
return handleTracking(request, env, ctx);
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -500,13 +500,13 @@ CREATE INDEX IF NOT EXISTS idx_fbc ON identity_graph(fbc);
|
|
|
500
500
|
|
|
501
501
|
### Browser
|
|
502
502
|
- [ ] cdp_uid gerado no primeiro acesso e persistido por 1 ano
|
|
503
|
-
- [ ] PageView disparado via Fetch para `/
|
|
503
|
+
- [ ] PageView disparado via Fetch para `/track`
|
|
504
504
|
- [ ] InitiateCheckout disparado antes do redirecionamento
|
|
505
505
|
- [ ] cdp_uid injetado na URL do checkout (xcod/sck/src)
|
|
506
506
|
- [ ] UTMs capturados e salvos no D1
|
|
507
507
|
|
|
508
508
|
### Cloudflare Worker
|
|
509
|
-
- [ ] Endpoint `/
|
|
509
|
+
- [ ] Endpoint `/track` configurado como Route no Cloudflare
|
|
510
510
|
- [ ] Endpoint `/api/wh/{plataforma}` para webhooks
|
|
511
511
|
- [ ] Salvar sessão no D1 com todos os cookies
|
|
512
512
|
- [ ] D1 lookup pelo cdp_uid ao receber webhook
|
|
@@ -527,13 +527,13 @@ CREATE INDEX IF NOT EXISTS idx_fbc ON identity_graph(fbc);
|
|
|
527
527
|
```
|
|
528
528
|
1. Visitante acessa a página de vendas
|
|
529
529
|
└── JS: gera cdp_uid → salva cookie (1 ano)
|
|
530
|
-
└── JS: dispara PageView → /
|
|
530
|
+
└── JS: dispara PageView → /track
|
|
531
531
|
└── Worker: salva sessão no D1 (fbp, fbc, ttp, UTMs, IP)
|
|
532
532
|
└── Worker: dispatch → Meta, GA4, TikTok (PageView)
|
|
533
533
|
|
|
534
534
|
2. Visitante clica no botão de compra
|
|
535
535
|
└── JS: intercepta clique → previne redirecionamento
|
|
536
|
-
└── JS: dispara InitiateCheckout → /
|
|
536
|
+
└── JS: dispara InitiateCheckout → /track
|
|
537
537
|
└── Worker: atualiza sessão no D1
|
|
538
538
|
└── Worker: dispatch → Meta, GA4, TikTok (InitiateCheckout)
|
|
539
539
|
└── JS: injeta cdp_uid na URL (xcod/sck/src)
|
package/templates/vsl-page.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
┌─────────────────────────────────────────────────────────────────┐
|
|
36
36
|
│ Cloudflare Worker │
|
|
37
37
|
│ │
|
|
38
|
-
│ /
|
|
38
|
+
│ /track: │
|
|
39
39
|
│ VideoProgress 75% → Meta: ViewContent + TikTok: ViewContent │
|
|
40
40
|
│ Lead → Meta: Lead + GA4: generate_lead + TikTok: SubmitForm │
|
|
41
41
|
│ InitiateCheckout → Meta + GA4 + TikTok │
|
|
@@ -175,7 +175,7 @@ function dispatchEvent(eventName, data = {}) {
|
|
|
175
175
|
### Configurar no Worker (`worker.js`) — handler de VideoProgress:
|
|
176
176
|
|
|
177
177
|
```javascript
|
|
178
|
-
// Dentro do handler /
|
|
178
|
+
// Dentro do handler /track, adicionar case para VideoProgress
|
|
179
179
|
case 'VideoProgress':
|
|
180
180
|
const vp = payload.behavioral_data?.progress_percent || 0;
|
|
181
181
|
// Só disparar plataformas em thresholds significativos
|