@hanzo/event 0.3.14 → 0.3.16

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.d.ts CHANGED
@@ -6,7 +6,7 @@ declare function getFirstTouch(): Attribution | undefined;
6
6
  /** Read persisted cohort dimensions. */
7
7
  declare function getCohort(): Cohort | undefined;
8
8
 
9
- declare const VERSION = "0.3.14";
9
+ declare const VERSION = "0.3.16";
10
10
 
11
11
  /**
12
12
  * parseDsn parses "https://<version>:<hmac>@<host>/v1/sentry/<projectId>" into its
@@ -60,13 +60,20 @@ declare function buildSentryEvent(input: BuildEventInput): SentryEvent;
60
60
  */
61
61
  declare function buildEnvelope(event: SentryEvent, dsn: Dsn, sentAt?: string): string;
62
62
 
63
+ // Types for anon.js, which is hand-written ES5 rather than TypeScript because
64
+ // hz.js and the door's hosted tag inline it VERBATIM and neither has a compiler.
65
+ // The declarations are here so the bundled client still imports it typed.
66
+
67
+ /** Mints a time-ordered UUIDv7 (RFC 9562 §5.7) for `now` in epoch milliseconds. */
68
+ declare function hzUuidv7(now?: number): string
69
+
63
70
  /**
64
71
  * uuidv7 mints a time-ordered UUIDv7 for `now` (epoch milliseconds).
65
72
  *
66
73
  * Two ids minted in the same millisecond sort arbitrarily between themselves; ids
67
74
  * from different milliseconds sort by time, lexically and numerically alike.
68
75
  */
69
- declare function uuidv7(now?: number): string;
76
+
70
77
  /** The millisecond timestamp a v7 id was minted at — the inverse of uuidv7. */
71
78
  declare function uuidv7Time(id: string): number;
72
79
 
@@ -283,4 +290,4 @@ declare function hasAttribution(a: Attribution): boolean;
283
290
  /** isoWeek returns the ISO-8601 week label, e.g. "2026-W28". */
284
291
  declare function isoWeek(d: Date): string;
285
292
 
286
- export { Attribution, COHORTS, CaptureErrorOptions, Cohort, type CohortDef, Dsn, EVENTS, type ErrorIdentity, type EventName, FUNNELS, type FunnelDef, type FunnelId, type FunnelStep, GOALS, type GoalDef, PAGEVIEW, PRODUCTS, PRODUCT_DSN, type ProductId, SentryEvent, SentryFrame, VERSION, buildEnvelope, buildSentryEvent, deriveChannel, dsnForProduct, eventsOf, framesFromStack, getCohort, getFirstTouch, hasAttribution, hostOf, isoWeek, parseAttribution, parseDsn, redactSecrets, scrubPII, scrubText, uuidv7, uuidv7Time };
293
+ export { Attribution, COHORTS, CaptureErrorOptions, Cohort, type CohortDef, Dsn, EVENTS, type ErrorIdentity, type EventName, FUNNELS, type FunnelDef, type FunnelId, type FunnelStep, GOALS, type GoalDef, PAGEVIEW, PRODUCTS, PRODUCT_DSN, type ProductId, SentryEvent, SentryFrame, VERSION, buildEnvelope, buildSentryEvent, deriveChannel, dsnForProduct, eventsOf, framesFromStack, getCohort, getFirstTouch, hasAttribution, hostOf, isoWeek, parseAttribution, parseDsn, redactSecrets, scrubPII, scrubText, hzUuidv7 as uuidv7, uuidv7Time };
package/dist/index.mjs CHANGED
@@ -257,34 +257,115 @@ function scrubText(s2, capturePII = false) {
257
257
  return s2;
258
258
  }
259
259
 
