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