@hanzo/event 0.3.21 → 0.3.23
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 +100 -235
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -142
- package/dist/index.d.ts +4 -142
- package/dist/index.mjs +68 -220
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +4 -7
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +2 -5
- package/dist/react.mjs.map +1 -1
- package/hz.js +1 -1
- package/package.json +4 -1
- package/src/events.ts +21 -76
- package/src/funnels.ts +14 -154
- package/src/goals.test.ts +33 -0
- package/src/version.ts +1 -1
- package/src/funnels.test.ts +0 -92
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { eventsOf, EVENTS, PAGEVIEW, EXCEPTION } from '@hanzo/events';
|
|
2
|
+
export { EVENTS, EXCEPTION, FUNNELS, PAGEVIEW, PRODUCTS, eventsOf } from '@hanzo/events';
|
|
3
|
+
|
|
1
4
|
// src/attribution.ts
|
|
2
5
|
var SOCIAL_HOSTS = [
|
|
3
6
|
"facebook.",
|
|
@@ -46,16 +49,16 @@ function deriveChannel(a) {
|
|
|
46
49
|
}
|
|
47
50
|
function hostOf(raw) {
|
|
48
51
|
if (!raw) return "";
|
|
49
|
-
let
|
|
50
|
-
const scheme =
|
|
51
|
-
if (scheme >= 0)
|
|
52
|
-
const cut =
|
|
53
|
-
if (cut >= 0)
|
|
54
|
-
const at =
|
|
55
|
-
if (at >= 0)
|
|
56
|
-
const colon =
|
|
57
|
-
if (colon >= 0)
|
|
58
|
-
return
|
|
52
|
+
let s = raw.trim();
|
|
53
|
+
const scheme = s.indexOf("://");
|
|
54
|
+
if (scheme >= 0) s = s.slice(scheme + 3);
|
|
55
|
+
const cut = s.search(/[/?#]/);
|
|
56
|
+
if (cut >= 0) s = s.slice(0, cut);
|
|
57
|
+
const at = s.indexOf("@");
|
|
58
|
+
if (at >= 0) s = s.slice(at + 1);
|
|
59
|
+
const colon = s.indexOf(":");
|
|
60
|
+
if (colon >= 0) s = s.slice(0, colon);
|
|
61
|
+
return s.toLowerCase().trim();
|
|
59
62
|
}
|
|
60
63
|
function hasAttribution(a) {
|
|
61
64
|
return Boolean(
|
|
@@ -87,59 +90,6 @@ function dsnForProduct(product, key) {
|
|
|
87
90
|
return `https://${key}@api.hanzo.ai/v1/sentry/${projectId}`;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
// src/events.ts
|
|
91
|
-
var EVENTS = {
|
|
92
|
-
// Signup funnel: view -> submit -> verify -> completed -> first action.
|
|
93
|
-
SIGNUP_VIEWED: "signup_viewed",
|
|
94
|
-
SIGNUP_SUBMITTED: "signup_submitted",
|
|
95
|
-
SIGNUP_VERIFIED: "signup_verified",
|
|
96
|
-
SIGNUP_COMPLETED: "signup_completed",
|
|
97
|
-
/** A RETURNING user authenticated — the non-signup half of the IAM callback.
|
|
98
|
-
* Keeping it distinct is what stops returning logins from inflating signups. */
|
|
99
|
-
LOGIN_COMPLETED: "login_completed",
|
|
100
|
-
/** Activation: the first moment of real value. ONE event for every product —
|
|
101
|
-
* the product-specific moment is the `action` property (api_call, app_live,
|
|
102
|
-
* chat_reply), never a new event name. */
|
|
103
|
-
FIRST_ACTION: "first_action",
|
|
104
|
-
// Waitlist + referral.
|
|
105
|
-
WAITLIST_JOINED: "waitlist_joined",
|
|
106
|
-
WAITLIST_SHARED: "waitlist_shared",
|
|
107
|
-
REFERRAL_USED: "referral_used",
|
|
108
|
-
REFERRAL_CLAIMED: "referral_claimed",
|
|
109
|
-
// Upgrade-intent + purchase.
|
|
110
|
-
PRICING_VIEWED: "pricing_viewed",
|
|
111
|
-
PLAN_CLICKED: "plan_clicked",
|
|
112
|
-
CHECKOUT_STARTED: "checkout_started",
|
|
113
|
-
ORDER_COMPLETED: "order_completed",
|
|
114
|
-
// Feature usage — generic + the common key surfaces across products.
|
|
115
|
-
FEATURE_USED: "feature_used",
|
|
116
|
-
API_KEY_CREATED: "api_key_created",
|
|
117
|
-
APP_CREATED: "app_created",
|
|
118
|
-
PROJECT_CREATED: "project_created",
|
|
119
|
-
AGENT_CREATED: "agent_created",
|
|
120
|
-
CHAT_STARTED: "chat_started",
|
|
121
|
-
CHAT_MESSAGE_SENT: "chat_message_sent",
|
|
122
|
-
/** The user switched model/endpoint — the single strongest quality signal a
|
|
123
|
-
* chat surface emits (a switch usually follows a bad answer). */
|
|
124
|
-
MODEL_SWITCHED: "model_switched",
|
|
125
|
-
TASK_STARTED: "task_started",
|
|
126
|
-
TASK_COMPLETED: "task_completed",
|
|
127
|
-
// Build → ship. `build_*` is a MODEL producing an artifact; `deploy_*` is that
|
|
128
|
-
// artifact going live. Intent (build_started) is never the same event as the
|
|
129
|
-
// artifact existing (app_created) — conflating them makes the funnel lie.
|
|
130
|
-
BUILD_STARTED: "build_started",
|
|
131
|
-
/** A model finished producing an artifact (an app build, a chat reply, an agent
|
|
132
|
-
* run). Carries `durationMs` — the outcome event owns its own duration, so no
|
|
133
|
-
* paired start event is needed. */
|
|
134
|
-
GENERATION_COMPLETED: "generation_completed",
|
|
135
|
-
GENERATION_FAILED: "generation_failed",
|
|
136
|
-
DEPLOY_STARTED: "deploy_started",
|
|
137
|
-
DEPLOY_SUCCEEDED: "deploy_succeeded",
|
|
138
|
-
DEPLOY_FAILED: "deploy_failed"
|
|
139
|
-
};
|
|
140
|
-
var PAGEVIEW = "$pageview";
|
|
141
|
-
var EXCEPTION = "$exception";
|
|
142
|
-
|
|
143
93
|
// src/scrub.ts
|
|
144
94
|
var REDACTED = "[redacted]";
|
|
145
95
|
var EMAIL_MARK = "[email]";
|
|
@@ -188,8 +138,8 @@ function luhn(digits) {
|
|
|
188
138
|
}
|
|
189
139
|
return sum % 10 === 0;
|
|
190
140
|
}
|
|
191
|
-
function redactPAN(
|
|
192
|
-
return
|
|
141
|
+
function redactPAN(s) {
|
|
142
|
+
return s.replace(RE_PAN, (m) => {
|
|
193
143
|
const digits = m.replace(/[ -]/g, "");
|
|
194
144
|
if (digits.length < 13 || digits.length > 19) return m;
|
|
195
145
|
return luhn(digits) ? REDACTED : m;
|
|
@@ -234,30 +184,30 @@ var RE_CREDENTIAL_PARAM = new RegExp(
|
|
|
234
184
|
"([?&#;](?:" + CREDENTIAL_PARAMS.join("|") + ")=)[^&#;\\s]{1,4096}",
|
|
235
185
|
"gi"
|
|
236
186
|
);
|
|
237
|
-
function redactCredentialParams(
|
|
238
|
-
return
|
|
187
|
+
function redactCredentialParams(s) {
|
|
188
|
+
return s.replace(RE_CREDENTIAL_PARAM, (_m, prefix) => prefix + REDACTED);
|
|
239
189
|
}
|
|
240
|
-
function redactSecrets(
|
|
241
|
-
|
|
242
|
-
for (const re of SECRET_PATTERNS)
|
|
243
|
-
return redactPAN(
|
|
190
|
+
function redactSecrets(s) {
|
|
191
|
+
s = redactCredentialParams(s);
|
|
192
|
+
for (const re of SECRET_PATTERNS) s = s.replace(re, REDACTED);
|
|
193
|
+
return redactPAN(s);
|
|
244
194
|
}
|
|
245
|
-
function scrubPII(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return
|
|
195
|
+
function scrubPII(s) {
|
|
196
|
+
s = s.replace(RE_EMAIL, EMAIL_MARK);
|
|
197
|
+
s = s.replace(RE_IPV6, IP_MARK);
|
|
198
|
+
s = s.replace(RE_IPV4, IP_MARK);
|
|
199
|
+
return s;
|
|
250
200
|
}
|
|
251
201
|
var MAX_SCRUB_LEN = 8192;
|
|
252
|
-
function truncate(
|
|
253
|
-
return
|
|
202
|
+
function truncate(s, max = MAX_SCRUB_LEN) {
|
|
203
|
+
return s.length > max ? s.slice(0, max) + "\u2026 [truncated]" : s;
|
|
254
204
|
}
|
|
255
|
-
function scrubText(
|
|
256
|
-
if (!
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (!capturePII)
|
|
260
|
-
return
|
|
205
|
+
function scrubText(s, capturePII = false) {
|
|
206
|
+
if (!s) return s ?? "";
|
|
207
|
+
s = truncate(s);
|
|
208
|
+
s = redactSecrets(s);
|
|
209
|
+
if (!capturePII) s = scrubPII(s);
|
|
210
|
+
return s;
|
|
261
211
|
}
|
|
262
212
|
|
|
263
213
|
// src/anon.js
|
|
@@ -351,12 +301,12 @@ function hzAnonWrite(name, value) {
|
|
|
351
301
|
}
|
|
352
302
|
function hzAnonId() {
|
|
353
303
|
if (typeof window === "undefined") return "";
|
|
354
|
-
var
|
|
355
|
-
var id = hzAnonCookie(HZ_ANON_KEY) || hzAnonItem(
|
|
304
|
+
var s = hzAnonStore();
|
|
305
|
+
var id = hzAnonCookie(HZ_ANON_KEY) || hzAnonItem(s, HZ_ANON_KEY) || hzAnonItem(s, HZ_ANON_LEGACY_KEY) || hzAnonMemo || hzUuidv7();
|
|
356
306
|
hzAnonMemo = id;
|
|
357
307
|
hzAnonWrite(HZ_ANON_KEY, id);
|
|
358
308
|
try {
|
|
359
|
-
if (
|
|
309
|
+
if (s && s.getItem(HZ_ANON_KEY) !== id) s.setItem(HZ_ANON_KEY, id);
|
|
360
310
|
} catch (e) {
|
|
361
311
|
}
|
|
362
312
|
return id;
|
|
@@ -368,7 +318,7 @@ function uuidv7Time(id) {
|
|
|
368
318
|
}
|
|
369
319
|
|
|
370
320
|
// src/version.ts
|
|
371
|
-
var VERSION = "0.3.
|
|
321
|
+
var VERSION = "0.3.23";
|
|
372
322
|
|
|
373
323
|
// src/sentry.ts
|
|
374
324
|
var MAX_FRAMES = 50;
|
|
@@ -379,10 +329,10 @@ var MAX_TAGS = 50;
|
|
|
379
329
|
function eventId() {
|
|
380
330
|
return hzUuidv7().replace(/-/g, "");
|
|
381
331
|
}
|
|
382
|
-
function byteLen(
|
|
383
|
-
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(
|
|
384
|
-
if (typeof Buffer !== "undefined") return Buffer.byteLength(
|
|
385
|
-
return
|
|
332
|
+
function byteLen(s) {
|
|
333
|
+
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(s).length;
|
|
334
|
+
if (typeof Buffer !== "undefined") return Buffer.byteLength(s, "utf8");
|
|
335
|
+
return s.length;
|
|
386
336
|
}
|
|
387
337
|
function parseDsn(dsn) {
|
|
388
338
|
if (!dsn) return null;
|
|
@@ -483,7 +433,7 @@ function str(v) {
|
|
|
483
433
|
}
|
|
484
434
|
}
|
|
485
435
|
function coerceTag(v) {
|
|
486
|
-
const
|
|
436
|
+
const s = typeof v === "string" ? v : (() => {
|
|
487
437
|
try {
|
|
488
438
|
return JSON.stringify(v) ?? String(v);
|
|
489
439
|
} catch {
|
|
@@ -494,7 +444,7 @@ function coerceTag(v) {
|
|
|
494
444
|
}
|
|
495
445
|
}
|
|
496
446
|
})();
|
|
497
|
-
return
|
|
447
|
+
return s.length > MAX_TAG_LEN ? s.slice(0, MAX_TAG_LEN) : s;
|
|
498
448
|
}
|
|
499
449
|
function buildSentryEvent(input) {
|
|
500
450
|
const { error, options = {}, identity, capturePII = false } = input;
|
|
@@ -561,12 +511,12 @@ ${payload}
|
|
|
561
511
|
`;
|
|
562
512
|
}
|
|
563
513
|
var MAX_VALUE = 4096;
|
|
564
|
-
function digest(
|
|
514
|
+
function digest(s) {
|
|
565
515
|
let out = "";
|
|
566
516
|
for (const seed of [2166136261, 16777619, 2654435769, 2246822507]) {
|
|
567
517
|
let h = seed >>> 0;
|
|
568
|
-
for (let i = 0; i <
|
|
569
|
-
h ^=
|
|
518
|
+
for (let i = 0; i < s.length; i++) {
|
|
519
|
+
h ^= s.charCodeAt(i);
|
|
570
520
|
h = h + (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24) >>> 0;
|
|
571
521
|
}
|
|
572
522
|
out += h.toString(16).padStart(8, "0");
|
|
@@ -595,12 +545,12 @@ function frameOf(f) {
|
|
|
595
545
|
module: null
|
|
596
546
|
};
|
|
597
547
|
}
|
|
598
|
-
function truncate2(
|
|
599
|
-
if (
|
|
548
|
+
function truncate2(s, max) {
|
|
549
|
+
if (s.length <= max) return s;
|
|
600
550
|
let end = max;
|
|
601
|
-
const c =
|
|
551
|
+
const c = s.charCodeAt(end - 1);
|
|
602
552
|
if (c >= 55296 && c <= 56319) end--;
|
|
603
|
-
return
|
|
553
|
+
return s.slice(0, end);
|
|
604
554
|
}
|
|
605
555
|
function exceptionEntry(err, opts) {
|
|
606
556
|
const n = normalizeError(err);
|
|
@@ -644,8 +594,8 @@ function exceptionProperties(err, opts) {
|
|
|
644
594
|
$exception_synthetic: entry.mechanism?.synthetic ?? false,
|
|
645
595
|
$exception_types: [entry.type],
|
|
646
596
|
$exception_values: [entry.value],
|
|
647
|
-
$exception_sources: frames.map((f) => f.source).filter((
|
|
648
|
-
$exception_functions: frames.map((f) => f.resolved_name ?? f.mangled_name).filter((
|
|
597
|
+
$exception_sources: frames.map((f) => f.source).filter((s) => !!s),
|
|
598
|
+
$exception_functions: frames.map((f) => f.resolved_name ?? f.mangled_name).filter((s) => !!s)
|
|
649
599
|
};
|
|
650
600
|
}
|
|
651
601
|
|
|
@@ -668,11 +618,11 @@ function anonId() {
|
|
|
668
618
|
return hzAnonId() || void 0;
|
|
669
619
|
}
|
|
670
620
|
function sessionId(now = Date.now()) {
|
|
671
|
-
const
|
|
672
|
-
if (!
|
|
621
|
+
const s = ls();
|
|
622
|
+
if (!s) return void 0;
|
|
673
623
|
let state = null;
|
|
674
624
|
try {
|
|
675
|
-
state = JSON.parse(
|
|
625
|
+
state = JSON.parse(s.getItem(KEY.session) || "null");
|
|
676
626
|
} catch {
|
|
677
627
|
state = null;
|
|
678
628
|
}
|
|
@@ -681,45 +631,45 @@ function sessionId(now = Date.now()) {
|
|
|
681
631
|
} else {
|
|
682
632
|
state.last = now;
|
|
683
633
|
}
|
|
684
|
-
|
|
634
|
+
s.setItem(KEY.session, JSON.stringify(state));
|
|
685
635
|
return state.id;
|
|
686
636
|
}
|
|
687
637
|
function getFirstTouch() {
|
|
688
|
-
const
|
|
689
|
-
if (!
|
|
638
|
+
const s = ls();
|
|
639
|
+
if (!s) return void 0;
|
|
690
640
|
try {
|
|
691
|
-
const v =
|
|
641
|
+
const v = s.getItem(KEY.firstTouch);
|
|
692
642
|
return v ? JSON.parse(v) : void 0;
|
|
693
643
|
} catch {
|
|
694
644
|
return void 0;
|
|
695
645
|
}
|
|
696
646
|
}
|
|
697
647
|
function setFirstTouchOnce(a) {
|
|
698
|
-
const
|
|
648
|
+
const s = ls();
|
|
699
649
|
const existing = getFirstTouch();
|
|
700
650
|
if (existing) return existing;
|
|
701
|
-
if (
|
|
651
|
+
if (s) s.setItem(KEY.firstTouch, JSON.stringify(a));
|
|
702
652
|
return a;
|
|
703
653
|
}
|
|
704
654
|
function getCohort() {
|
|
705
|
-
const
|
|
706
|
-
if (!
|
|
655
|
+
const s = ls();
|
|
656
|
+
if (!s) return void 0;
|
|
707
657
|
try {
|
|
708
|
-
const v =
|
|
658
|
+
const v = s.getItem(KEY.cohort);
|
|
709
659
|
return v ? JSON.parse(v) : void 0;
|
|
710
660
|
} catch {
|
|
711
661
|
return void 0;
|
|
712
662
|
}
|
|
713
663
|
}
|
|
714
664
|
function mergeCohort(patch) {
|
|
715
|
-
const
|
|
665
|
+
const s = ls();
|
|
716
666
|
const cur = getCohort() || {};
|
|
717
667
|
const next = {
|
|
718
668
|
signupWeek: cur.signupWeek || patch.signupWeek,
|
|
719
669
|
channel: patch.channel || cur.channel,
|
|
720
670
|
refCode: cur.refCode || patch.refCode
|
|
721
671
|
};
|
|
722
|
-
if (
|
|
672
|
+
if (s) s.setItem(KEY.cohort, JSON.stringify(next));
|
|
723
673
|
return next;
|
|
724
674
|
}
|
|
725
675
|
|
|
@@ -1079,108 +1029,6 @@ function createAnalytics(config) {
|
|
|
1079
1029
|
return new Analytics(config);
|
|
1080
1030
|
}
|
|
1081
1031
|
|
|
1082
|
-
// src/funnels.ts
|
|
1083
|
-
var PRODUCTS = ["site", "app", "chat", "console", "admin", "cloud"];
|
|
1084
|
-
var s = (event, label, where) => ({
|
|
1085
|
-
event,
|
|
1086
|
-
label,
|
|
1087
|
-
...where ? { where } : {}
|
|
1088
|
-
});
|
|
1089
|
-
var FUNNELS = {
|
|
1090
|
-
/** hanzo.ai: land → sign up. IAM hosts the form, so `signup_submitted` is the
|
|
1091
|
-
* redirect INTO IAM and `signup_completed` is the return at /auth/callback. */
|
|
1092
|
-
signup: {
|
|
1093
|
-
label: "Signup",
|
|
1094
|
-
products: ["site"],
|
|
1095
|
-
join: "person",
|
|
1096
|
-
steps: [
|
|
1097
|
-
s(PAGEVIEW, "Landed"),
|
|
1098
|
-
s(EVENTS.SIGNUP_VIEWED, "Opened signup"),
|
|
1099
|
-
s(EVENTS.SIGNUP_SUBMITTED, "Redirected to Hanzo ID"),
|
|
1100
|
-
s(EVENTS.SIGNUP_COMPLETED, "Account created"),
|
|
1101
|
-
s(EVENTS.FIRST_ACTION, "First action")
|
|
1102
|
-
]
|
|
1103
|
-
},
|
|
1104
|
-
/** The developer activation path: an account is worth nothing until a key has
|
|
1105
|
-
* made a call. `first_action{action:'api_call'}` is emitted SERVER-SIDE by
|
|
1106
|
-
* Cloud on an org's first successful /v1 request — a browser cannot see it. */
|
|
1107
|
-
apiActivation: {
|
|
1108
|
-
label: "API activation",
|
|
1109
|
-
products: ["site", "cloud"],
|
|
1110
|
-
join: "person",
|
|
1111
|
-
steps: [
|
|
1112
|
-
s(EVENTS.SIGNUP_COMPLETED, "Account created"),
|
|
1113
|
-
s(EVENTS.API_KEY_CREATED, "Key minted"),
|
|
1114
|
-
s(EVENTS.FIRST_ACTION, "First successful API call", {
|
|
1115
|
-
property: "action",
|
|
1116
|
-
equals: "api_call"
|
|
1117
|
-
})
|
|
1118
|
-
]
|
|
1119
|
-
},
|
|
1120
|
-
/** Upgrade intent → revenue. `order_completed{kind:'plan'}` is the Sale goal. */
|
|
1121
|
-
upgrade: {
|
|
1122
|
-
label: "Upgrade",
|
|
1123
|
-
products: ["site", "app", "console"],
|
|
1124
|
-
join: "person",
|
|
1125
|
-
steps: [
|
|
1126
|
-
s(EVENTS.PRICING_VIEWED, "Viewed pricing"),
|
|
1127
|
-
s(EVENTS.PLAN_CLICKED, "Chose a plan"),
|
|
1128
|
-
s(EVENTS.CHECKOUT_STARTED, "Started checkout"),
|
|
1129
|
-
s(EVENTS.ORDER_COMPLETED, "Paid")
|
|
1130
|
-
]
|
|
1131
|
-
},
|
|
1132
|
-
/** hanzo.app: describe → build → deploy → live URL. The whole product thesis
|
|
1133
|
-
* in five steps; `deploy_succeeded` is the moment a live URL exists. */
|
|
1134
|
-
appShip: {
|
|
1135
|
-
label: "Describe \u2192 ship",
|
|
1136
|
-
products: ["app"],
|
|
1137
|
-
join: "person",
|
|
1138
|
-
steps: [
|
|
1139
|
-
s(PAGEVIEW, "Landed"),
|
|
1140
|
-
s(EVENTS.BUILD_STARTED, "Described an app"),
|
|
1141
|
-
s(EVENTS.GENERATION_COMPLETED, "Got a working build"),
|
|
1142
|
-
s(EVENTS.DEPLOY_STARTED, "Hit publish"),
|
|
1143
|
-
s(EVENTS.DEPLOY_SUCCEEDED, "Live URL")
|
|
1144
|
-
]
|
|
1145
|
-
},
|
|
1146
|
-
/** hanzo.chat: visit → first message → answer. `generation_completed` is what
|
|
1147
|
-
* separates "typed something" from "got value". */
|
|
1148
|
-
chatEngage: {
|
|
1149
|
-
label: "Chat engagement",
|
|
1150
|
-
products: ["chat"],
|
|
1151
|
-
join: "person",
|
|
1152
|
-
steps: [
|
|
1153
|
-
s(PAGEVIEW, "Landed"),
|
|
1154
|
-
s(EVENTS.CHAT_STARTED, "Started a conversation"),
|
|
1155
|
-
s(EVENTS.CHAT_MESSAGE_SENT, "Sent a message"),
|
|
1156
|
-
s(EVENTS.GENERATION_COMPLETED, "Got an answer")
|
|
1157
|
-
]
|
|
1158
|
-
},
|
|
1159
|
-
/** The cross-surface handoff: the hanzo.ai composer forwards its prompt to
|
|
1160
|
-
* hanzo.chat. Two origins, two anonymousIds — so this is an AGGREGATE funnel.
|
|
1161
|
-
* The join is the `referrerProduct` property hanzo.chat reads off `?hz_ref=`,
|
|
1162
|
-
* which makes the drop-off measurable without any cross-domain identity. */
|
|
1163
|
-
siteToChat: {
|
|
1164
|
-
label: "Site \u2192 Chat handoff",
|
|
1165
|
-
products: ["site", "chat"],
|
|
1166
|
-
join: "aggregate",
|
|
1167
|
-
steps: [
|
|
1168
|
-
s(EVENTS.CHAT_STARTED, "Submitted the hanzo.ai composer", {
|
|
1169
|
-
property: "source",
|
|
1170
|
-
equals: "composer"
|
|
1171
|
-
}),
|
|
1172
|
-
s(EVENTS.CHAT_STARTED, "Landed in hanzo.chat", {
|
|
1173
|
-
property: "referrerProduct",
|
|
1174
|
-
equals: "site"
|
|
1175
|
-
}),
|
|
1176
|
-
s(EVENTS.GENERATION_COMPLETED, "Got an answer")
|
|
1177
|
-
]
|
|
1178
|
-
}
|
|
1179
|
-
};
|
|
1180
|
-
function eventsOf(id) {
|
|
1181
|
-
return FUNNELS[id].steps.map((step) => step.event);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1032
|
// src/goals.ts
|
|
1185
1033
|
var GOALS = {
|
|
1186
1034
|
// Signup: the conversion is signup_completed, along the site signup funnel.
|
|
@@ -1241,6 +1089,6 @@ var COHORTS = {
|
|
|
1241
1089
|
* reformat of one copy is a diff against the other.
|
|
1242
1090
|
*/
|
|
1243
1091
|
|
|
1244
|
-
export { Analytics, COHORTS,
|
|
1092
|
+
export { Analytics, COHORTS, GOALS, PRODUCT_PROJECT, VERSION, buildEnvelope, buildSentryEvent, createAnalytics, deriveChannel, digest, dsnForProduct, exceptionEntry, exceptionProperties, fingerprint, framesFromStack, getCohort, getFirstTouch, hasAttribution, hostOf, isoWeek, parseAttribution, parseDsn, redactSecrets, scrubPII, scrubText, hzUuidv7 as uuidv7, uuidv7Time };
|
|
1245
1093
|
//# sourceMappingURL=index.mjs.map
|
|
1246
1094
|
//# sourceMappingURL=index.mjs.map
|