cdp-edge 1.23.2 → 1.24.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 (42) hide show
  1. package/README.md +82 -21
  2. package/bin/cdp-edge.js +10 -1
  3. package/contracts/agent-versions.json +42 -41
  4. package/contracts/types.ts +81 -0
  5. package/dist/commands/install.js +6 -1
  6. package/dist/commands/server.js +4 -4
  7. package/docs/whatsapp-ctwa.md +3 -2
  8. package/extracted-skill/tracking-events-generator/agents/database-agent.md +5 -4
  9. package/extracted-skill/tracking-events-generator/agents/fraud-detection-agent.md +0 -1
  10. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +1 -1
  11. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +4 -4
  12. package/extracted-skill/tracking-events-generator/agents/ml-clustering-agent.md +81 -70
  13. package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +6 -2
  14. package/extracted-skill/tracking-events-generator/cdpTrack.js +7 -0
  15. package/extracted-skill/tracking-events-generator/models/lancamento-imobiliario.md +344 -0
  16. package/extracted-skill/tracking-events-generator/route-intent-capture.js +222 -0
  17. package/package.json +9 -5
  18. package/server-edge-tracker/INSTALAR.md +5 -5
  19. package/server-edge-tracker/{index.js → index.ts} +186 -72
  20. package/server-edge-tracker/modules/{db.js → db.ts} +180 -69
  21. package/server-edge-tracker/modules/dispatch/{ga4.js → ga4.ts} +12 -10
  22. package/server-edge-tracker/modules/dispatch/meta.ts +138 -0
  23. package/server-edge-tracker/modules/dispatch/{platforms.js → platforms.ts} +58 -56
  24. package/server-edge-tracker/modules/dispatch/{tiktok.js → tiktok.ts} +22 -20
  25. package/server-edge-tracker/modules/dispatch/{whatsapp.js → whatsapp.ts} +59 -25
  26. package/server-edge-tracker/modules/{intelligence.js → intelligence.ts} +175 -60
  27. package/server-edge-tracker/modules/ml/{bidding.js → bidding.ts} +37 -35
  28. package/server-edge-tracker/modules/ml/{fraud.js → fraud.ts} +49 -56
  29. package/server-edge-tracker/modules/ml/{logistic.js → logistic.ts} +44 -19
  30. package/server-edge-tracker/modules/ml/{ltv.js → ltv.ts} +179 -83
  31. package/server-edge-tracker/modules/ml/{matchquality.js → matchquality.ts} +70 -26
  32. package/server-edge-tracker/modules/ml/segmentation.ts +407 -0
  33. package/server-edge-tracker/modules/utils.ts +186 -0
  34. package/server-edge-tracker/schema-ltv-feedback.sql +11 -0
  35. package/server-edge-tracker/types.ts +251 -0
  36. package/server-edge-tracker/wrangler.toml +24 -6
  37. package/templates/lancamento-imobiliario.md +344 -0
  38. package/docs/PixelBuilder-Documentacao-Completa (2).docx +0 -0
  39. package/server-edge-tracker/modules/dispatch/meta.js +0 -119
  40. package/server-edge-tracker/modules/ml/segmentation.js +0 -316
  41. package/server-edge-tracker/modules/utils.js +0 -89
  42. package/server-edge-tracker/worker.js +0 -4577
