@hanzo/event 0.3.6 → 0.3.9

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/README.md CHANGED
@@ -27,10 +27,28 @@ starve the other:
27
27
  > plane is **inert**: nothing is sent, nothing throws, analytics is unaffected.
28
28
  > Assert `client.errorPlaneEnabled` if you want to know which you have.
29
29
 
30
- Web analytics (analytics.hanzo.ai) is a **third, separate** plane and is NOT this
31
- client — it is the `hz.js` tag, which speaks a different wire (a bare JSON array).
32
- Note that `analytics.hanzo.ai/v1/event` and `api.hanzo.ai/v1/event` share a path
33
- spelling but are **different protocols**; point this client at the API host.
30
+ ### No bundler? Use the `hz.js` tag same client, same wire
31
+
32
+ ```html
33
+ <script async src="https://unpkg.com/@hanzo/event/hz.js"
34
+ data-product="hanzo.ai"
35
+ data-capture="1"></script>
36
+ ```
37
+
38
+ `hz.js` (in this package) is the **script-tag distribution** of this client, for
39
+ surfaces with no build step. It posts the SAME `{ batch: [WireEvent, …] }` to the
40
+ SAME `POST {host}/v1/event`, and adds DOM **autocapture** — `$click` (with an
41
+ element locator), `$outbound`, `$scroll`, `$form`, `$vitals` — which a bundled app
42
+ does not need and a plain page cannot get. Manual API: `window.hanzo.track()` /
43
+ `identify()` / `page()`.
44
+
45
+ > The tag used to live in `hanzoai/analytics` and post a **bare JSON array** of
46
+ > `{site, ts, type, path, …}` to `analytics.hanzo.ai/v1/event` — a second protocol
47
+ > behind an identical path spelling, served by a second collector with its own
48
+ > database. `POST api.hanzo.ai/v1/event {"batch":[]}` answered 200 while
49
+ > `POST analytics.hanzo.ai/v1/event []` answered 204, and a client pointed at the
50
+ > wrong host failed silently. Both the second door and the second collector are
51
+ > deleted. One wire, one door, one client home — this package.
34
52
 
35
53
  - **Batched** with a size + interval flush, and **beacon-on-unload**
36
54
  (`sendBeacon` for cookie/publishable-key apps, `fetch(keepalive)` for token apps).
package/dist/index.cjs CHANGED
@@ -219,8 +219,34 @@ function scrubText(s2, capturePII = false) {
219
219
  return s2;
220
220
  }
221
221
 
222
+ // src/uid.ts
223
+ var HEX = Array.from({ length: 256 }, (_, i) => (i + 256).toString(16).slice(1));
224
+ function fill(b) {
225
+ const c = typeof crypto !== "undefined" ? crypto : void 0;
226
+ if (c && typeof c.getRandomValues === "function") {
227
+ c.getRandomValues(b);
228
+ return b;
229
+ }
230
+ for (let i = 0; i < b.length; i++) b[i] = Math.random() * 256 | 0;
231
+ return b;
232
+ }
233
+ function uuidv7(now = Date.now()) {
234
+ const b = fill(new Uint8Array(16));
235
+ let t = Math.floor(now);
236
+ for (let i = 5; i >= 0; i--) {
237
+ b[i] = t % 256;
238
+ t = Math.floor(t / 256);
239
+ }
240
+ b[6] = 112 | b[6] & 15;
241
+ b[8] = 128 | b[8] & 63;
242
+ 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]];
243
+ }
244
+ function uuidv7Time(id) {
245
+ return parseInt(id.slice(0, 8) + id.slice(9, 13), 16);
246
+ }
247
+
222
248
  // src/version.ts
223
- var VERSION = "0.3.6";
249
+ var VERSION = "0.3.9";
224
250
 
225
251
  // src/sentry.ts
226
252
  var MAX_FRAMES = 50;
@@ -229,11 +255,7 @@ var MAX_LINE_LEN = 2048;
229
255
  var MAX_TAG_LEN = 1024;
230
256
  var MAX_TAGS = 50;
