@hanzo/event 0.3.42 → 0.3.43
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/core.d.ts +8 -0
- package/dist/core.d.ts.map +1 -1
- package/dist/index.cjs +124 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +121 -3
- 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/package.json +1 -1
- package/src/anon.d.ts +3 -0
- package/src/anon.js +19 -1
- package/src/core.ts +45 -3
- package/src/index.ts +2 -0
- package/src/link.test.ts +222 -0
- package/src/link.ts +103 -0
- package/src/storage.ts +14 -1
package/dist/link.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Attribution } from './types';
|
|
2
|
+
export declare const LINK_PARAMS: {
|
|
3
|
+
readonly anon: 'hz_aid';
|
|
4
|
+
readonly session: 'hz_sid';
|
|
5
|
+
readonly firstTouch: 'hz_ft';
|
|
6
|
+
};
|
|
7
|
+
export declare const isId: (v: string | null | undefined) => v is string;
|
|
8
|
+
/** What a link carries. Any field may be absent. */
|
|
9
|
+
export interface LinkState {
|
|
10
|
+
anonId?: string;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
firstTouch?: Attribution;
|
|
13
|
+
}
|
|
14
|
+
/** First touch as a compact tuple: utm source, medium, campaign, term, content, refCode,
|
|
15
|
+
* referrer, channel. Short because it rides a URL; a tuple because keys cost bytes. */
|
|
16
|
+
export declare function encodeFirstTouch(a: Attribution): string;
|
|
17
|
+
export declare function decodeFirstTouch(raw: string | null | undefined): Attribution | undefined;
|
|
18
|
+
/** linkUrl returns `url` with the visitor's identity appended when its host is
|
|
19
|
+
* Hanzo-owned and is not `fromHost`; otherwise `url` unchanged. Relative URLs
|
|
20
|
+
* resolve against `base`. Never throws. */
|
|
21
|
+
export declare function linkUrl(url: string, state: LinkState, fromHost: string, base?: string): string;
|
|
22
|
+
/** readLink parses what a link carried out of a location.search value. Invalid
|
|
23
|
+
* values are dropped, not repaired. */
|
|
24
|
+
export declare function readLink(search: string): LinkState;
|
|
25
|
+
/** stripLink returns the path, query and hash of `href` without the link parameters, or
|
|
26
|
+
* undefined when there were none. */
|
|
27
|
+
export declare function stripLink(href: string): string | undefined;
|
|
28
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,eAAO,MAAM,WAAW;aAAK,IAAI,EAAE,QAAQ;aAAE,OAAO,EAAE,QAAQ;aAAE,UAAU,EAAE,OAAO;CAAW,CAAA;AAK9F,eAAO,MAAM,IAAI,MAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,CAAC,IAAI,MAA6B,CAAA;AAEtF,oDAAoD;AACpD,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,WAAW,CAAA;CACzB;AAED;wFACwF;AACxF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAKvD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAgBxF;AAED;;4CAE4C;AAC5C,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAc9F;AAED;wCACwC;AACxC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CASlD;AAED;sCACsC;AACtC,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU1D"}
|
package/dist/react.cjs
CHANGED
|
@@ -386,9 +386,18 @@ function hzAnonId() {
|
|
|
386
386
|
}
|
|
387
387
|
return id;
|
|
388
388
|
}
|
|
389
|
+
function hzAnonAdopt(id) {
|
|
390
|
+
hzAnonMemo = id;
|
|
391
|
+
hzAnonWrite(HZ_ANON_KEY, id);
|
|
392
|
+
try {
|
|
393
|
+
var s = hzAnonStore();
|
|
394
|
+
if (s) s.setItem(HZ_ANON_KEY, id);
|
|
395
|
+
} catch (e) {
|
|
396
|
+
}
|
|
397
|
+
}
|
|
389
398
|
|
|
390
399
|
// package.json
|
|
391
|
-
var version = "0.3.
|
|
400
|
+
var version = "0.3.43";
|
|
392
401
|
|
|
393
402
|
// src/version.ts
|
|
394
403
|
var VERSION = version;
|
|
@@ -708,6 +717,13 @@ function sessionId(now = Date.now()) {
|
|
|
708
717
|
s.setItem(KEY.session, JSON.stringify(state));
|
|
709
718
|
return state.id;
|
|
710
719
|
}
|
|
720
|
+
function adoptSession(id, now = Date.now()) {
|
|
721
|
+
const s = ls();
|
|
722
|
+
if (s) s.setItem(KEY.session, JSON.stringify({ id, last: now }));
|
|
723
|
+
}
|
|
724
|
+
function adoptAnonId(id) {
|
|
725
|
+
hzAnonAdopt(id);
|
|
726
|
+
}
|
|
711
727
|
function getFirstTouch() {
|
|
712
728
|
const s = ls();
|
|
713
729
|
if (!s) return void 0;
|
|
@@ -747,6 +763,76 @@ function mergeCohort(patch) {
|
|
|
747
763
|
return next;
|
|
748
764
|
}
|
|
749
765
|
|
|
766
|
+
// src/link.ts
|
|
767
|
+
var LINK_PARAMS = { anon: "hz_aid", session: "hz_sid", firstTouch: "hz_ft" };
|
|
768
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
769
|
+
var isId = (v) => !!v && UUID.test(v);
|
|
770
|
+
function encodeFirstTouch(a) {
|
|
771
|
+
return JSON.stringify([
|
|
772
|
+
a.utm.source,
|
|
773
|
+
a.utm.medium,
|
|
774
|
+
a.utm.campaign,
|
|
775
|
+
a.utm.term,
|
|
776
|
+
a.utm.content,
|
|
777
|
+
a.refCode,
|
|
778
|
+
a.referrer,
|
|
779
|
+
a.channel
|
|
780
|
+
].map((v) => v ?? ""));
|
|
781
|
+
}
|
|
782
|
+
function decodeFirstTouch(raw) {
|
|
783
|
+
if (!raw || raw.length > 1024) return void 0;
|
|
784
|
+
try {
|
|
785
|
+
const t = JSON.parse(raw);
|
|
786
|
+
if (!Array.isArray(t) || t.length !== 8 || t.some((v) => typeof v !== "string")) return void 0;
|
|
787
|
+
const [source, medium, campaign, term, content, refCode, referrer, channel] = t;
|
|
788
|
+
const u = (v) => v || void 0;
|
|
789
|
+
return {
|
|
790
|
+
utm: { source: u(source), medium: u(medium), campaign: u(campaign), term: u(term), content: u(content) },
|
|
791
|
+
refCode: u(refCode),
|
|
792
|
+
referrer: u(referrer),
|
|
793
|
+
channel: u(channel)
|
|
794
|
+
};
|
|
795
|
+
} catch {
|
|
796
|
+
return void 0;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
function linkUrl(url, state, fromHost, base) {
|
|
800
|
+
let u;
|
|
801
|
+
try {
|
|
802
|
+
u = new URL(url, base);
|
|
803
|
+
} catch {
|
|
804
|
+
return url;
|
|
805
|
+
}
|
|
806
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return url;
|
|
807
|
+
if (!orgOf(u.hostname) || u.hostname === fromHost) return url;
|
|
808
|
+
const set = (k, v) => v && u.searchParams.set(k, v);
|
|
809
|
+
if (isId(state.anonId)) set(LINK_PARAMS.anon, state.anonId);
|
|
810
|
+
if (isId(state.sessionId)) set(LINK_PARAMS.session, state.sessionId);
|
|
811
|
+
if (state.firstTouch) set(LINK_PARAMS.firstTouch, encodeFirstTouch(state.firstTouch));
|
|
812
|
+
return u.toString();
|
|
813
|
+
}
|
|
814
|
+
function readLink(search) {
|
|
815
|
+
const q = new URLSearchParams(search || "");
|
|
816
|
+
const a = q.get(LINK_PARAMS.anon);
|
|
817
|
+
const s = q.get(LINK_PARAMS.session);
|
|
818
|
+
return {
|
|
819
|
+
anonId: isId(a) ? a : void 0,
|
|
820
|
+
sessionId: isId(s) ? s : void 0,
|
|
821
|
+
firstTouch: decodeFirstTouch(q.get(LINK_PARAMS.firstTouch))
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
function stripLink(href) {
|
|
825
|
+
const u = new URL(href);
|
|
826
|
+
let hit = false;
|
|
827
|
+
for (const k of Object.values(LINK_PARAMS)) {
|
|
828
|
+
if (u.searchParams.has(k)) {
|
|
829
|
+
u.searchParams.delete(k);
|
|
830
|
+
hit = true;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return hit ? u.pathname + u.search + u.hash : void 0;
|
|
834
|
+
}
|
|
835
|
+
|
|
750
836
|
// src/core.ts
|
|
751
837
|
var EVENT_PATH = "/v1/event";
|
|
752
838
|
var DEFAULT_HOST = "https://api.hanzo.ai";
|
|
@@ -907,8 +993,19 @@ var Analytics = class {
|
|
|
907
993
|
if (this.started || !this.cfg.enabled) return;
|
|
908
994
|
this.started = true;
|
|
909
995
|
if (!isBrowser()) return;
|
|
996
|
+
const linked = readLink(window.location.search);
|
|
997
|
+
if (linked.anonId) adoptAnonId(linked.anonId);
|
|
998
|
+
if (linked.sessionId) adoptSession(linked.sessionId);
|
|
999
|
+
const clean = stripLink(window.location.href);
|
|
1000
|
+
if (clean !== void 0) {
|
|
1001
|
+
try {
|
|
1002
|
+
window.history.replaceState(window.history.state, "", clean);
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
910
1006
|
const parsed = parseAttribution(window.location.search, document.referrer);
|
|
911
|
-
|
|
1007
|
+
const first = linked.firstTouch && !getFirstTouch() ? setFirstTouchOnce(linked.firstTouch) : void 0;
|
|
1008
|
+
this.attribution = first ?? (hasAttribution(parsed) ? setFirstTouchOnce(parsed) : getFirstTouch() ?? parsed);
|
|
912
1009
|
this.cohort = mergeCohort({
|
|
913
1010
|
channel: this.attribution.channel ?? deriveChannel(this.attribution),
|
|
914
1011
|
refCode: this.attribution.refCode
|
|
@@ -927,6 +1024,27 @@ var Analytics = class {
|
|
|
927
1024
|
});
|
|
928
1025
|
}
|
|
929
1026
|
}
|
|
1027
|
+
/** link returns `url` carrying this visitor's anonymous id, session and first
|
|
1028
|
+
* touch when the destination is a Hanzo-owned host, so the journey continues
|
|
1029
|
+
* across the hop. Any other URL comes back unchanged. */
|
|
1030
|
+
link(url) {
|
|
1031
|
+
if (!isBrowser()) return url;
|
|
1032
|
+
if (!this.started) this.init();
|
|
1033
|
+
return linkUrl(
|
|
1034
|
+
url,
|
|
1035
|
+
{ anonId: anonId(), sessionId: sessionId(), firstTouch: this.attribution },
|
|
1036
|
+
window.location.hostname,
|
|
1037
|
+
window.location.href
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
/** authorize decorates an OAuth authorize URL for the redirect to IAM and flushes
|
|
1041
|
+
* what is queued, because the page is about to unload. Assign the result to
|
|
1042
|
+
* `location`. */
|
|
1043
|
+
authorize(url) {
|
|
1044
|
+
const out = this.link(url);
|
|
1045
|
+
this.flush(true);
|
|
1046
|
+
return out;
|
|
1047
|
+
}
|
|
930
1048
|
/** identify binds the current visitor to a stable person id (post-login). */
|
|
931
1049
|
identify(personId, traits) {
|
|
932
1050
|
this.personId = personId;
|