cdp-edge 1.0.2 → 1.0.4

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.
@@ -140,7 +140,8 @@ const _wbraid = _urlParams.get('wbraid') || ''; // Google Ads (iOS, web-to-app,
140
140
  const _gbraid = _urlParams.get('gbraid') || ''; // Google Ads (app campaigns, privacy preserving)
141
141
 
142
142
  // TikTok
143
- const _ttclid = _urlParams.get('ttclid') || ''; // TikTok Ads click ID → complementa cookie _ttp
143
+ const _ttclid = _urlParams.get('ttclid') || ''; // TikTok Ads click ID → complementa cookie _ttp
144
+ const _msclkid = _urlParams.get('msclkid') || ''; // Microsoft Ads click ID
144
145
 
145
146
  // UTMs — rastreamento interno de origem de tráfego (independente da atribuição das plataformas)
146
147
  const _utms = {
@@ -217,7 +218,8 @@ const _getClickIDs = () => {
217
218
  wbraid: _wbraid || undefined,
218
219
  ttclid: _ttclid || undefined,
219
220
  ttp: document.cookie.match(/_ttp=([^;]+)/)?.[1] || undefined, // TikTok Pixel cookie — EMQ TikTok
220
- rclid: _urlParams.get('rclid') || undefined,
221
+ rclid: _urlParams.get('rclid') || undefined,
222
+ msclkid: _msclkid || undefined,
221
223
  };
222
224
  };
223
225
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-edge",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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",
@@ -389,7 +389,8 @@ export default {
389
389
  if (!trackPayload.wbraid && c.wbraid) trackPayload.wbraid = c.wbraid;
390
390
  if (!trackPayload.gbraid && c.gbraid) trackPayload.gbraid = c.gbraid;
391
391
  if (!trackPayload.ttclid && c.ttclid) trackPayload.ttclid = c.ttclid;
392
- if (!trackPayload.ttp && c.ttp) trackPayload.ttp = c.ttp;
392
+ if (!trackPayload.ttp && c.ttp) trackPayload.ttp = c.ttp;
393
+ if (!trackPayload.msclkid && c.msclkid) trackPayload.msclkid = c.msclkid;
393
394
  }
394
395
  if (payload.utms && typeof payload.utms === 'object') {
395
396
  const u = payload.utms as Record<string, string>;
@@ -761,6 +762,21 @@ export default {
761
762
  ctx.waitUntil(resolveDeviceGraph(env.DB, payload.userId, payload.email, payload.phone));
762
763
  }
763
764
 
765
+ // Deduplicação server-side — INSERT OR IGNORE retorna changes=0 para duplicatas
766
+ if (env.DB && payload.eventId) {
767
+ try {
768
+ const dedup = await env.DB.prepare(
769
+ `INSERT OR IGNORE INTO events (event_id, event_name, user_id, created_at)
770
+ VALUES (?, ?, ?, datetime('now'))`
771
+ ).bind(payload.eventId, eventName, payload.userId || null).run();
772
+ if (dedup.meta.changes === 0) {
773
+ return new Response(JSON.stringify({ ok: true, skipped: 'duplicate' }), { status: 200, headers });
774
+ }
775
+ } catch {
776
+ // Tabela ausente ou erro de DB — não bloqueia o pipeline
777
+ }
778
+ }
779
+
764
780
  // R2 Audit Log — background
765
781
  ctx.waitUntil(writeAuditLog(env, eventName, payload, geoData));
766
782
 
@@ -115,6 +115,7 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
115
115
  const {
116
116
  userId, email, phone,
117
117
  fbp, fbc, ttp, gclid, ttclid, gaClientId,
118
+ wbraid, gbraid, msclkid,
118
119
  city, state, country,
119
120
  engagementScore, userScore,
120
121
  } = payload;
@@ -131,8 +132,9 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
131
132
  await env.DB.prepare(`
132
133
  INSERT INTO user_profiles
133
134
  (user_id, email, phone, fbp, fbc, ttp, gclid, ttclid, ga_client_id,
135
+ wbraid, gbraid, msclkid,
134
136
  city, state, country, score, cohort_label, created_at, updated_at)
135
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,datetime('now'),datetime('now'))
137
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,datetime('now'),datetime('now'))
136
138
  ON CONFLICT(user_id) DO UPDATE SET
137
139
  email = COALESCE(excluded.email, user_profiles.email),
138
140
  phone = COALESCE(excluded.phone, user_profiles.phone),
@@ -142,6 +144,9 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
142
144
  gclid = COALESCE(excluded.gclid, user_profiles.gclid),
143
145
  ttclid = COALESCE(excluded.ttclid, user_profiles.ttclid),
144
146
  ga_client_id = COALESCE(excluded.ga_client_id, user_profiles.ga_client_id),
147
+ wbraid = COALESCE(excluded.wbraid, user_profiles.wbraid),
148
+ gbraid = COALESCE(excluded.gbraid, user_profiles.gbraid),
149
+ msclkid = COALESCE(excluded.msclkid, user_profiles.msclkid),
145
150
  city = COALESCE(excluded.city, user_profiles.city),
146
151
  state = COALESCE(excluded.state, user_profiles.state),
147
152
  country = COALESCE(excluded.country, user_profiles.country),
@@ -158,6 +163,9 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
158
163
  gclid || null,
159
164
  ttclid || null,
160
165
  gaClientId || null,
166
+ wbraid || null,
167
+ gbraid || null,
168
+ msclkid || null,
161
169
  city || null,
162
170
  state || null,
163
171
  (country || (request as any).cf?.country || null),
@@ -79,6 +79,22 @@ CREATE INDEX IF NOT EXISTS idx_profiles_fbp ON user_profiles(fbp);
79
79
  CREATE INDEX IF NOT EXISTS idx_profiles_msclkid ON user_profiles(msclkid);
80
80
  CREATE INDEX IF NOT EXISTS idx_profiles_li_fat ON user_profiles(li_fat_id);
81
81
 
82
+ -- ── Tabela de Eventos (Deduplicação + Label ML) ──────────────────────────────
83
+ -- Registra cada evento processado com sucesso.
84
+ -- Duplo uso: (1) deduplicação server-side via event_id UNIQUE;
85
+ -- (2) label de LTV para treinamento ML (lead comprou depois?)
86
+ CREATE TABLE IF NOT EXISTS events (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ event_id TEXT UNIQUE, -- deduplicação: INSERT OR IGNORE descarta duplicatas
89
+ event_name TEXT NOT NULL,
90
+ user_id TEXT,
91
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
92
+ );
93
+
94
+ CREATE INDEX IF NOT EXISTS idx_events_user_id ON events(user_id);
95
+ CREATE INDEX IF NOT EXISTS idx_events_event_name ON events(event_name);
96
+ CREATE INDEX IF NOT EXISTS idx_events_created ON events(created_at);
97
+
82
98
  -- ── Tabela de LOG de Webhooks Offline ─────────────────────────────────────────
83
99
  CREATE TABLE IF NOT EXISTS webhook_events (
84
100
  id INTEGER PRIMARY KEY AUTOINCREMENT,