@@ -0,0 +1,222 @@
1
+ /**
2
+ * CDP Edge — Route Intent Capture
3
+ * @version 2.0.0
4
+ *
5
+ * Fluxo:
6
+ * 1. Usuário clica em "Ver rota" → Google Maps abre normalmente
7
+ * 2. Widget aparece com um botão de WhatsApp
8
+ * 3. Usuário clica → WhatsApp abre com mensagem pré-escrita
9
+ * 4. Usuário envia → corretor recebe e responde
10
+ *
11
+ * Uso:
12
+ * initRouteIntentCapture({
13
+ * whatsappNumber: '5511999999999', // número do plantão/corretor
14
+ * propertyName: 'Reserva do Jardim',
15
+ * propertyId: 'rj-001',
16
+ * propertyLat: -23.6519,
17
+ * propertyLng: -46.5330,
18
+ * });
19
+ */
20
+
21
+ const isBrowser = typeof window !== 'undefined';
22
+
23
+ /**
24
+ * @param {object} options
25
+ * @param {string} options.whatsappNumber Número do plantão (ex: '5511999999999')
26
+ * @param {string} [options.propertyName] Nome do empreendimento
27
+ * @param {string} [options.propertyId] ID interno do imóvel
28
+ * @param {number} [options.propertyLat] Latitude
29
+ * @param {number} [options.propertyLng] Longitude
30
+ * @param {string} [options.routeSelector] CSS selector dos botões de rota (default auto)
31
+ * @param {string} [options.distanceBucket] 'very_close'|'close'|'nearby'|'moderate'|'far'
32
+ * @param {number} [options.distanceKm] Distância em km — exibe tempo estimado
33
+ * @param {string} [options.metaSignalBucket] 'hot'|'warm'|'cold'
34
+ * @param {string} [options.brokerName] Nome do corretor de plantão (ex: 'Ramon')
35
+ */
36
+ export function initRouteIntentCapture(options = {}) {
37
+ if (!isBrowser) return;
38
+
39
+ const {
40
+ whatsappNumber,
41
+ propertyName = 'o imóvel',
42
+ propertyId = null,
43
+ propertyLat = null,
44
+ propertyLng = null,
45
+ routeSelector = '[data-route-intent], a[href*="maps/dir"], a[href*="maps?q="]',
46
+ distanceBucket = null,
47
+ distanceKm = null,
48
+ metaSignalBucket = null,
49
+ brokerName = null,
50
+ } = options;
51
+
52
+ if (!whatsappNumber) {
53
+ console.warn('[RouteIntent] whatsappNumber é obrigatório.');
54
+ return;
55
+ }
56
+
57
+ _injectStyles();
58
+
59
+ document.addEventListener('click', (e) => {
60
+ const btn = e.target.closest(routeSelector);
61
+ if (!btn) return;
62
+ _showWidget(btn, { whatsappNumber, propertyName, propertyId, propertyLat, propertyLng, distanceBucket, distanceKm, metaSignalBucket, brokerName });
63
+ }, true);
64
+ }
65
+
66
+ // ── Widget ────────────────────────────────────────────────────────────────────
67
+
68
+ function _showWidget(anchorEl, opts) {
69
+ document.getElementById('cdp-ri-widget')?.remove();
70
+
71
+ const travelMinutes = _estimateTravelMinutes(opts.distanceKm);
72
+ const travelText = travelMinutes
73
+ ? `<p class="cdp-ri-travel">📍 Você está a cerca de <strong>${travelMinutes} min</strong> daqui</p>`
74
+ : '';
75
+
76
+ const brokerLine = opts.brokerName
77
+ ? `<p class="cdp-ri-broker">Ao chegar, pergunte pelo <strong>${opts.brokerName}</strong> — ele vai estar te aguardando com tudo pronto 🤝</p>`
78
+ : '';
79
+
80
+ const widget = document.createElement('div');
81
+ widget.id = 'cdp-ri-widget';
82
+ widget.innerHTML = `
83
+ <div class="cdp-ri-inner">
84
+ ${travelText}
85
+ <p class="cdp-ri-headline">Vi que você quer visitar o local! Confirme sua vinda enviando a mensagem abaixo — nossa equipe já fica de prontidão pra te receber 👇</p>
86
+ ${brokerLine}
87
+ <button id="cdp-ri-btn" type="button">
88
+ ${_waIcon()} Confirmar minha visita
89
+ </button>
90
+ <button id="cdp-ri-dismiss" type="button" class="cdp-ri-dismiss">Agora não</button>
91
+ </div>
92
+ `;
93
+
94
+ anchorEl.insertAdjacentElement('afterend', widget);
95
+ setTimeout(() => widget.scrollIntoView({ behavior: 'smooth', block: 'nearest' }), 150);
96
+
97
+ document.getElementById('cdp-ri-btn').addEventListener('click', () => _handleClick(widget, opts));
98
+ document.getElementById('cdp-ri-dismiss').addEventListener('click', () => {
99
+ window.cdpTrack?.track?.('ViewContent', {
100
+ content_name: 'rota_dispensada',
101
+ property_id: opts.propertyId,
102
+ funnel_stage: 'route_dismiss',
103
+ intent_score: 'medium',
104
+ distance_bucket: opts.distanceBucket || undefined,
105
+ meta_signal_bucket: opts.metaSignalBucket || undefined,
106
+ });
107
+ widget.remove();
108
+ });
109
+ }
110
+
111
+ // ── Click — dispara evento e abre WhatsApp ────────────────────────────────────
112
+
113
+ async function _handleClick(widget, opts) {
114
+ const btn = document.getElementById('cdp-ri-btn');
115
+ if (btn) btn.disabled = true;
116
+
117
+ // Evento Contact → Worker: LTV + hot lead trigger + CAPI
118
+ // Phone capturado depois via webhook quando a mensagem chegar
119
+ window.cdpTrack?.track?.('Contact', {
120
+ content_name: 'aviso_chegada_whatsapp',
121
+ property_id: opts.propertyId,
122
+ property_lat: opts.propertyLat,
123
+ property_lng: opts.propertyLng,
124
+ funnel_stage: 'route_click',
125
+ intent_score: 'high',
126
+ distance_bucket: opts.distanceBucket || undefined,
127
+ meta_signal_bucket: opts.metaSignalBucket || undefined,
128
+ });
129
+
130
+ // Monta mensagem — acolhedora, foco em confirmar chegada
131
+ const travelMinutes = _estimateTravelMinutes(opts.distanceKm);
132
+ const travelLine = travelMinutes ? `Estou a cerca de ${travelMinutes} minutos daí.` : '';
133
+ const brokerLine = opts.brokerName
134
+ ? `Vou procurar pelo ${opts.brokerName} ao chegar.`
135
+ : '';
136
+
137
+ const msg = [
138
+ `Oi! Estou interessado(a) em visitar o ${opts.propertyName} e gostaria de confirmar minha visita.`,
139
+ travelLine,
140
+ brokerLine || `Vocês conseguem me receber agora ou preciso marcar horário?`,
141
+ ].filter(Boolean).join(' ');
142
+
143
+ _showSuccess(widget, opts.whatsappNumber, msg);
144
+ }
145
+
146
+ // ── Tela de confirmação ───────────────────────────────────────────────────────
147
+
148
+ function _showSuccess(widget, whatsappNumber, msg) {
149
+ widget.innerHTML = `
150
+ <div class="cdp-ri-inner cdp-ri-ok">
151
+ <span class="cdp-ri-check">✅</span>
152
+ <p><strong>Abrindo WhatsApp...</strong><br><span>A equipe já foi avisada!</span></p>
153
+ </div>
154
+ `;
155
+ setTimeout(() => {
156
+ window.open(`https://wa.me/${whatsappNumber}?text=${encodeURIComponent(msg)}`, '_blank');
157
+ setTimeout(() => widget?.remove(), 3500);
158
+ }, 600);
159
+ }
160
+
161
+ // ── Helpers ───────────────────────────────────────────────────────────────────
162
+
163
+ function _estimateTravelMinutes(distanceKm) {
164
+ if (!distanceKm || distanceKm <= 0) return null;
165
+ return Math.max(5, Math.round((distanceKm * 60 / 25) / 5) * 5);
166
+ }
167
+
168
+ function _waIcon() {
169
+ return `<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
170
+ <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347z"/>
171
+ <path d="M12 0C5.373 0 0 5.373 0 12c0 2.122.554 4.118 1.528 5.852L0 24l6.335-1.513A11.933 11.933 0 0012 24c6.627 0 12-5.373 12-12S18.627 0 12 0zm0 21.818a9.818 9.818 0 01-5.002-1.368l-.359-.213-3.722.888.924-3.617-.234-.372A9.818 9.818 0 012.182 12C2.182 6.57 6.57 2.182 12 2.182S21.818 6.57 21.818 12 17.43 21.818 12 21.818z"/>
172
+ </svg>`;
173
+ }
174
+
175
+ // ── Estilos ───────────────────────────────────────────────────────────────────
176
+
177
+ function _injectStyles() {
178
+ if (document.getElementById('cdp-ri-styles')) return;
179
+ const s = document.createElement('style');
180
+ s.id = 'cdp-ri-styles';
181
+ s.textContent = `
182
+ #cdp-ri-widget {
183
+ margin-top: 12px;
184
+ padding: 16px 18px;
185
+ background: #f0fdf4;
186
+ border: 1.5px solid #22c55e;
187
+ border-radius: 12px;
188
+ font-family: Arial, sans-serif;
189
+ animation: cdp-ri-in .25s ease;
190
+ }
191
+ @keyframes cdp-ri-in {
192
+ from { opacity: 0; transform: translateY(-8px); }
193
+ to { opacity: 1; transform: translateY(0); }
194
+ }
195
+ .cdp-ri-inner { display: flex; flex-direction: column; gap: 10px; }
196
+ .cdp-ri-travel { margin: 0; font-size: 13px; color: #555; }
197
+ .cdp-ri-travel strong { color: #0f766e; }
198
+ .cdp-ri-headline { margin: 0; font-size: 14px; color: #15803d; font-weight: bold; line-height: 1.4; }
199
+ .cdp-ri-broker { margin: 0; font-size: 13px; color: #555; line-height: 1.4; }
200
+ .cdp-ri-broker strong { color: #0f766e; }
201
+ #cdp-ri-btn {
202
+ display: flex; align-items: center; justify-content: center; gap: 8px;
203
+ width: 100%; padding: 13px 16px;
204
+ background: #25D366; color: #fff;
205
+ border: none; border-radius: 10px;
206
+ font-size: 15px; font-weight: bold; cursor: pointer;
207
+ }
208
+ #cdp-ri-btn:hover { background: #1ebe5a; }
209
+ #cdp-ri-btn:disabled { background: #86efac; cursor: not-allowed; }
210
+ .cdp-ri-dismiss {
211
+ background: none; border: none;
212
+ color: #aaa; font-size: 12px;
213
+ cursor: pointer; padding: 2px 0; text-align: center;
214
+ }
215
+ .cdp-ri-dismiss:hover { color: #666; }
216
+ .cdp-ri-ok { align-items: center; text-align: center; gap: 8px; }
217
+ .cdp-ri-check { font-size: 28px; }
218
+ .cdp-ri-ok p { margin: 0; font-size: 14px; color: #15803d; line-height: 1.5; }
219
+ .cdp-ri-ok span { font-size: 13px; color: #555; }
220
+ `;
221
+ document.head.appendChild(s);
222
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-edge",
3
- "version": "1.23.2",
3
+ "version": "1.24.0",
4
4
  "description": "CDP Edge - Quantum Tracking - Sistema multi-agente para tracking digital Cloudflare Native (Workers + D1)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -23,17 +23,19 @@
23
23
  "build": "node build.js",
24
24
  "dev": "node build.js --watch",
25
25
  "test": "node test.js",
26
- "test:unit": "node tests/unit/normalization.test.js && node tests/unit/hashing.test.js && node tests/unit/deduplication.test.js && node tests/unit/payload-validation.test.js && node tests/unit/new-features.test.js",
26
+ "test:unit": "node tests/unit/normalization.test.js && node tests/unit/hashing.test.js && node tests/unit/deduplication.test.js && node tests/unit/payload-validation.test.js && node tests/unit/new-features.test.js && node tests/unit/utils.test.js",
27
27
  "test:unit:normalize": "node tests/unit/normalization.test.js",
28
28
  "test:unit:hash": "node tests/unit/hashing.test.js",
29
29
  "test:unit:dedup": "node tests/unit/deduplication.test.js",
30
30
  "test:unit:payload": "node tests/unit/payload-validation.test.js",
31
+ "test:unit:utils": "node tests/unit/utils.test.js",
31
32
  "test:all": "npm run test:unit",
32
33
  "test:integration": "cd tests/integration && npx vitest run",
33
34
  "agents:check": "node scripts/validate-agents.js",
34
35
  "agents:sync": "node scripts/sync-agents.js",
35
36
  "agents:sync:list": "node scripts/sync-agents.js --list",
36
- "agents:sync:all": "node scripts/sync-agents.js --apply-all"
37
+ "agents:sync:all": "node scripts/sync-agents.js --apply-all",
38
+ "typecheck": "tsc --noEmit"
37
39
  },