260
- // src/uid.ts
261
- var HEX = Array.from({ length: 256 }, (_, i) => (i + 256).toString(16).slice(1));
262
- function fill(b) {
263
- const c = typeof crypto !== "undefined" ? crypto : void 0;
264
- if (c && typeof c.getRandomValues === "function") {
265
- c.getRandomValues(b);
266
- return b;
267
- }
268
- for (let i = 0; i < b.length; i++) b[i] = Math.random() * 256 | 0;
269
- return b;
270
- }
271
- function uuidv7(now = Date.now()) {
272
- const b = fill(new Uint8Array(16));
273
- let t = Math.floor(now);
274
- for (let i = 5; i >= 0; i--) {
260
+ // src/anon.js
261
+ var HZ_ANON_KEY = "hz_anon_id";
262
+ var HZ_ANON_LEGACY_KEY = "hz_id";
263
+ var HZ_ANON_DOMAIN = "hanzo.ai";
264
+ var HZ_ANON_MAX_AGE = 2 * 365 * 24 * 60 * 60;
265
+ var hzAnonMemo;
266
+ function hzUuidv7(now) {
267
+ var b = new Uint8Array(16);
268
+ var i;
269
+ var c = typeof crypto !== "undefined" ? crypto : void 0;
270
+ if (c && typeof c.getRandomValues === "function") c.getRandomValues(b);
271
+ else for (i = 0; i < 16; i++) b[i] = Math.random() * 256 | 0;
272
+ var t = Math.floor(now === void 0 ? Date.now() : now);
273
+ for (i = 5; i >= 0; i--) {
275
274
  b[i] = t % 256;
276
275
  t = Math.floor(t / 256);
277
276
  }
278
277
  b[6] = 112 | b[6] & 15;
279
278
  b[8] = 128 | b[8] & 63;
280
- return HEX[b[0]] + HEX[b[1]] + HEX[b[2]] + HEX[b[3]] + "-" + HEX[b[4]] + HEX[b[5]] + "-" + HEX[b[6]] + HEX[b[7]] + "-" + HEX[b[8]] + HEX[b[9]] + "-" + HEX[b[10]] + HEX[b[11]] + HEX[b[12]] + HEX[b[13]] + HEX[b[14]] + HEX[b[15]];
279
+ var h = "";
280
+ for (i = 0; i < 16; i++) {
281
+ h += (b[i] + 256).toString(16).slice(1);
282
+ if (i === 3 || i === 5 || i === 7 || i === 9) h += "-";
283
+ }
284
+ return h;
285
+ }
286
+ function hzAnonJar() {
287
+ try {
288
+ if (typeof document === "undefined" || typeof document.cookie !== "string") return null;
289
+ return document;
290
+ } catch (e) {
291
+ return null;
292
+ }
293
+ }
294
+ function hzAnonStore() {
295
+ try {
296
+ if (typeof window === "undefined" || !window.localStorage) return null;
297
+ return window.localStorage;
298
+ } catch (e) {
299
+ return null;
300
+ }
301
+ }
302
+ function hzAnonItem(store, name) {
303
+ try {
304
+ return store && store.getItem(name) || "";
305
+ } catch (e) {
306
+ return "";
307
+ }
308
+ }
309
+ function hzAnonCookie(name) {
310
+ var d = hzAnonJar();
311
+ if (!d) return "";
312
+ var parts = d.cookie.split(";");
313
+ for (var i = 0; i < parts.length; i++) {
314
+ var eq = parts[i].indexOf("=");
315
+ if (eq < 0 || parts[i].slice(0, eq).trim() !== name) continue;
316
+ var v = parts[i].slice(eq + 1).trim();
317
+ if (!v) continue;
318
+ try {
319
+ return decodeURIComponent(v);
320
+ } catch (e) {
321
+ return v;
322
+ }
323
+ }
324
+ return "";
325
+ }
326
+ function hzAnonWrite(name, value) {
327
+ var d = hzAnonJar();
328
+ if (!d) return;
329
+ var host = "";
330
+ var secure = false;
331
+ try {
332
+ if (typeof window !== "undefined" && window.location) {
333
+ host = window.location.hostname || "";
334
+ secure = window.location.protocol === "https:";
335
+ }
336
+ } catch (e) {
337
+ }
338
+ var c = name + "=" + encodeURIComponent(value);
339
+ c += "; Path=/; Max-Age=" + HZ_ANON_MAX_AGE + "; SameSite=Lax";
340
+ if (("." + host).slice(-(HZ_ANON_DOMAIN.length + 1)) === "." + HZ_ANON_DOMAIN) {
341
+ c += "; Domain=" + HZ_ANON_DOMAIN;
342
+ }
343
+ if (secure) c += "; Secure";
344
+ try {
345
+ d.cookie = c;
346
+ } catch (e) {
347
+ }
281
348
  }
349
+ function hzAnonId() {
350
+ if (typeof window === "undefined") return "";
351
+ var s2 = hzAnonStore();
352
+ var id = hzAnonCookie(HZ_ANON_KEY) || hzAnonItem(s2, HZ_ANON_KEY) || hzAnonItem(s2, HZ_ANON_LEGACY_KEY) || hzAnonMemo || hzUuidv7();
353
+ hzAnonMemo = id;
354
+ hzAnonWrite(HZ_ANON_KEY, id);
355
+ try {
356
+ if (s2 && s2.getItem(HZ_ANON_KEY) !== id) s2.setItem(HZ_ANON_KEY, id);
357
+ } catch (e) {
358
+ }
359
+ return id;
360
+ }
361
+
362
+ // src/uid.ts
282
363
  function uuidv7Time(id) {
283
364
  return parseInt(id.slice(0, 8) + id.slice(9, 13), 16);
284
365
  }
285
366
 
286
367
  // src/version.ts
287
- var VERSION = "0.3.14";
368
+ var VERSION = "0.3.16";
288
369
 
289
370
  // src/sentry.ts
290
371
  var MAX_FRAMES = 50;
@@ -293,7 +374,7 @@ var MAX_LINE_LEN = 2048;
293
374
  var MAX_TAG_LEN = 1024;
294
375
  var MAX_TAGS = 50;
295
376
  function eventId() {
296
- return uuidv7().replace(/-/g, "");
377
+ return hzUuidv7().replace(/-/g, "");
297
378
  }
298
379
  function byteLen(s2) {
299
380
  if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(s2).length;
@@ -479,14 +560,11 @@ ${payload}
479
560
 
480
561
  // src/storage.ts
481
562
  var KEY = {
482
- anon: "hz_anon_id",
483
563
  session: "hz_session",
484
564
  firstTouch: "hz_first_touch",
485
565
  cohort: "hz_cohort"
486
566
  };
487
567
  var SESSION_TTL_MS = 30 * 60 * 1e3;
488
- var ANON_DOMAIN = "hanzo.ai";
489
- var ANON_MAX_AGE_S = 2 * 365 * 24 * 60 * 60;
490
568
  function ls() {
491
569
  try {
492
570
  if (typeof window === "undefined" || !window.localStorage) return void 0;
@@ -495,69 +573,8 @@ function ls() {
495
573
  return void 0;
496
574
  }
497
575
  }
498
- function jar() {
499
- try {
500
- if (typeof document === "undefined" || typeof document.cookie !== "string") return void 0;
501
- return document;
502
- } catch {
503
- return void 0;
504
- }
505
- }
506
- function getCookie(name) {
507
- const d = jar();
508
- if (!d) return void 0;
509
- for (const part of d.cookie.split(";")) {
510
- const eq = part.indexOf("=");
511
- if (eq < 0 || part.slice(0, eq).trim() !== name) continue;
512
- const v = part.slice(eq + 1).trim();
513
- if (!v) continue;
514
- try {
515
- return decodeURIComponent(v);
516
- } catch {
517
- return v;
518
- }
519
- }
520
- return void 0;
521
- }
522
- function setCookie(name, value) {
523
- const d = jar();
524
- if (!d) return;
525
- let host = "";
526
- let secure = false;
527
- try {
528
- if (typeof window !== "undefined" && window.location) {
529
- host = window.location.hostname || "";
530
- secure = window.location.protocol === "https:";
531
- }
532
- } catch {
533
- }
534
- const attrs = [
535
- // encodeURIComponent leaves a UUID byte-identical while making any value that
536
- // is not one unable to forge a `;` and inject an attribute.
537
- `${name}=${encodeURIComponent(value)}`,
538
- "Path=/",
539
- `Max-Age=${ANON_MAX_AGE_S}`,
540
- "SameSite=Lax"
541
- ];
542
- if (host === ANON_DOMAIN || host.endsWith(`.${ANON_DOMAIN}`)) attrs.push(`Domain=${ANON_DOMAIN}`);
543
- if (secure) attrs.push("Secure");
544
- try {
545
- d.cookie = attrs.join("; ");
546
- } catch {
547
- }
548
- }
549
- var memAnon;
550
576
  function anonId() {
551
- if (typeof window === "undefined") return void 0;
552
- const s2 = ls();
553
- const id = getCookie(KEY.anon) || s2?.getItem(KEY.anon) || memAnon || uuidv7();
554
- memAnon = id;
555
- setCookie(KEY.anon, id);
556
- try {
557
- if (s2 && s2.getItem(KEY.anon) !== id) s2.setItem(KEY.anon, id);
558
- } catch {
559
- }
560
- return id;
577
+ return hzAnonId() || void 0;
561
578
  }
562
579
  function sessionId(now = Date.now()) {
563
580
  const s2 = ls();
@@ -569,7 +586,7 @@ function sessionId(now = Date.now()) {
569
586
  state = null;
570
587
  }
571
588
  if (!state || now - state.last > SESSION_TTL_MS) {
572
- state = { id: uuidv7(now), last: now };
589
+ state = { id: hzUuidv7(now), last: now };
573
590
  } else {
574
591
  state.last = now;
575
592
  }
@@ -642,7 +659,7 @@ function normalizeError2(err) {
642
659
  const n = normalizeError(err);
643
660
  return { type: n.name, message: n.message, stack: n.stack };
644
661
  }
645
- var isBrowser = () => typeof window !== "undefined";
662
+ var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
646
663
  function serializeBatch(batch) {
647
664
  try {
648
665
  return JSON.stringify({ batch });
@@ -905,7 +922,7 @@ var Analytics = class {
905
922
  build(kind, event, extra) {
906
923
  const anon = anonId();
907
924
  const wire = {
908
- messageId: uuidv7(),
925
+ messageId: hzUuidv7(),
909
926
  type: kind,
910
927
  event,
911
928
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1101,7 +1118,28 @@ var COHORTS = {
1101
1118
  channel: { field: "channel", label: "Acquisition channel" },
1102
1119
  refCode: { field: "ref_code", label: "Referral code" }
1103
1120
  };
1121
+ /*! anon.js — THE anonymous-identity chain. ONE implementation, three distributions.
1122
+ *
1123
+ * One browser is ONE person on every Hanzo surface, whichever client a page
1124
+ * happens to have loaded. There were three implementations writing TWO keys —
1125
+ * `hz_anon_id` (the npm client, the hosted tag) and `hz_id` (hz.js) — so the same
1126
+ * visitor was several people depending on which snippet the surface shipped.
1127
+ *
1128
+ * The three call sites:
1129
+ * 1. src/storage.ts — the bundled npm client; IMPORTS this file.
1130
+ * 2. hz.js — the no-build script tag; INLINES the marked region.
1131
+ * 3. hanzoai/cloud apps/analytics/tag.js — the tag the door hosts at
1132
+ * /v1/event.js; vendors this file and its tag.go serves the marked region
1133
+ * with the tag as one asset, so the door holds no second copy either.
1134
+ *
1135
+ * (2) and (3) have no bundler and cannot import anything, which is why the chain
1136
+ * lives in a file that is plain ES5 rather than in a .ts: the region between the
1137
+ * BEGIN and END markers is COPIED VERBATIM, and src/anon.test.ts fails if hz.js's
1138
+ * copy is so much as a byte different. Keep the region ES5, dependency-free,
1139
+ * `hz`-prefixed (it is spliced into other people's scopes) and unformatted — a
1140
+ * reformat of one copy is a diff against the other.
1141
+ */
1104
1142
 
1105
- export { Analytics, COHORTS, EVENTS, FUNNELS, GOALS, PAGEVIEW, PRODUCTS, PRODUCT_DSN, VERSION, buildEnvelope, buildSentryEvent, createAnalytics, deriveChannel, dsnForProduct, eventsOf, framesFromStack, getCohort, getFirstTouch, hasAttribution, hostOf, isoWeek, parseAttribution, parseDsn, redactSecrets, scrubPII, scrubText, uuidv7, uuidv7Time };
1143
+ export { Analytics, COHORTS, EVENTS, FUNNELS, GOALS, PAGEVIEW, PRODUCTS, PRODUCT_DSN, VERSION, buildEnvelope, buildSentryEvent, createAnalytics, deriveChannel, dsnForProduct, eventsOf, framesFromStack, getCohort, getFirstTouch, hasAttribution, hostOf, isoWeek, parseAttribution, parseDsn, redactSecrets, scrubPII, scrubText, hzUuidv7 as uuidv7, uuidv7Time };
1106
1144
  //# sourceMappingURL=index.mjs.map
1107
1145
  //# sourceMappingURL=index.mjs.map