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