38
40
  "keywords": [
39
41
  "pixel",
@@ -70,12 +72,14 @@
70
72
  "ora": "^8.0.0"
71
73
  },
72
74
  "devDependencies": {
75
+ "@cloudflare/workers-types": "^4.20260412.1",
73
76
  "@semantic-release/changelog": "^6.0.3",
74
77
  "@semantic-release/commit-analyzer": "^13.0.1",
75
78
  "@semantic-release/github": "^12.0.6",
76
79
  "@semantic-release/npm": "^13.1.5",
77
80
  "@semantic-release/release-notes-generator": "^14.1.0",
78
- "@types/node": "^20.0.0",
79
- "semantic-release": "^25.0.3"
81
+ "@types/node": "^20.19.39",
82
+ "semantic-release": "^25.0.3",
83
+ "typescript": "^6.0.2"
80
84
  }
81
85
  }
@@ -298,7 +298,7 @@ wrangler d1 execute cdp-edge-db --remote --command="SELECT event_name, email, ci
298
298
  ```
299
299
  ERROR: Can't deploy routes that are assigned to another worker.
300
300
  "server-edge-tracker" is already assigned to routes:
301
- - lancamentosabc.com.br/track*
301
+ - SEU_DOMINIO/track*
302
302
  ```
303
303
 