231
257
  function eventId() {
232
- const c = typeof crypto !== "undefined" ? crypto : void 0;
233
- if (c && "randomUUID" in c) return c.randomUUID().replace(/-/g, "");
234
- let s2 = "";
235
- for (let i = 0; i < 32; i++) s2 += Math.floor(Math.random() * 16).toString(16);
236
- return s2;
258
+ return uuidv7().replace(/-/g, "");
237
259
  }
238
260
  function byteLen(s2) {
239
261
  if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(s2).length;
@@ -433,17 +455,12 @@ function ls() {
433
455
  return void 0;
434
456
  }
435
457
  }
436
- function uid() {
437
- const c = typeof crypto !== "undefined" ? crypto : void 0;
438
- if (c && "randomUUID" in c) return c.randomUUID();
439
- return "a-" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
440
- }
441
458
  function anonId() {
442
459
  const s2 = ls();
443
460
  if (!s2) return void 0;
444
461
  let v = s2.getItem(KEY.anon);
445
462
  if (!v) {
446
- v = uid();
463
+ v = uuidv7();
447
464
  s2.setItem(KEY.anon, v);
448
465
  }
449
466
  return v;
@@ -458,7 +475,7 @@ function sessionId(now = Date.now()) {
458
475
  state = null;
459
476
  }
460
477
  if (!state || now - state.last > SESSION_TTL_MS) {
461
- state = { id: uid(), last: now };
478
+ state = { id: uuidv7(now), last: now };
462
479
  } else {
463
480
  state.last = now;
464
481
  }
@@ -527,11 +544,6 @@ function readEnv(name) {
527
544
  function appendQuery(url, key, value) {
528
545
  return url + (url.includes("?") ? "&" : "?") + key + "=" + encodeURIComponent(value);
529
546
  }
530
- function uid2() {
531
- const c = typeof crypto !== "undefined" ? crypto : void 0;
532
- if (c && "randomUUID" in c) return c.randomUUID();
533
- return "m-" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
534
- }
535
547
  function normalizeError2(err) {
536
548
  const n = normalizeError(err);
537
549
  return { type: n.name, message: n.message, stack: n.stack };
@@ -661,9 +673,8 @@ var Analytics = class {
661
673
  }
662
674
  /** pageview records a $pageview for the current (or given) location. */
663
675
  pageview(path, properties) {
664
- const url = isBrowser() ? window.location.href : void 0;
665
676
  const p = path ?? (isBrowser() ? window.location.pathname : void 0);
666
- this.enqueue("pageview", PAGEVIEW, { url, path: p, properties });
677
+ this.enqueue("pageview", PAGEVIEW, { path: p, properties });
667
678
  }
668
679
  /** capture records a named product event with optional properties. Commerce
669
680
  * fields (productId/quantity/revenue/currency) may be passed for order events. */
@@ -783,8 +794,8 @@ var Analytics = class {
783
794
  }
784
795
  build(kind, event, extra) {
785
796
  const anon = anonId();
786
- return {
787
- messageId: uid2(),
797
+ const wire = {
798
+ messageId: uuidv7(),
788
799
  type: kind,
789
800
  event,
790
801
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -816,6 +827,11 @@ var Analytics = class {
816
827
  libraryVersion: VERSION,
817
828
  ...extra
818
829
  };
830
+ const capturePII = this.cfg.capturePII ?? false;
831
+ if (wire.url) wire.url = scrubText(wire.url, capturePII);
832
+ if (wire.path) wire.path = scrubText(wire.path, capturePII);
833
+ if (wire.referrer) wire.referrer = scrubText(wire.referrer, capturePII);
834
+ return wire;
819
835
  }
820
836
  schedule() {
821
837
  if (this.timer || !this.cfg.enabled) return;
@@ -1002,5 +1018,7 @@ exports.parseDsn = parseDsn;
1002
1018
  exports.redactSecrets = redactSecrets;
1003
1019
  exports.scrubPII = scrubPII;
1004
1020
  exports.scrubText = scrubText;
1021
+ exports.uuidv7 = uuidv7;
1022
+ exports.uuidv7Time = uuidv7Time;
1005
1023
  //# sourceMappingURL=index.cjs.map
1006
1024
  //# sourceMappingURL=index.cjs.map