@hanzo/event 0.3.42 → 0.3.44

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/dist/index.cjs CHANGED
@@ -19,20 +19,20 @@ var SOCIAL_HOSTS = [
19
19
  var SEARCH_HOSTS = ["google.", "bing.", "duckduckgo.", "yahoo.", "baidu.", "ecosia."];
20
20
  function parseAttribution(search, referrer) {
21
21
  const q = new URLSearchParams(search || "");
22
- const get = (k) => {
22
+ const get2 = (k) => {
23
23
  const v = q.get(k);
24
24
  return v ? v.trim() : void 0;
25
25
  };
26
26
  const a = {
27
27
  utm: {
28
- source: get("utm_source"),
29
- medium: get("utm_medium"),
30
- campaign: get("utm_campaign"),
31
- term: get("utm_term"),
32
- content: get("utm_content")
28
+ source: get2("utm_source"),
29
+ medium: get2("utm_medium"),
30
+ campaign: get2("utm_campaign"),
31
+ term: get2("utm_term"),
32
+ content: get2("utm_content")
33
33
  },
34
34
  referrer: referrer ? referrer.trim() : void 0,
35
- refCode: get("ref") || get("refCode") || get("ref_code") || void 0
35
+ refCode: get2("ref") || get2("refCode") || get2("ref_code") || void 0
36
36
  };
37
37
  a.channel = deriveChannel(a);
38
38
  return a;
@@ -289,13 +289,13 @@ var HZ_ANON_ADOPT = ["hz_anon_id", "hz_id"];
289
289
  var HZ_ANON_DOMAIN = "hanzo.ai";
290
290
  var HZ_ANON_MAX_AGE = 2 * 365 * 24 * 60 * 60;
291
291
  var hzAnonMemo;
292
- function hzUuidv7(now) {
292
+ function hzUuidv7(now2) {
293
293
  var b = new Uint8Array(16);
294
294
  var i;
295
295
  var c = typeof crypto !== "undefined" ? crypto : void 0;
296
296
  if (c && typeof c.getRandomValues === "function") c.getRandomValues(b);
297
297
  else for (i = 0; i < 16; i++) b[i] = Math.random() * 256 | 0;
298
- var t = Math.floor(now === void 0 ? Date.now() : now);
298
+ var t = Math.floor(now2 === void 0 ? Date.now() : now2);
299
299
  for (i = 5; i >= 0; i--) {
300
300
  b[i] = t % 256;
301
301
  t = Math.floor(t / 256);
@@ -391,6 +391,15 @@ function hzAnonId() {
391
391
  }
392
392
  return id;
393
393
  }
394
+ function hzAnonAdopt(id) {
395
+ hzAnonMemo = id;
396
+ hzAnonWrite(HZ_ANON_KEY, id);
397
+ try {
398
+ var s = hzAnonStore();
399
+ if (s) s.setItem(HZ_ANON_KEY, id);
400
+ } catch (e) {
401
+ }
402
+ }
394
403
 
395
404
  // src/uid.ts
396
405
  function uuidv7Time(id) {
@@ -398,7 +407,7 @@ function uuidv7Time(id) {
398
407
  }
399
408
 
400
409
  // package.json
401
- var version = "0.3.42";
410
+ var version = "0.3.44";
402
411
 
403
412
  // src/version.ts
404
413
  var VERSION = version;
@@ -531,22 +540,22 @@ function coerceTag(v) {
531
540
  return s.length > MAX_TAG_LEN ? s.slice(0, MAX_TAG_LEN) : s;
532
541
  }
533
542
  function buildSentryEvent(input) {
534
- const { error, options = {}, identity, capturePII = false } = input;
543
+ const { error, options: options2 = {}, identity, capturePII = false } = input;
535
544
  const norm = normalizeError(error);
536
- const handled = options.handled !== false;
537
- const level = options.level ?? (handled ? "error" : "fatal");
538
- const tags = { handled: String(handled) };
539
- if (identity.product) tags.product = identity.product;
540
- if (identity.sessionId) tags.session = identity.sessionId;
545
+ const handled = options2.handled !== false;
546
+ const level = options2.level ?? (handled ? "error" : "fatal");
547
+ const tags2 = { handled: String(handled) };
548
+ if (identity.product) tags2.product = identity.product;
549
+ if (identity.sessionId) tags2.session = identity.sessionId;
541
550
  try {
542
- const props = options.properties ?? {};
551
+ const props = options2.properties ?? {};
543
552
  let n = 0;
544
553
  for (const k of Object.keys(props)) {
545
554
  if (n >= MAX_TAGS) break;
546
555
  try {
547
556
  const val = props[k];
548
557
  if (val === void 0 || val === null) continue;
549
- tags[k] = scrubText(coerceTag(val), capturePII);
558
+ tags2[k] = scrubText(coerceTag(val), capturePII);
550
559
  n++;
551
560
  } catch {
552
561
  continue;
@@ -571,7 +580,7 @@ function buildSentryEvent(input) {
571
580
  }
572
581
  ]
573
582
  },
574
- tags,
583
+ tags: tags2,
575
584
  sdk: { name: "@hanzo/event", version: VERSION }
576
585
  };
577
586
  if (identity.userId) event.user = { id: identity.userId };
@@ -701,7 +710,7 @@ function ls() {
701
710
  function anonId() {
702
711
  return hzAnonId() || void 0;
703
712
  }
704
- function sessionId(now = Date.now()) {
713
+ function sessionId(now2 = Date.now()) {
705
714
  const s = ls();
706
715
  if (!s) return void 0;
707
716
  let state = null;
@@ -710,14 +719,21 @@ function sessionId(now = Date.now()) {
710
719
  } catch {
711
720
  state = null;
712
721
  }
713
- if (!state || now - state.last > SESSION_TTL_MS) {
714
- state = { id: hzUuidv7(now), last: now };
722
+ if (!state || now2 - state.last > SESSION_TTL_MS) {
723
+ state = { id: hzUuidv7(now2), last: now2 };
715
724
  } else {
716
- state.last = now;
725
+ state.last = now2;
717
726
  }
718
727
  s.setItem(KEY.session, JSON.stringify(state));
719
728
  return state.id;
720
729
  }
730
+ function adoptSession(id, now2 = Date.now()) {
731
+ const s = ls();
732
+ if (s) s.setItem(KEY.session, JSON.stringify({ id, last: now2 }));
733
+ }
734
+ function adoptAnonId(id) {
735
+ hzAnonAdopt(id);
736
+ }
721
737
  function getFirstTouch() {
722
738
  const s = ls();
723
739
  if (!s) return void 0;
@@ -757,6 +773,76 @@ function mergeCohort(patch) {
757
773
  return next;
758
774
  }
759
775
 
776
+ // src/link.ts
777
+ var LINK_PARAMS = { anon: "hz_aid", session: "hz_sid", firstTouch: "hz_ft" };
778
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
779
+ var isId = (v) => !!v && UUID.test(v);
780
+ function encodeFirstTouch(a) {
781
+ return JSON.stringify([
782
+ a.utm.source,
783
+ a.utm.medium,
784
+ a.utm.campaign,
785
+ a.utm.term,
786
+ a.utm.content,
787
+ a.refCode,
788
+ a.referrer,
789
+ a.channel
790
+ ].map((v) => v ?? ""));
791
+ }
792
+ function decodeFirstTouch(raw) {
793
+ if (!raw || raw.length > 1024) return void 0;
794
+ try {
795
+ const t = JSON.parse(raw);
796
+ if (!Array.isArray(t) || t.length !== 8 || t.some((v) => typeof v !== "string")) return void 0;
797
+ const [source, medium, campaign, term, content, refCode, referrer, channel] = t;
798
+ const u = (v) => v || void 0;
799
+ return {
800
+ utm: { source: u(source), medium: u(medium), campaign: u(campaign), term: u(term), content: u(content) },
801
+ refCode: u(refCode),
802
+ referrer: u(referrer),
803
+ channel: u(channel)
804
+ };
805
+ } catch {
806
+ return void 0;
807
+ }
808
+ }
809
+ function linkUrl(url, state, fromHost, base) {
810
+ let u;
811
+ try {
812
+ u = new URL(url, base);
813
+ } catch {
814
+ return url;
815
+ }
816
+ if (u.protocol !== "https:" && u.protocol !== "http:") return url;
817
+ if (!orgOf(u.hostname) || u.hostname === fromHost) return url;
818
+ const set2 = (k, v) => v && u.searchParams.set(k, v);
819
+ if (isId(state.anonId)) set2(LINK_PARAMS.anon, state.anonId);
820
+ if (isId(state.sessionId)) set2(LINK_PARAMS.session, state.sessionId);
821
+ if (state.firstTouch) set2(LINK_PARAMS.firstTouch, encodeFirstTouch(state.firstTouch));
822
+ return u.toString();
823
+ }
824
+ function readLink(search) {
825
+ const q = new URLSearchParams(search || "");
826
+ const a = q.get(LINK_PARAMS.anon);
827
+ const s = q.get(LINK_PARAMS.session);
828
+ return {
829
+ anonId: isId(a) ? a : void 0,
830
+ sessionId: isId(s) ? s : void 0,
831
+ firstTouch: decodeFirstTouch(q.get(LINK_PARAMS.firstTouch))
832
+ };
833
+ }
834
+ function stripLink(href) {
835
+ const u = new URL(href);
836
+ let hit = false;
837
+ for (const k of Object.values(LINK_PARAMS)) {
838
+ if (u.searchParams.has(k)) {
839
+ u.searchParams.delete(k);
840
+ hit = true;
841
+ }
842
+ }
843
+ return hit ? u.pathname + u.search + u.hash : void 0;
844
+ }
845
+
760
846
  // src/core.ts
761
847
  var EVENT_PATH = "/v1/event";
762
848
  var DEFAULT_HOST = "https://api.hanzo.ai";
@@ -917,8 +1003,19 @@ var Analytics = class {
917
1003
  if (this.started || !this.cfg.enabled) return;
918
1004
  this.started = true;
919
1005
  if (!isBrowser()) return;
1006
+ const linked = readLink(window.location.search);
1007
+ if (linked.anonId) adoptAnonId(linked.anonId);
1008
+ if (linked.sessionId) adoptSession(linked.sessionId);
1009
+ const clean = stripLink(window.location.href);
1010
+ if (clean !== void 0) {
1011
+ try {
1012
+ window.history.replaceState(window.history.state, "", clean);
1013
+ } catch {
1014
+ }
1015
+ }
920
1016
  const parsed = parseAttribution(window.location.search, document.referrer);
921
- this.attribution = hasAttribution(parsed) ? setFirstTouchOnce(parsed) : getFirstTouch() ?? parsed;
1017
+ const first = linked.firstTouch && !getFirstTouch() ? setFirstTouchOnce(linked.firstTouch) : void 0;
1018
+ this.attribution = first ?? (hasAttribution(parsed) ? setFirstTouchOnce(parsed) : getFirstTouch() ?? parsed);
922
1019
  this.cohort = mergeCohort({
923
1020
  channel: this.attribution.channel ?? deriveChannel(this.attribution),
924
1021
  refCode: this.attribution.refCode
@@ -937,6 +1034,27 @@ var Analytics = class {
937
1034
  });
938
1035
  }
939
1036
  }
1037
+ /** link returns `url` carrying this visitor's anonymous id, session and first
1038
+ * touch when the destination is a Hanzo-owned host, so the journey continues
1039
+ * across the hop. Any other URL comes back unchanged. */
1040
+ link(url) {
1041
+ if (!isBrowser()) return url;
1042
+ if (!this.started) this.init();
1043
+ return linkUrl(
1044
+ url,
1045
+ { anonId: anonId(), sessionId: sessionId(), firstTouch: this.attribution },
1046
+ window.location.hostname,
1047
+ window.location.href
1048
+ );
1049
+ }
1050
+ /** authorize decorates an OAuth authorize URL for the redirect to IAM and flushes
1051
+ * what is queued, because the page is about to unload. Assign the result to
1052
+ * `location`. */
1053
+ authorize(url) {
1054
+ const out = this.link(url);
1055
+ this.flush(true);
1056
+ return out;
1057
+ }
940
1058
  /** identify binds the current visitor to a stable person id (post-login). */
941
1059
  identify(personId, traits) {
942
1060
  this.personId = personId;
@@ -1050,11 +1168,11 @@ var Analytics = class {
1050
1168
  * or publishable key is attached here — the two planes authenticate
1051
1169
  * independently. Errors are sent one envelope per event, immediately: batching
1052
1170
  * a crash report is how you lose it. */
1053
- sendError(err, options) {
1171
+ sendError(err, options2) {
1054
1172
  if (!this.cfg.enabled || !this.dsn) return;
1055
1173
  const event = buildSentryEvent({
1056
1174
  error: err,
1057
- options,
1175
+ options: options2,
1058
1176
  identity: this.errorIdentity(),
1059
1177
  capturePII: this.cfg.capturePII ?? false
1060
1178
  });
@@ -1141,16 +1259,16 @@ var Analytics = class {
1141
1259
  };
1142
1260
  var CLIENTS = /* @__PURE__ */ Symbol.for("hanzo.event.clients");
1143
1261
  function registry() {
1144
- const g = globalThis;
1145
- const existing = g[CLIENTS];
1262
+ const g2 = globalThis;
1263
+ const existing = g2[CLIENTS];
1146
1264
  if (existing) return existing;
1147
1265
  const fresh = /* @__PURE__ */ new Map();
1148
- g[CLIENTS] = fresh;
1266
+ g2[CLIENTS] = fresh;
1149
1267
  return fresh;
1150
1268
  }
1151
1269
  function resetClients() {
1152
- const g = globalThis;
1153
- delete g[CLIENTS];
1270
+ const g2 = globalThis;
1271
+ delete g2[CLIENTS];
1154
1272
  }
1155
1273
  function createAnalytics(config) {
1156
1274
  if (!isBrowser() || config.enabled === false) return new Analytics(config);
@@ -1166,6 +1284,427 @@ function createAnalytics(config) {
1166
1284
  return fresh;
1167
1285
  }
1168
1286
 
1287
+ // src/cookie.ts
1288
+ var DAY = 86400;
1289
+ function domain() {
1290
+ const h = window.location.hostname;
1291
+ if (h === "localhost" || /^[\d.]+$/.test(h) || h.includes(":")) return "";
1292
+ const parts = h.split(".");
1293
+ return parts.length > 2 ? `; Domain=.${parts.slice(-2).join(".")}` : `; Domain=.${h}`;
1294
+ }
1295
+ function get(name) {
1296
+ if (typeof document === "undefined") return void 0;
1297
+ const hit = document.cookie.split("; ").find((c) => c.startsWith(`${name}=`));
1298
+ return hit ? decodeURIComponent(hit.slice(name.length + 1)) : void 0;
1299
+ }
1300
+ function set(name, value, days) {
1301
+ if (typeof document === "undefined") return;
1302
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
1303
+ document.cookie = `${name}=${encodeURIComponent(value)}; Max-Age=${days * DAY}; Path=/; SameSite=Lax${domain()}${secure}`;
1304
+ }
1305
+
1306
+ // src/consent.ts
1307
+ var CONSENT_COOKIE = "hz_consent";
1308
+ var CONSENT_EVENT = "hzconsent";
1309
+ var US = /^(America\/(New_York|Chicago|Denver|Los_Angeles|Phoenix|Anchorage|Adak|Boise|Detroit|Juneau|Sitka|Nome|Yakutat|Menominee|Metlakatla|Indiana\/.*|Kentucky\/.*|North_Dakota\/.*)|Pacific\/Honolulu|US\/.*)$/;
1310
+ function gpc() {
1311
+ if (typeof navigator === "undefined") return false;
1312
+ return navigator.globalPrivacyControl === true;
1313
+ }
1314
+ function refused() {
1315
+ if (typeof window === "undefined") return false;
1316
+ const n = navigator;
1317
+ const dnt = n.doNotTrack ?? window.doNotTrack ?? n.msDoNotTrack;
1318
+ return gpc() || dnt === "1" || dnt === "yes";
1319
+ }
1320
+ function region() {
1321
+ try {
1322
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "";
1323
+ return US.test(zone) ? "opt-out" : "opt-in";
1324
+ } catch {
1325
+ return "opt-in";
1326
+ }
1327
+ }
1328
+ function stored() {
1329
+ return get(CONSENT_COOKIE) !== void 0;
1330
+ }
1331
+ function read2() {
1332
+ const kept = get(CONSENT_COOKIE);
1333
+ const on = kept !== void 0 ? kept.split(",") : region() === "opt-in" || refused() ? [] : ["analytics", "marketing", "ads"];
1334
+ const has = (k) => on.includes(k);
1335
+ return { analytics: has("analytics"), marketing: has("marketing") && !gpc(), ads: has("ads") && !gpc() };
1336
+ }
1337
+ function asks() {
1338
+ return typeof document !== "undefined" && region() === "opt-in" && !stored();
1339
+ }
1340
+ function render(c) {
1341
+ return ["analytics", "marketing", "ads"].filter((k) => c[k]).join(",");
1342
+ }
1343
+ function save(c) {
1344
+ const out = { analytics: c.analytics, marketing: c.marketing && !gpc(), ads: c.ads && !gpc() };
1345
+ set(CONSENT_COOKIE, render(out), 395);
1346
+ if (typeof window !== "undefined") window.dispatchEvent(new Event(CONSENT_EVENT));
1347
+ }
1348
+ var acceptAll = () => save({ analytics: true, marketing: true, ads: true });
1349
+ var rejectAll = () => save({ analytics: false, marketing: false, ads: false });
1350
+
1351
+ // src/touch.ts
1352
+ var CLICK_IDS = ["gclid", "gbraid", "wbraid", "fbclid", "li_fat_id", "twclid", "ttclid"];
1353
+ var DAYS = 90;
1354
+ var now = () => Math.floor(Date.now() / 1e3);
1355
+ var rand = () => Math.floor(Math.random() * 2 ** 31);
1356
+ function capture(c) {
1357
+ if (typeof window === "undefined") return;
1358
+ if (c.marketing) {
1359
+ const q = new URLSearchParams(window.location.search);
1360
+ const click = {};
1361
+ for (const k of CLICK_IDS) {
1362
+ const v = q.get(k)?.trim();
1363
+ if (v) click[k] = v;
1364
+ }
1365
+ if (Object.keys(click).length) {
1366
+ click.clicked = now();
1367
+ set("hz_touch", JSON.stringify(click), DAYS);
1368
+ if (click.fbclid) set("_fbc", `fb.1.${Date.now()}.${click.fbclid}`, DAYS);
1369
+ }
1370
+ if (!get("_fbp")) set("_fbp", `fb.1.${Date.now()}.${rand()}`, DAYS);
1371
+ }
1372
+ if (c.analytics && !get("_ga") && !get("hz_cid")) set("hz_cid", `${rand()}.${now()}`, 730);
1373
+ }
1374
+ function touch(c) {
1375
+ const out = {};
1376
+ if (c.marketing) {
1377
+ try {
1378
+ Object.assign(out, JSON.parse(get("hz_touch") ?? "{}"));
1379
+ } catch {
1380
+ }
1381
+ const fbc = get("_fbc");
1382
+ const fbp = get("_fbp");
1383
+ if (fbc) out.fbc = fbc;
1384
+ if (fbp) out.fbp = fbp;
1385
+ }
1386
+ if (c.analytics) {
1387
+ const ga = get("_ga")?.split(".");
1388
+ const cid = ga && ga.length >= 4 ? ga.slice(-2).join(".") : get("hz_cid");
1389
+ if (cid) out.cid = cid;
1390
+ }
1391
+ return out;
1392
+ }
1393
+ var page = () => window;
1394
+ var tags = [];
1395
+ var configured = false;
1396
+ var options = {};
1397
+ var loaded = /* @__PURE__ */ new Set();
1398
+ var google = "idle";
1399
+ var configAnswered = false;
1400
+ var held = [];
1401
+ var leaving = false;
1402
+ function settled() {
1403
+ return configAnswered && google !== "wait";
1404
+ }
1405
+ function flush() {
1406
+ if (settled() || leaving) held.splice(0).forEach((send) => send());
1407
+ }
1408
+ function script(src, onload, onerror) {
1409
+ const s = document.createElement("script");
1410
+ s.async = true;
1411
+ s.src = src;
1412
+ if (onload) s.onload = onload;
1413
+ if (onerror) s.onerror = onerror;
1414
+ document.head.appendChild(s);
1415
+ }
1416
+ function gtagStub() {
1417
+ const p = page();
1418
+ p.dataLayer = p.dataLayer || [];
1419
+ if (!p.gtag) {
1420
+ p.gtag = function() {
1421
+ p.dataLayer.push(arguments);
1422
+ };
1423
+ }
1424
+ return p.gtag;
1425
+ }
1426
+ var g = (on) => on ? "granted" : "denied";
1427
+ function consentMode(c, first) {
1428
+ const gtag = gtagStub();
1429
+ const state = {
1430
+ analytics_storage: g(c.analytics),
1431
+ ad_storage: g(c.marketing),
1432
+ ad_user_data: g(c.marketing),
1433
+ ad_personalization: g(c.marketing && c.ads)
1434
+ };
1435
+ if (first) {
1436
+ gtag("consent", "default", {
1437
+ analytics_storage: "denied",
1438
+ ad_storage: "denied",
1439
+ ad_user_data: "denied",
1440
+ ad_personalization: "denied",
1441
+ wait_for_update: 500
1442
+ });
1443
+ }
1444
+ gtag("consent", "update", state);
1445
+ }
1446
+ function loadGoogle(ids) {
1447
+ const gtag = gtagStub();
1448
+ const fresh = ids.filter((id) => !loaded.has(id));
1449
+ if (!fresh.length) return;
1450
+ if (google === "idle") {
1451
+ google = "wait";
1452
+ gtag("js", /* @__PURE__ */ new Date());
1453
+ const done = (to) => () => {
1454
+ if (google !== "wait") return;
1455
+ google = to;
1456
+ flush();
1457
+ };
1458
+ script(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(fresh[0])}`, done("loaded"), done("failed"));
1459
+ setTimeout(done("failed"), 8e3);
1460
+ }
1461
+ for (const id of fresh) {
1462
+ loaded.add(id);
1463
+ gtag("config", id, options.domains ? { linker: { domains: options.domains } } : {});
1464
+ }
1465
+ }
1466
+ function loadMeta(id, c) {
1467
+ const p = page();
1468
+ if (!p.fbq) {
1469
+ const n = p.fbq = function(...a) {
1470
+ if (n.callMethod) n.callMethod(...a);
1471
+ else n.queue.push(a);
1472
+ };
1473
+ if (!p._fbq) p._fbq = n;
1474
+ n.push = n;
1475
+ n.loaded = true;
1476
+ n.version = "2.0";
1477
+ n.queue = [];
1478
+ script("https://connect.facebook.net/en_US/fbevents.js");
1479
+ }
1480
+ if (!c.ads) p.fbq("dataProcessingOptions", ["LDU"], 0, 0);
1481
+ p.fbq("init", id);
1482
+ p.fbq("track", "PageView");
1483
+ }
1484
+ function loadLinkedIn(id) {
1485
+ const p = page();
1486
+ p._linkedin_data_partner_ids = p._linkedin_data_partner_ids || [];
1487
+ p._linkedin_data_partner_ids.push(id);
1488
+ if (!p.lintrk) {
1489
+ const l = p.lintrk = function(...a) {
1490
+ l.q.push(a);
1491
+ };
1492
+ l.q = [];
1493
+ }
1494
+ script("https://snap.licdn.com/li.lms-analytics/insight.min.js");
1495
+ }
1496
+ function loadX(id) {
1497
+ const p = page();
1498
+ if (!p.twq) {
1499
+ const s = p.twq = function(...a) {
1500
+ if (s.exe) s.exe(...a);
1501
+ else s.queue.push(a);
1502
+ };
1503
+ s.version = "1.1";
1504
+ s.queue = [];
1505
+ script("https://static.ads-twitter.com/uwt.js");
1506
+ }
1507
+ p.twq("config", id);
1508
+ }
1509
+ var TIKTOK_METHODS = ["page", "track", "identify", "instances", "debug", "on", "off", "once", "ready", "alias", "group", "enableCookie", "disableCookie"];
1510
+ function loadTikTok(id) {
1511
+ const p = page();
1512
+ p.TiktokAnalyticsObject = "ttq";
1513
+ const q = p.ttq = p.ttq || [];
1514
+ if (!q._i) {
1515
+ q.methods = TIKTOK_METHODS;
1516
+ for (const m of TIKTOK_METHODS) q[m] = (...a) => q.push([m, ...a]);
1517
+ q._i = {};
1518
+ q._t = {};
1519
+ q._o = {};
1520
+ q.load = (sdk) => {
1521
+ q._i[sdk] = [];
1522
+ q._t[sdk] = Date.now();
1523
+ script(`https://analytics.tiktok.com/i18n/pixel/events.js?sdkid=${encodeURIComponent(sdk)}&lib=ttq`);
1524
+ };
1525
+ }
1526
+ q.load(id);
1527
+ q.page();
1528
+ }
1529
+ var NEEDS = {
1530
+ ga: "analytics",
1531
+ gads: "marketing",
1532
+ meta: "marketing",
1533
+ linkedin: "marketing",
1534
+ x: "marketing",
1535
+ tiktok: "marketing"
1536
+ };
1537
+ var allowed = (t, c) => t.type === "ga" ? c.analytics || c.marketing : Boolean(NEEDS[t.type] && c[NEEDS[t.type]]);
1538
+ function apply() {
1539
+ if (typeof window === "undefined" || !configured) return;
1540
+ const c = read2();
1541
+ capture(c);
1542
+ const on = tags.filter((t) => allowed(t, c));
1543
+ const ids = on.filter((t) => t.type === "ga" || t.type === "gads").map((t) => t.id);
1544
+ if (ids.length) {
1545
+ consentMode(c, !loaded.has("consent"));
1546
+ loaded.add("consent");
1547
+ loadGoogle(ids);
1548
+ }
1549
+ for (const t of on) {
1550
+ const k = `${t.type}:${t.id}`;
1551
+ if (loaded.has(k)) continue;
1552
+ if (t.type === "meta") loadMeta(t.id, c);
1553
+ else if (t.type === "linkedin") loadLinkedIn(t.id);
1554
+ else if (t.type === "x") loadX(t.id);
1555
+ else if (t.type === "tiktok") loadTikTok(t.id);
1556
+ else continue;
1557
+ loaded.add(k);
1558
+ }
1559
+ flush();
1560
+ }
1561
+ function start(o = {}) {
1562
+ if (typeof window === "undefined") return () => void 0;
1563
+ options = o;
1564
+ addEventListener(CONSENT_EVENT, apply);
1565
+ addEventListener("pagehide", pagehide);
1566
+ if (!configured) {
1567
+ configured = true;
1568
+ const base = (o.base ?? "https://api.hanzo.ai").replace(/\/$/, "");
1569
+ const q = new URLSearchParams();
1570
+ if (o.key) q.set("key", o.key);
1571
+ q.set("host", o.host ?? window.location.hostname);
1572
+ const answer = (list) => {
1573
+ tags = list;
1574
+ configAnswered = true;
1575
+ apply();
1576
+ };
1577
+ const cap = setTimeout(() => answer([]), 3e3);
1578
+ fetch(`${base}/v1/project/tags?${q}`).then((r) => r.ok ? r.json() : { tags: [] }).then((j) => {
1579
+ clearTimeout(cap);
1580
+ answer(Array.isArray(j.tags) ? j.tags : []);
1581
+ }).catch(() => {
1582
+ clearTimeout(cap);
1583
+ answer([]);
1584
+ });
1585
+ }
1586
+ return () => {
1587
+ removeEventListener(CONSENT_EVENT, apply);
1588
+ removeEventListener("pagehide", pagehide);
1589
+ };
1590
+ }
1591
+ function pagehide() {
1592
+ leaving = true;
1593
+ flush();
1594
+ }
1595
+ function reach() {
1596
+ const c = read2();
1597
+ const to = [];
1598
+ for (const t of tags) {
1599
+ if (!allowed(t, c)) continue;
1600
+ const running = t.type === "ga" ? google === "loaded" && c.analytics : t.type === "gads" ? google === "loaded" && c.marketing : loaded.has(`${t.type}:${t.id}`);
1601
+ if (running && !to.includes(t.type)) to.push(t.type);
1602
+ }
1603
+ return to.join(",");
1604
+ }
1605
+ var META_STANDARD = /* @__PURE__ */ new Set([
1606
+ "AddPaymentInfo",
1607
+ "AddToCart",
1608
+ "AddToWishlist",
1609
+ "CompleteRegistration",
1610
+ "Contact",
1611
+ "CustomizeProduct",
1612
+ "Donate",
1613
+ "FindLocation",
1614
+ "InitiateCheckout",
1615
+ "Lead",
1616
+ "Purchase",
1617
+ "Schedule",
1618
+ "Search",
1619
+ "StartTrial",
1620
+ "SubmitApplication",
1621
+ "Subscribe",
1622
+ "ViewContent"
1623
+ ]);
1624
+ function shape(platform, p) {
1625
+ const items = Array.isArray(p.items) ? p.items : [];
1626
+ const out = {};
1627
+ if (typeof p.value === "number") out.value = p.value;
1628
+ out.currency = typeof p.currency === "string" ? p.currency : "USD";
1629
+ if (!items.length) return out;
1630
+ if (platform === "meta") {
1631
+ out.content_type = "product";
1632
+ out.content_ids = items.map((i) => i.item_id);
1633
+ out.contents = items.map((i) => ({ id: i.item_id, quantity: i.quantity ?? 1, item_price: i.price }));
1634
+ out.content_name = items[0].item_name;
1635
+ out.num_items = items.reduce((n, i) => n + (i.quantity ?? 1), 0);
1636
+ } else if (platform === "tiktok") {
1637
+ out.content_type = "product";
1638
+ out.contents = items.map((i) => ({ content_id: i.item_id, content_name: i.item_name, quantity: i.quantity ?? 1, price: i.price }));
1639
+ } else {
1640
+ out.contents = items.map((i) => ({ content_id: i.item_id, content_name: i.item_name, content_price: i.price, num_items: i.quantity ?? 1 }));
1641
+ }
1642
+ return out;
1643
+ }
1644
+ function unpriced(p) {
1645
+ const out = { ...p, value: 0 };
1646
+ if (Array.isArray(p.items)) out.items = p.items.map((i) => ({ ...i, price: 0 }));
1647
+ return out;
1648
+ }
1649
+ function mirror(name, params, id, to = reach()) {
1650
+ const p = page();
1651
+ const on = new Set(to.split(",").filter(Boolean));
1652
+ const ga4 = events.namesOn(name, "ga4");
1653
+ const test = params.test === true;
1654
+ if (test) params = unpriced(params);
1655
+ const transaction = typeof params.order_id === "string" ? { transaction_id: params.order_id } : {};
1656
+ for (const t of tags) {
1657
+ if (!on.has(t.type)) continue;
1658
+ const rule = t.events?.[name];
1659
+ if (t.type === "ga") {
1660
+ for (const n of ga4) {
1661
+ p.gtag?.("event", n, { ...params, ...transaction, event_id: id, transport_type: "beacon", ...test ? { debug_mode: true } : {} });
1662
+ }
1663
+ } else if (t.type === "gads" && rule) {
1664
+ p.gtag?.("event", "conversion", {
1665
+ send_to: rule,
1666
+ value: typeof params.value === "number" ? params.value : void 0,
1667
+ currency: typeof params.currency === "string" ? params.currency : "USD",
1668
+ ...transaction
1669
+ });
1670
+ } else if (t.type === "meta") {
1671
+ for (const n of events.namesOn(name, "meta")) {
1672
+ p.fbq?.(META_STANDARD.has(n) ? "track" : "trackCustom", n, shape("meta", params), { eventID: id });
1673
+ }
1674
+ } else if (t.type === "tiktok") {
1675
+ for (const n of events.namesOn(name, "tiktok")) p.ttq?.track?.(n, shape("tiktok", params), { event_id: id });
1676
+ } else if (t.type === "linkedin" && rule && events.namesOn(name, "linkedin").length) {
1677
+ p.lintrk?.("track", { conversion_id: Number(rule) });
1678
+ } else if (t.type === "x" && rule && events.namesOn(name, "x").length) {
1679
+ p.twq?.("event", rule, { ...shape("x", params), conversion_id: id });
1680
+ }
1681
+ }
1682
+ }
1683
+ function track(stream, name, params = {}) {
1684
+ if (typeof window === "undefined") return;
1685
+ if (!leaving && !settled()) {
1686
+ held.push(() => track(stream, name, params));
1687
+ return;
1688
+ }
1689
+ const c = read2();
1690
+ const event_id = crypto.randomUUID();
1691
+ const to = reach();
1692
+ mirror(name, params, event_id, to);
1693
+ stream?.capture(name, {
1694
+ ...params,
1695
+ ...touch(c),
1696
+ event_id,
1697
+ consent: render(c),
1698
+ tags: to,
1699
+ userAgent: navigator.userAgent,
1700
+ url: window.location.href
1701
+ });
1702
+ }
1703
+ function visit() {
1704
+ const c = read2();
1705
+ return { ...touch(c), consent: render(c), tags: reach(), userAgent: navigator.userAgent, url: window.location.href };
1706
+ }
1707
+
1169
1708
  // src/goals.ts
1170
1709
  var GOALS = {
1171
1710
  // Signup: the conversion is signup_completed, along the site signup funnel.
@@ -1251,14 +1790,21 @@ Object.defineProperty(exports, "eventsOf", {
1251
1790
  get: function () { return events.eventsOf; }
1252
1791
  });
1253
1792
  exports.Analytics = Analytics;
1793
+ exports.CLICK_IDS = CLICK_IDS;
1254
1794
  exports.COHORTS = COHORTS;
1795
+ exports.CONSENT_COOKIE = CONSENT_COOKIE;
1796
+ exports.CONSENT_EVENT = CONSENT_EVENT;
1255
1797
  exports.GOALS = GOALS;
1798
+ exports.LINK_PARAMS = LINK_PARAMS;
1256
1799
  exports.ORG_DOMAIN = ORG_DOMAIN;
1257
1800
  exports.ORG_KEY = ORG_KEY;
1258
1801
  exports.PRODUCT_PROJECT = PRODUCT_PROJECT;
1259
1802
  exports.VERSION = VERSION;
1803
+ exports.acceptAll = acceptAll;
1804
+ exports.asks = asks;
1260
1805
  exports.buildEnvelope = buildEnvelope;
1261
1806
  exports.buildSentryEvent = buildSentryEvent;
1807
+ exports.captureClick = capture;
1262
1808
  exports.createAnalytics = createAnalytics;
1263
1809
  exports.deriveChannel = deriveChannel;
1264
1810
  exports.digest = digest;
@@ -1269,19 +1815,36 @@ exports.fingerprint = fingerprint;
1269
1815
  exports.framesFromStack = framesFromStack;
1270
1816
  exports.getCohort = getCohort;
1271
1817
  exports.getFirstTouch = getFirstTouch;
1818
+ exports.gpc = gpc;
1272
1819
  exports.hasAttribution = hasAttribution;
1273
1820
  exports.hostOf = hostOf;
1274
1821
  exports.isoWeek = isoWeek;
1275
1822
  exports.keyFor = keyFor;
1276
1823
  exports.keyForPage = keyForPage;
1824
+ exports.linkUrl = linkUrl;
1825
+ exports.mirror = mirror;
1277
1826
  exports.orgOf = orgOf;
1278
1827
  exports.parseAttribution = parseAttribution;
1279
1828
  exports.parseDsn = parseDsn;
1829
+ exports.reach = reach;
1830
+ exports.readConsent = read2;
1831
+ exports.readLink = readLink;
1280
1832
  exports.redactSecrets = redactSecrets;
1833
+ exports.refused = refused;
1834
+ exports.region = region;
1835
+ exports.rejectAll = rejectAll;
1836
+ exports.renderConsent = render;
1281
1837
  exports.resetClients = resetClients;
1838
+ exports.saveConsent = save;
1282
1839
  exports.scrubPII = scrubPII;
1283
1840
  exports.scrubText = scrubText;
1841
+ exports.startTags = start;
1842
+ exports.storedConsent = stored;
1843
+ exports.stripLink = stripLink;
1844
+ exports.touch = touch;
1845
+ exports.track = track;
1284
1846
  exports.uuidv7 = hzUuidv7;
1285
1847
  exports.uuidv7Time = uuidv7Time;
1848
+ exports.visit = visit;
1286
1849
  //# sourceMappingURL=index.cjs.map
1287
1850
  //# sourceMappingURL=index.cjs.map