304
304
  ### SOLUÇÃO 1 — Via Painel Cloudflare (RECOMENDADO):
@@ -306,7 +306,7 @@ ERROR: Can't deploy routes that are assigned to another worker.
306
306
  1. Acesse: https://dash.cloudflare.com/[ID_DA_CONTA]/workers/overview
307
307
  2. Clique no worker que está usando as rotas do seu domínio
308
308
  3. Vá em Settings → Triggers → Routes
309
- 4. Clique "Delete" nas rotas do domínio `lancamentosabc.com.br`
309
+ 4. Clique "Delete" nas rotas do domínio `SEU_DOMINIO`
310
310
  5. Repita o `wrangler deploy`
311
311
 
312
312
  ### SOLUÇÃO 2 — Via Wrangler CLI:
@@ -325,11 +325,11 @@ Se não quiser remover rotas existentes, use sufixo:
325
325
 
326
326
  ```toml
327
327
  [[routes]]
328
- pattern = "lancamentosabc.com.br/track-worker-novo*"
329
- zone_name = "lancamentosabc.com.br"
328
+ pattern = "SEU_DOMINIO/track-worker-novo*"
329
+ zone_name = "SEU_DOMINIO"
330
330
  ```
331
331
 
332
- **URL do tracking:** `https://lancamentosabc.com.br/track-worker-novo`
332
+ **URL do tracking:** `https://SEU_DOMINIO/track-worker-novo`
333
333
 
334
334
  ---
335
335