cdp-edge 1.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.
Files changed (176) hide show
  1. package/README.md +324 -0
  2. package/bin/cdp-edge.js +71 -0
  3. package/contracts/agent-versions.json +679 -0
  4. package/contracts/api-versions.json +372 -0
  5. package/contracts/types.ts +81 -0
  6. package/dist/commands/analyze.js +52 -0
  7. package/dist/commands/infra.js +54 -0
  8. package/dist/commands/install.js +191 -0
  9. package/dist/commands/server.js +174 -0
  10. package/dist/commands/setup.js +355 -0
  11. package/dist/commands/validate.js +248 -0
  12. package/dist/index.js +12 -0
  13. package/dist/sdk/cdpTrack.js +2095 -0
  14. package/dist/sdk/cdpTrack.min.js +64 -0
  15. package/dist/sdk/install-snippet.html +10 -0
  16. package/docs/CI-CD-SETUP.md +217 -0
  17. package/docs/events-reference.md +359 -0
  18. package/docs/installation.md +155 -0
  19. package/docs/quick-start.md +185 -0
  20. package/docs/sdk-reference.md +371 -0
  21. package/docs/whatsapp-ctwa.md +210 -0
  22. package/extracted-skill/tracking-events-generator/INDEX.md +94 -0
  23. package/extracted-skill/tracking-events-generator/INSTALACAO-CDPEDGE.md +58 -0
  24. package/extracted-skill/tracking-events-generator/INTEGRACAO-COMPLETA.md +683 -0
  25. package/extracted-skill/tracking-events-generator/MELHORIAS-IMPLEMENTADAS.md +513 -0
  26. package/extracted-skill/tracking-events-generator/Premium-Tracking-Intelligence-Resumo.md +333 -0
  27. package/extracted-skill/tracking-events-generator/SKILL.md +257 -0
  28. package/extracted-skill/tracking-events-generator/advanced-matching.js +364 -0
  29. package/extracted-skill/tracking-events-generator/agents/ab-ltv-agent.md +196 -0
  30. package/extracted-skill/tracking-events-generator/agents/ab-testing-agent.md +54 -0
  31. package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +1304 -0
  32. package/extracted-skill/tracking-events-generator/agents/bidding-agent.md +347 -0
  33. package/extracted-skill/tracking-events-generator/agents/bing-agent.md +66 -0
  34. package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +364 -0
  35. package/extracted-skill/tracking-events-generator/agents/code-guardian-agent.md +149 -0
  36. package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +2097 -0
  37. package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +1459 -0
  38. package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +456 -0
  39. package/extracted-skill/tracking-events-generator/agents/database-agent.md +668 -0
  40. package/extracted-skill/tracking-events-generator/agents/debug-agent.md +1455 -0
  41. package/extracted-skill/tracking-events-generator/agents/devops-agent.md +232 -0
  42. package/extracted-skill/tracking-events-generator/agents/domain-setup-agent.md +238 -0
  43. package/extracted-skill/tracking-events-generator/agents/email-agent.md +88 -0
  44. package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +257 -0
  45. package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +143 -0
  46. package/extracted-skill/tracking-events-generator/agents/google-agent.md +235 -0
  47. package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +525 -0
  48. package/extracted-skill/tracking-events-generator/agents/lead-scoring-agent.md +282 -0
  49. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +173 -0
  50. package/extracted-skill/tracking-events-generator/agents/localization-agent.md +55 -0
  51. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +59 -0
  52. package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +960 -0
  53. package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +2154 -0
  54. package/extracted-skill/tracking-events-generator/agents/match-quality-agent.md +304 -0
  55. package/extracted-skill/tracking-events-generator/agents/memory-agent.json +25 -0
  56. package/extracted-skill/tracking-events-generator/agents/memory-agent.md +878 -0
  57. package/extracted-skill/tracking-events-generator/agents/meta-agent.md +118 -0
  58. package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +749 -0
  59. package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +272 -0
  60. package/extracted-skill/tracking-events-generator/agents/performance-agent.md +1167 -0
  61. package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +1442 -0
  62. package/extracted-skill/tracking-events-generator/agents/pinterest-agent.md +318 -0
  63. package/extracted-skill/tracking-events-generator/agents/premium-tracking-intelligence-agent.md +849 -0
  64. package/extracted-skill/tracking-events-generator/agents/r2-setup-agent.md +258 -0
  65. package/extracted-skill/tracking-events-generator/agents/reddit-agent.md +321 -0
  66. package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +1861 -0
  67. package/extracted-skill/tracking-events-generator/agents/server-tracking.md +1188 -0
  68. package/extracted-skill/tracking-events-generator/agents/spotify-agent.md +391 -0
  69. package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +182 -0
  70. package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +459 -0
  71. package/extracted-skill/tracking-events-generator/agents/utm-agent.md +322 -0
  72. package/extracted-skill/tracking-events-generator/agents/validator-agent.md +271 -0
  73. package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +177 -0
  74. package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +129 -0
  75. package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +707 -0
  76. package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +537 -0
  77. package/extracted-skill/tracking-events-generator/anti-blocking.js +285 -0
  78. package/extracted-skill/tracking-events-generator/cdpTrack.js +640 -0
  79. package/extracted-skill/tracking-events-generator/contracts/api-versions.json +372 -0
  80. package/extracted-skill/tracking-events-generator/docs/guia-cloudflare-iniciante.md +107 -0
  81. package/extracted-skill/tracking-events-generator/engagement-scoring.js +226 -0
  82. package/extracted-skill/tracking-events-generator/evals/evals.json +235 -0
  83. package/extracted-skill/tracking-events-generator/integration-test.js +497 -0
  84. package/extracted-skill/tracking-events-generator/knowledge-base.md +3066 -0
  85. package/extracted-skill/tracking-events-generator/micro-events.js +992 -0
  86. package/extracted-skill/tracking-events-generator/models/captura-de-lead.md +78 -0
  87. package/extracted-skill/tracking-events-generator/models/captura-lead-evento-externo.md +99 -0
  88. package/extracted-skill/tracking-events-generator/models/checkout-proprio.md +111 -0
  89. package/extracted-skill/tracking-events-generator/models/lancamento-imobiliario.md +344 -0
  90. package/extracted-skill/tracking-events-generator/models/multi-step-checkout.md +672 -0
  91. package/extracted-skill/tracking-events-generator/models/pagina-obrigado.md +55 -0
  92. package/extracted-skill/tracking-events-generator/models/pinterest/conversions-api-template.js +144 -0
  93. package/extracted-skill/tracking-events-generator/models/pinterest/event-mappings.json +48 -0
  94. package/extracted-skill/tracking-events-generator/models/pinterest/tag-template.js +28 -0
  95. package/extracted-skill/tracking-events-generator/models/quiz-funnel.md +132 -0
  96. package/extracted-skill/tracking-events-generator/models/reddit/conversions-api-template.js +205 -0
  97. package/extracted-skill/tracking-events-generator/models/reddit/event-mappings.json +56 -0
  98. package/extracted-skill/tracking-events-generator/models/reddit/pixel-template.js +19 -0
  99. package/extracted-skill/tracking-events-generator/models/scenarios/behavior-engine.js +425 -0
  100. package/extracted-skill/tracking-events-generator/models/scenarios/real-estate-logic.md +50 -0
  101. package/extracted-skill/tracking-events-generator/models/scenarios/sales-page-logic.md +50 -0
  102. package/extracted-skill/tracking-events-generator/models/trafego-direto.md +582 -0
  103. package/extracted-skill/tracking-events-generator/models/webinar-registration.md +63 -0
  104. package/extracted-skill/tracking-events-generator/route-intent-capture.js +222 -0
  105. package/extracted-skill/tracking-events-generator/tracking.config.js +46 -0
  106. package/extracted-skill/tracking-events-generator/walkthrough.md +26 -0
  107. package/package.json +89 -0
  108. package/scripts/build-sdk.js +106 -0
  109. package/server-edge-tracker/.client.env.example +14 -0
  110. package/server-edge-tracker/INSTALAR.md +527 -0
  111. package/server-edge-tracker/SEGMENTATION-DOCS.md +513 -0
  112. package/server-edge-tracker/config/utm-mapping.json +64 -0
  113. package/server-edge-tracker/deploy-client.cjs +76 -0
  114. package/server-edge-tracker/index.ts +1164 -0
  115. package/server-edge-tracker/migrate-new-db.sql +137 -0
  116. package/server-edge-tracker/migrate-v2.sql +16 -0
  117. package/server-edge-tracker/migrate-v3.sql +6 -0
  118. package/server-edge-tracker/migrate-v4.sql +18 -0
  119. package/server-edge-tracker/migrate-v5.sql +17 -0
  120. package/server-edge-tracker/migrate-v6.sql +24 -0
  121. package/server-edge-tracker/migrate-v7.sql +64 -0
  122. package/server-edge-tracker/migrate.sql +111 -0
  123. package/server-edge-tracker/modules/db.ts +702 -0
  124. package/server-edge-tracker/modules/dispatch/ga4.ts +72 -0
  125. package/server-edge-tracker/modules/dispatch/meta.ts +143 -0
  126. package/server-edge-tracker/modules/dispatch/platforms.ts +255 -0
  127. package/server-edge-tracker/modules/dispatch/tiktok.ts +107 -0
  128. package/server-edge-tracker/modules/dispatch/whatsapp.ts +279 -0
  129. package/server-edge-tracker/modules/intelligence.ts +589 -0
  130. package/server-edge-tracker/modules/ml/bidding.ts +247 -0
  131. package/server-edge-tracker/modules/ml/fraud.ts +302 -0
  132. package/server-edge-tracker/modules/ml/logistic.ts +226 -0
  133. package/server-edge-tracker/modules/ml/ltv.ts +531 -0
  134. package/server-edge-tracker/modules/ml/matchquality.ts +232 -0
  135. package/server-edge-tracker/modules/ml/quiz.ts +343 -0
  136. package/server-edge-tracker/modules/ml/roas.ts +255 -0
  137. package/server-edge-tracker/modules/ml/segmentation.ts +407 -0
  138. package/server-edge-tracker/modules/nurture.ts +257 -0
  139. package/server-edge-tracker/modules/utils.ts +311 -0
  140. package/server-edge-tracker/modules/utm/utm-enricher.ts +231 -0
  141. package/server-edge-tracker/schema-ab-ltv.sql +97 -0
  142. package/server-edge-tracker/schema-bidding.sql +86 -0
  143. package/server-edge-tracker/schema-fraud.sql +90 -0
  144. package/server-edge-tracker/schema-indexes.sql +67 -0
  145. package/server-edge-tracker/schema-ltv-feedback.sql +11 -0
  146. package/server-edge-tracker/schema-quiz.sql +52 -0
  147. package/server-edge-tracker/schema-sales-engine.sql +113 -0
  148. package/server-edge-tracker/schema-segmentation.sql +219 -0
  149. package/server-edge-tracker/schema-utm.sql +82 -0
  150. package/server-edge-tracker/schema.sql +265 -0
  151. package/server-edge-tracker/types.ts +258 -0
  152. package/server-edge-tracker/wrangler.toml +136 -0
  153. package/templates/afiliado-sem-landing.md +312 -0
  154. package/templates/captura-de-lead.md +78 -0
  155. package/templates/captura-lead-evento-externo.md +99 -0
  156. package/templates/checkout-proprio.md +111 -0
  157. package/templates/install/.claude/commands/cdp.md +1 -0
  158. package/templates/install/CLAUDE.md +65 -0
  159. package/templates/lancamento-imobiliario.md +344 -0
  160. package/templates/linkedin/tag-template.js +46 -0
  161. package/templates/multi-step-checkout.md +672 -0
  162. package/templates/pagina-obrigado.md +55 -0
  163. package/templates/pinterest/conversions-api-template.js +144 -0
  164. package/templates/pinterest/event-mappings.json +48 -0
  165. package/templates/pinterest/tag-template.js +28 -0
  166. package/templates/quiz-funnel.md +132 -0
  167. package/templates/reddit/conversions-api-template.js +205 -0
  168. package/templates/reddit/event-mappings.json +56 -0
  169. package/templates/reddit/pixel-template.js +19 -0
  170. package/templates/scenarios/behavior-engine.js +425 -0
  171. package/templates/scenarios/real-estate-logic.md +50 -0
  172. package/templates/scenarios/sales-page-logic.md +50 -0
  173. package/templates/spotify/pixel-template.js +46 -0
  174. package/templates/trafego-direto.md +582 -0
  175. package/templates/vsl-page.md +292 -0
  176. package/templates/webinar-registration.md +63 -0
@@ -0,0 +1,640 @@
1
+ /**
2
+ * CDP EDGE TRACKING SDK (Quantum Tier)
3
+ * @version 1.0.0
4
+ *
5
+ * SDK principal de tracking do CDP Edge.
6
+ * Comunica diretamente com o Cloudflare Worker.
7
+ * Suporta micro-events, engagement scoring e tracking premium.
8
+ */
9
+
10
+ // ── Imports ────────────────────────────────────────────────
11
+ import CONFIG from './tracking.config.js';
12
+ import { initMicroEvents, trackScroll, trackTimeOnPage, trackVideo, trackCTAHover } from './micro-events.js';
13
+ import {
14
+ calculateEngagementScore,
15
+ updateEngagementState,
16
+ engagementState
17
+ } from './engagement-scoring.js';
18
+ import {
19
+ extractFormPII,
20
+ capturePII,
21
+ isValidPII
22
+ } from './advanced-matching.js';
23
+ import {
24
+ sendWithRetry,
25
+ initAntiBlocking,
26
+ ANTI_BLOCKING_CONFIG
27
+ } from './anti-blocking.js';
28
+ import { initRouteIntentCapture } from './route-intent-capture.js';
29
+
30
+ // ── Guards — segurança em SSR e SDK não carregado ──
31
+ const isBrowser = typeof window !== 'undefined';
32
+ const has = (fn) => isBrowser && typeof window[fn] === 'function';
33
+
34
+ // ── Google Consent Mode v2 ────────────────────────────────────────────────────
35
+ // Inicializa com todos os sinais negados por padrão (LGPD/GDPR compliance).
36
+ // Sites devem chamar cdpTrack.updateConsent({ analytics: true, ads: true })
37
+ // após o usuário aceitar o banner de cookies.
38
+ const CONSENT_KEY = '_cdp_consent';
39
+
40
+ const _defaultConsent = {
41
+ ad_storage: 'denied',
42
+ analytics_storage: 'denied',
43
+ ad_user_data: 'denied',
44
+ ad_personalization: 'denied',
45
+ };
46
+
47
+ // Lê consentimento salvo (boolean → objeto de sinais)
48
+ function _readStoredConsent() {
49
+ if (!isBrowser) return null;
50
+ try {
51
+ const raw = localStorage.getItem(CONSENT_KEY);
52
+ return raw ? JSON.parse(raw) : null;
53
+ } catch { return null; }
54
+ }
55
+
56
+ /**
57
+ * Inicializa o Google Consent Mode v2 com defaults negados.
58
+ * Chamado automaticamente em init() ANTES de qualquer gtag disparo.
59
+ * Também aplica consentimento salvo previamente (sessões recorrentes).
60
+ */
61
+ function initConsentMode() {
62
+ if (!isBrowser) return;
63
+
64
+ // Se consent mode desabilitado na config, não inicializa
65
+ if (CONFIG.consent?.defaultDenied === false) return;
66
+
67
+ // gtag helper — funciona mesmo que gtag não tenha sido carregado ainda
68
+ window.dataLayer = window.dataLayer || [];
69
+ function gtag() { window.dataLayer.push(arguments); }
70
+
71
+ const waitForUpdate = CONFIG.consent?.waitForUpdate ?? 500;
72
+
73
+ // Consent padrão: tudo negado + url_passthrough para preservar atribuição
74
+ gtag('consent', 'default', {
75
+ ..._defaultConsent,
76
+ wait_for_update: waitForUpdate,
77
+ });
78
+
79
+ // url_passthrough: preserva gclid/fbclid na URL mesmo sem consent
80
+ if (CONFIG.consent?.urlPassthrough !== false) {
81
+ gtag('set', 'url_passthrough', true);
82
+ }
83
+
84
+ // Aplicar consentimento salvo (usuário que já aceitou anteriormente)
85
+ const stored = _readStoredConsent();
86
+ if (stored) {
87
+ gtag('consent', 'update', stored);
88
+ if (CONFIG.debug) console.log('✅ Consent Mode: consentimento anterior restaurado');
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Atualiza o Consent Mode quando o usuário aceita/rejeita o banner.
94
+ *
95
+ * @param {object} params - { analytics?: boolean, ads?: boolean }
96
+ * @example
97
+ * // Usuário aceitou tudo
98
+ * cdpTrack.updateConsent({ analytics: true, ads: true });
99
+ *
100
+ * // Usuário aceitou só analytics, rejeitou anúncios
101
+ * cdpTrack.updateConsent({ analytics: true, ads: false });
102
+ */
103
+ export function updateConsent(params = {}) {
104
+ if (!isBrowser) return;
105
+
106
+ window.dataLayer = window.dataLayer || [];
107
+ function gtag() { window.dataLayer.push(arguments); }
108
+
109
+ const analyticsGranted = params.analytics === true ? 'granted' : 'denied';
110
+ const adsGranted = params.ads === true ? 'granted' : 'denied';
111
+
112
+ const consentUpdate = {
113
+ analytics_storage: analyticsGranted,
114
+ ad_storage: adsGranted,
115
+ ad_user_data: adsGranted,
116
+ ad_personalization: adsGranted,
117
+ };
118
+
119
+ gtag('consent', 'update', consentUpdate);
120
+
121
+ // Persiste para sessões futuras
122
+ try {
123
+ localStorage.setItem(CONSENT_KEY, JSON.stringify(consentUpdate));
124
+ } catch { /* storage bloqueado (incognito, etc.) */ }
125
+
126
+ console.log('✅ Consent Mode atualizado:', consentUpdate);
127
+ }
128
+
129
+ // ── Captura de parâmetros de URL no carregamento da página ──
130
+ // Cada plataforma injeta seu próprio Click ID na URL quando o usuário clica em um anúncio.
131
+ // Guardamos todos em memória para enviar ao servidor e garantir atribuição correta.
132
+ const _urlParams = isBrowser ? new URLSearchParams(window.location.search) : new URLSearchParams();
133
+
134
+ // Meta
135
+ const _fbclid = _urlParams.get('fbclid') || ''; // Meta Ads click ID → gera cookie _fbc
136
+
137
+ // Google Ads (três variantes por tipo de match/privacy)
138
+ const _gclid = _urlParams.get('gclid') || ''; // Google Ads standard click ID → gera cookie _gcl_aw
139
+ const _wbraid = _urlParams.get('wbraid') || ''; // Google Ads (iOS, web-to-app, privacy preserving)
140
+ const _gbraid = _urlParams.get('gbraid') || ''; // Google Ads (app campaigns, privacy preserving)
141
+
142
+ // TikTok
143
+ const _ttclid = _urlParams.get('ttclid') || ''; // TikTok Ads click ID → complementa cookie _ttp
144
+
145
+ // UTMs — rastreamento interno de origem de tráfego (independente da atribuição das plataformas)
146
+ const _utms = {
147
+ utm_source: _urlParams.get('utm_source') || '',
148
+ utm_medium: _urlParams.get('utm_medium') || '',
149
+ utm_campaign: _urlParams.get('utm_campaign') || '',
150
+ utm_content: _urlParams.get('utm_content') || '',
151
+ utm_term: _urlParams.get('utm_term') || '',
152
+ };
153
+
154
+ // ── User ID persistente (first-party cookie) ──────────────────
155
+ // Identifica o mesmo usuário entre sessões diferentes, mesmo sem login.
156
+ // Resolve o problema de cookies de terceiros bloqueados por ad-blockers.
157
+ const _getUserId = () => {
158
+ if (!isBrowser) return '';
159
+ const KEY = '_cdp_uid';
160
+ let uid = document.cookie.match(new RegExp(`${KEY}=([^;]+)`))?.[1];
161
+ if (!uid) {
162
+ uid = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
163
+ // Cookie first-party de 365 dias — não bloqueado por navegadores
164
+ document.cookie = `${KEY}=${uid}; max-age=${60*60*24*365}; path=/; SameSite=Lax`;
165
+ }
166
+ return uid;
167
+ };
168
+ const _userId = isBrowser ? _getUserId() : '';
169
+
170
+ // ── Session ID (cross-tab) ─────────────────────────────────────
171
+ const _getSessionId = () => {
172
+ if (!isBrowser) return '';
173
+ const sessionId = 'cdp_session';
174
+ let sid = sessionStorage.getItem(sessionId);
175
+ if (!sid) {
176
+ sid = `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
177
+ sessionStorage.setItem(sessionId, sid);
178
+ }
179
+ return sid;
180
+ };
181
+ const _sessionId = isBrowser ? _getSessionId() : '';
182
+
183
+ /**
184
+ * getUTMs() — retorna os UTMs capturados na chegada do usuário
185
+ * Útil para: salvar no CRM, incluir no payload do servidor, rastreamento interno.
186
+ * Atribuição própria independente da janela das plataformas (último clique registrado).
187
+ */
188
+ export const getUTMs = () => ({ ..._utms });
189
+
190
+ /**
191
+ * getUserId() — retorna o User ID persistente gerado como cookie first-party.
192
+ * Identificador de usuário persistente nativo.
193
+ */
194
+ export const getUserId = () => _userId;
195
+
196
+ /**
197
+ * getSessionId() — retorna o Session ID (cross-tab).
198
+ */
199
+ export const getSessionId = () => _sessionId;
200
+
201
+ /**
202
+ * generateId() — gera um ID único de evento
203
+ * Baseado em timestamp + random
204
+ */
205
+ export const generateId = () => {
206
+ return `${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
207
+ };
208
+
209
+ // ── Captura de Click IDs das plataformas ──────────────────────
210
+ const _getClickIDs = () => {
211
+ return {
212
+ fbp: document.cookie.match(/_fbp=([^;]+)/)?.[1] || undefined,
213
+ fbc: document.cookie.match(/_fbc=([^;]+)/)?.[1] || undefined,
214
+ fbclid: _fbclid || undefined, // click ID Meta → Worker gera fbc se cookie _fbc ausente
215
+ gclid: _gclid || undefined,
216
+ gbraid: _gbraid || undefined,
217
+ wbraid: _wbraid || undefined,
218
+ ttclid: _ttclid || undefined,
219
+ ttp: document.cookie.match(/_ttp=([^;]+)/)?.[1] || undefined, // TikTok Pixel cookie — EMQ TikTok
220
+ rclid: _urlParams.get('rclid') || undefined,
221
+ };
222
+ };
223
+
224
+ /**
225
+ * passCheckoutParams(options?) — adiciona UTMs + User ID nos links de checkout externo.
226
+ *
227
+ * Problema: quando o usuário clica em um link de Hotmart, Kiwify, Eduzz etc., os UTMs e
228
+ * o user ID são perdidos — a plataforma não sabe de onde veio o comprador.
229
+ *
230
+ * Solução: interceptar os links de checkout e adicionar os parâmetros de rastreamento
231
+ * na URL antes do clique, para que a plataforma receba e registre a origem.
232
+ *
233
+ * Plataformas suportadas e seus parâmetros:
234
+ * hotmart → xcod (user ID) + sck (UTMs pipe-separados: src|med|camp|con|term)
235
+ * kiwify → src (utm_source)
236
+ * eduzz → src (utm_source)
237
+ * monetizze → src (utm_source)
238
+ * cartpanda → utm_* passthrough direto
239
+ * custom → qualquer domínio via opção `domains`
240
+ *
241
+ * Uso:
242
+ * passCheckoutParams() // detecta automaticamente
243
+ * passCheckoutParams({ platforms: ['hotmart'] }) // só Hotmart
244
+ * passCheckoutParams({ domains: ['meusite.com/checkout'] }) // domínio customizado
245
+ */
246
+ export const passCheckoutParams = (options = {}) => {
247
+ if (!isBrowser) return;
248
+
249
+ const {
250
+ platforms = ['hotmart', 'kiwify', 'eduzz', 'monetizze', 'cartpanda', 'ticto'],
251
+ domains = []
252
+ } = options;
253
+
254
+ const utms = getUTMs();
255
+ const userId = getUserId();
256
+
257
+ // Mapa de domínios por plataforma
258
+ const PLATFORM_DOMAINS = {
259
+ hotmart: ['hotmart.com', 'pay.hotmart.com', 'payment.hotmart.com'],
260
+ kiwify: ['kiwify.com.br', 'checkout.kiwify.com.br'],
261
+ eduzz: ['eduzz.com', 'sun.eduzz.com'],
262
+ monetizze: ['monetizze.com.br'],
263
+ cartpanda: ['cartpanda.com', 'pay.cartpanda.com'],
264
+ ticto: ['ticto.app', 'pay.ticto.app', 'checkout.ticto.app']
265
+ };
266
+
267
+ // Constrói o `sck` do Hotmart: utm_source|utm_medium|utm_campaign|utm_content|utm_term
268
+ const buildSck = () =>
269
+ [utms.utm_source, utms.utm_medium, utms.utm_campaign, utms.utm_content, utms.utm_term]
270
+ .map(v => v || 'direto')
271
+ .join('|');
272
+
273
+ // Parâmetros por plataforma
274
+ const getParamsForUrl = (href) => {
275
+ const params = { ...domains };
276
+ const url = href.toLowerCase();
277
+
278
+ const isMatch = (list) => list.some(d => url.includes(d));
279
+
280
+ if (platforms.includes('hotmart') && isMatch(PLATFORM_DOMAINS.hotmart)) {
281
+ if (userId) params.xcod = userId;
282
+ if (utms.utm_source) params.sck = buildSck();
283
+ } else if (platforms.includes('kiwify') && isMatch(PLATFORM_DOMAINS.kiwify)) {
284
+ if (utms.utm_source) params.src = utms.utm_source;
285
+ if (utms.utm_medium) params.utm_medium = utms.utm_medium;
286
+ if (utms.utm_campaign) params.utm_campaign = utms.utm_campaign;
287
+ } else if (platforms.includes('eduzz') && isMatch(PLATFORM_DOMAINS.eduzz)) {
288
+ if (utms.utm_source) params.src = utms.utm_source;
289
+ } else if (platforms.includes('monetizze') && isMatch(PLATFORM_DOMAINS.monetizze)) {
290
+ if (utms.utm_source) params.src = utms.utm_source;
291
+ } else if (platforms.includes('cartpanda') && isMatch(PLATFORM_DOMAINS.cartpanda)) {
292
+ Object.entries(utms).forEach(([k, v]) => { if (v) params[k] = v; });
293
+ } else if (platforms.includes('ticto') && isMatch(PLATFORM_DOMAINS.ticto)) {
294
+ // Ticto captura UTMs em wh.tracking e parâmetros extras em wh.url_params
295
+ Object.entries(utms).forEach(([k, v]) => { if (v) params[k] = v; });
296
+ if (userId) params.user_id = userId; // recuperado no webhook via D1 cross-check
297
+ } else {
298
+ // Domínios customizados: repassa utm_* e userId
299
+ const allDomains = Object.values(PLATFORM_DOMAINS).flat().concat(domains);
300
+ if (!isMatch(allDomains)) return null;
301
+ Object.entries(utms).forEach(([k, v]) => { if (v) params[k] = v; });
302
+ if (userId) params.user_id = userId;
303
+ }
304
+
305
+ return Object.keys(params).length ? params : null;
306
+ };
307
+
308
+ const applyParams = (link) => {
309
+ if (!link.href || link.href.startsWith('javascript')) return;
310
+ const p = getParamsForUrl(link);
311
+ if (!p) return;
312
+
313
+ try {
314
+ const url = new URL(link.href);
315
+ Object.entries(p).forEach(([k, v]) => url.searchParams.set(k, v));
316
+ link.href = url.toString();
317
+ } catch { /* URL inválida — ignora */ }
318
+ };
319
+
320
+ // Aplica em todos os links existentes
321
+ document.querySelectorAll('a[href]').forEach(applyParams);
322
+
323
+ // Aplica em links adicionados dinamicamente (ex: botões de checkout lazy loaded)
324
+ // BUG CORRIGIDO: .observe() estava faltando — links dinâmicos nunca eram rastreados
325
+ new MutationObserver((mutations) => {
326
+ mutations.forEach(m => m.addedNodes.forEach(node => {
327
+ if (node.nodeType !== 1) return;
328
+ if (node.tagName === 'A') applyParams(node);
329
+ // Também verifica <a> aninhados dentro do nó adicionado
330
+ node.querySelectorAll?.('a[href]').forEach(applyParams);
331
+ }));
332
+ }).observe(document.body, { childList: true, subtree: true });
333
+
334
+ console.log('✅ PassCheckoutParams inicializado');
335
+ };
336
+
337
+ // ── Fingerprint Fallback para Links de Afiliado ───────────────────────────────
338
+ // Problema: quando o cookie _cdp_uid ou os UTMs são perdidos antes do clique
339
+ // (ex: usuário abre em nova aba, troca de navegador), o checkout chega sem cdp_uid.
340
+ //
341
+ // Solução: salvar uid + UTMs no localStorage ao carregar. passCheckoutParams()
342
+ // usa este backup quando os parâmetros de URL estiverem vazios.
343
+
344
+ const _AFFILIATE_STORAGE_KEY = '_cdp_aff';
345
+
346
+ function _saveAffiliateContext() {
347
+ if (!isBrowser) return;
348
+ try {
349
+ const hasUtms = Object.values(_utms).some(v => !!v);
350
+ const existing = _loadAffiliateContext();
351
+ // Só sobrescreve se esta visita tem UTMs — não apaga atribuição melhor anterior
352
+ if (hasUtms || !existing) {
353
+ localStorage.setItem(_AFFILIATE_STORAGE_KEY, JSON.stringify({
354
+ uid: _userId,
355
+ utms: _utms,
356
+ ts: Date.now(),
357
+ }));
358
+ }
359
+ } catch { /* localStorage bloqueado */ }
360
+ }
361
+
362
+ function _loadAffiliateContext() {
363
+ if (!isBrowser) return null;
364
+ try {
365
+ const raw = localStorage.getItem(_AFFILIATE_STORAGE_KEY);
366
+ if (!raw) return null;
367
+ const ctx = JSON.parse(raw);
368
+ if (Date.now() - ctx.ts > 30 * 24 * 60 * 60 * 1000) {
369
+ localStorage.removeItem(_AFFILIATE_STORAGE_KEY);
370
+ return null;
371
+ }
372
+ return ctx;
373
+ } catch { return null; }
374
+ }
375
+
376
+ /**
377
+ * Retorna UTMs ativos: da URL (prioridade) ou do localStorage (fallback de 30 dias).
378
+ * Uso principal: links de afiliado onde o usuário pode chegar sem parâmetros na URL.
379
+ */
380
+ export function getUTMsWithFallback() {
381
+ const live = getUTMs();
382
+ if (Object.values(live).some(v => !!v)) return live;
383
+ const stored = _loadAffiliateContext();
384
+ if (stored?.utms && Object.values(stored.utms).some(v => !!v)) {
385
+ if (CONFIG.debug) console.log('ℹ️ UTMs restaurados do localStorage (affiliate fallback)');
386
+ return stored.utms;
387
+ }
388
+ return live;
389
+ }
390
+
391
+ /**
392
+ * Retorna o userId com fallback para o armazenado no localStorage.
393
+ */
394
+ export function getUserIdWithFallback() {
395
+ return _userId || _loadAffiliateContext()?.uid || '';
396
+ }
397
+
398
+ // ── Tracking Events (Principal) ─────────────────────────────────
399
+
400
+ /**
401
+ * track() — Função principal de tracking
402
+ *
403
+ * @param {string} eventName - Nome do evento
404
+ * @param {object} data - Dados do evento
405
+ * @param {object} options - Opções adicionais
406
+ * @returns {Promise} - Promise com resultado
407
+ *
408
+ * Dispara evento para:
409
+ * - Worker (server-side)
410
+ * - Meta CAPI v22.0 (se configurado)
411
+ * - Google GA4 Measurement Protocol (se configurado)
412
+ * - TikTok Events API v1.3 (se configurado)
413
+ *
414
+ * Processo:
415
+ * 1. Gera event_id único
416
+ * 2. Coleta UTMs e Click IDs
417
+ * 3. Envia para Worker via fetch (same-domain)
418
+ * 4. Worker processa e despacha para plataformas
419
+ */
420
+ export async function track(eventName, data = {}, options = {}) {
421
+ if (!isBrowser) {
422
+ console.warn('⚠️ cdpTrack.track() deve ser chamado no browser');
423
+ return { success: false, error: 'Not in browser' };
424
+ }
425
+
426
+ const event_id = generateId();
427
+ const utms = getUTMs();
428
+ const clickIds = _getClickIDs();
429
+ const userId = getUserId();
430
+ const sessionId = getSessionId();
431
+ const timestamp = Date.now();
432
+
433
+ // Calcular engajamento preliminar (browser-side)
434
+ const engagementScore = calculateEngagementScore();
435
+ const { totalScore, timeLevel, scrollScore, clickScore, videoScore, hoverScore, intentionLevel } = engagementScore;
436
+
437
+ // Payload completo
438
+ const payload = {
439
+ event_id,
440
+ event_name: eventName,
441
+ user_id: userId,
442
+ session_id: sessionId,
443
+ utms,
444
+ click_ids: clickIds,
445
+ timestamp,
446
+ page_url: window.location.href,
447
+ referrer: document.referrer,
448
+ user_agent: navigator.userAgent,
449
+ behavioral_data: {
450
+ ...data,
451
+ engagement_score: totalScore,
452
+ time_level: timeLevel,
453
+ scroll_score: scrollScore,
454
+ click_score: clickScore,
455
+ video_score: videoScore,
456
+ hover_score: hoverScore,
457
+ intention_level: intentionLevel
458
+ },
459
+ ...options
460
+ };
461
+
462
+ try {
463
+ // Enviar para Worker com retry automático (anti-blocking)
464
+ const result = await sendWithRetry(payload, '/track');
465
+
466
+ if (result.success) {
467
+ console.log(`✅ Evento ${eventName} enviado com sucesso:`, event_id, result);
468
+
469
+ // Atualizar estado de engajamento
470
+ updateEngagementState('timeOnPage', Math.round((Date.now() - timestamp) / 1000));
471
+ updateEngagementState('totalScore', totalScore);
472
+
473
+ return { success: true, event_id, result };
474
+ } else {
475
+ throw new Error(result.error || 'Envio falhou após todas as tentativas');
476
+ }
477
+ } catch (error) {
478
+ console.error(`❌ Erro ao enviar evento ${eventName}:`, error);
479
+ return { success: false, error: error.message, event_id, attempts: ANTI_BLOCKING_CONFIG.maxRetries };
480
+ }
481
+ }
482
+
483
+ // ── Conveniência: Track Direct ──────────────────────────────────────
484
+
485
+ /**
486
+ * trackLead() — Captura de lead com dados PII crus
487
+ *
488
+ * @param {object} userData - Dados do usuário (email, phone, name, etc.)
489
+ * @param {HTMLFormElement} form - Formulário HTML (opcional, para captura automática)
490
+ * @returns {Promise} - Promise com resultado
491
+ */
492
+ export async function trackLead(userData, form = null) {
493
+ // Se formulário fornecido, extrair dados PII automaticamente
494
+ let piiData = { ...userData };
495
+
496
+ if (form) {
497
+ const formPII = extractFormPII(form);
498
+ piiData = { ...piiData, ...formPII };
499
+ }
500
+
501
+ // Passa dados crus para Worker fazer hashing
502
+ // Worker fará SHA256 para máximo performance e segurança
503
+ return track('Lead', piiData);
504
+ }
505
+
506
+ /**
507
+ * trackPurchase() — Captura de compra
508
+ *
509
+ * @param {object} orderData - Dados da compra (order_id, value, currency, etc.)
510
+ * @param {HTMLFormElement} form - Formulário HTML (opcional, para captura automática)
511
+ * @returns {Promise} - Promise com resultado
512
+ */
513
+ export async function trackPurchase(orderData, form = null) {
514
+ let purchaseData = { ...orderData };
515
+
516
+ if (form) {
517
+ const formPII = extractFormPII(form);
518
+ purchaseData = { ...purchaseData, ...formPII };
519
+ }
520
+
521
+ return track('Purchase', purchaseData);
522
+ }
523
+
524
+ /**
525
+ * Auto-captura formulários de lead na página
526
+ * Intercepta submit de formulários e captura PII automaticamente
527
+ */
528
+ export function setupAutoFormCapture() {
529
+ if (!isBrowser) return;
530
+
531
+ // Interceptar todos os formulários
532
+ document.addEventListener('submit', async (e) => {
533
+ const form = e.target;
534
+
535
+ // Verificar se é um formulário de lead (baseado em campos)
536
+ const hasEmailField = form.querySelector('[name*="email"]');
537
+ const hasPhoneField = form.querySelector('[name*="phone"], [name*="telefone"], [name*="celular"]');
538
+
539
+ if (hasEmailField || hasPhoneField) {
540
+ e.preventDefault();
541
+
542
+ const piiData = extractFormPII(form);
543
+
544
+ if (isValidPII(piiData)) {
545
+ await trackLead(piiData, form);
546
+ console.log('✅ Lead capturado automaticamente com Advanced Matching:', piiData);
547
+ }
548
+
549
+ // Continuar com submit do formulário
550
+ setTimeout(() => form.submit(), 100);
551
+ }
552
+ }, true); // Capture para formulários dinâmicos
553
+ }
554
+
555
+ /**
556
+ * trackViewContent() — Captura de visualização de conteúdo
557
+ *
558
+ * @param {object} contentData - Dados do conteúdo (content_id, content_name, value, currency)
559
+ * @returns {Promise} - Promise com resultado
560
+ */
561
+ export async function trackViewContent(contentData) {
562
+ return track('ViewContent', contentData);
563
+ }
564
+
565
+ /**
566
+ * trackAddToCart() — Captura de adição ao carrinho
567
+ *
568
+ * @param {object} cartData - Dados do carrinho (content_id, value, currency)
569
+ * @returns {Promise} - Promise com resultado
570
+ */
571
+ export async function trackAddToCart(cartData) {
572
+ return track('AddToCart', cartData);
573
+ }
574
+
575
+ // ── Inicialização ──────────────────────────────────────────────────────
576
+
577
+ /**
578
+ * Inicializa o cdpTrack SDK
579
+ * Deve ser chamado após carregar tracking.config.js
580
+ */
581
+ export async function init() {
582
+ if (!isBrowser) return;
583
+
584
+ if (CONFIG.debug) console.log('🚀 Inicializando cdpTrack SDK...');
585
+
586
+ // 0. Google Consent Mode v2 — DEVE ser o primeiro passo, antes de qualquer gtag
587
+ initConsentMode();
588
+
589
+ // 1. Inicializar anti-blocking
590
+ initAntiBlocking();
591
+
592
+ // 1-B. Salvar contexto de afiliado no localStorage (fallback para UTM resurrection)
593
+ _saveAffiliateContext();
594
+
595
+ // 2. Inicializar micro-events
596
+ initMicroEvents();
597
+
598
+ // 3. Inicializar auto-captura de formulários (respeitando CONFIG)
599
+ if (CONFIG.autoCaptureForms !== false) {
600
+ setupAutoFormCapture();
601
+ }
602
+
603
+ // 4. Expõe cdpTrack globalmente ANTES do BehaviorEngine (ele referencia window.cdpTrack)
604
+ window.cdpTrack = { track, trackLead, trackPurchase, trackViewContent, trackAddToCart, getUserId, getUserIdWithFallback, getSessionId, getUTMs, getUTMsWithFallback, updateConsent };
605
+
606
+ // 5. Carregar BehaviorEngine (respeitando CONFIG)
607
+ if (CONFIG.initBehaviorEngine !== false) {
608
+ try {
609
+ const behaviorModule = await import('./models/scenarios/behavior-engine.js');
610
+ const BehaviorEngine = behaviorModule.default;
611
+
612
+ if (BehaviorEngine && typeof BehaviorEngine.init === 'function') {
613
+ BehaviorEngine.init();
614
+ if (CONFIG.debug) console.log('✅ Behavior Engine inicializado');
615
+ }
616
+ } catch (error) {
617
+ // Não bloqueia o SDK — continua sem funcionalidades avançadas
618
+ if (CONFIG.debug) console.info('ℹ️ Behavior Engine não disponível:', error.message);
619
+ }
620
+ }
621
+
622
+ // 6. passCheckoutParams — controlado por CONFIG.passCheckoutParams + CONFIG.platforms
623
+ if (CONFIG.passCheckoutParams !== false && CONFIG.platforms?.length > 0) {
624
+ passCheckoutParams({ platforms: CONFIG.platforms });
625
+ }
626
+
627
+ // 7. Route Intent Capture — widget pós-clique de rota para imóveis
628
+ // Ativa quando CONFIG.routeIntent.whatsappNumber estiver configurado.
629
+ if (CONFIG.routeIntent?.whatsappNumber) {
630
+ initRouteIntentCapture(CONFIG.routeIntent);
631
+ }
632
+
633
+ if (CONFIG.debug) console.log('✅ cdpTrack SDK inicializado (Quantum Tier)');
634
+ }
635
+
636
+ // Auto-inicialização
637
+ if (isBrowser) {
638
+ // Inicializa após carregamento da página
639
+ window.addEventListener('DOMContentLoaded', init);
640
+ }