@hanzo/event 0.3.10 → 0.3.12

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
@@ -32,9 +32,18 @@ starve the other:
32
32
  ```html
33
33
  <script async src="https://unpkg.com/@hanzo/event/hz.js"
34
34
  data-product="hanzo.ai"
35
+ data-ingest-key="pk-…"
35
36
  data-capture="1"></script>
36
37
  ```
37
38
 
39
+ > **`data-ingest-key` is required off the door's own origin**, and through
40
+ > 0.3.11 this file could not present one at all — no header, no query. A keyed
41
+ > static surface therefore sent UNATTRIBUTED writes, which the door refuses
42
+ > (`401 ingest_key_required`) silently, because nothing here reads the response.
43
+ > The tag measured fine in the browser and filed nothing. It now rides
44
+ > `Authorization: Bearer pk-…` on fetch and `?ingest_key=pk-…` on a headerless
45
+ > unload beacon — the same pair the npm client uses.
46
+
38
47
  `hz.js` (in this package) is the **script-tag distribution** of this client, for
39
48
  surfaces with no build step. It posts the SAME `{ batch: [WireEvent, …] }` to the
40
49
  SAME `POST {host}/v1/event`, and adds DOM **autocapture** — `$click` (with an
@@ -42,6 +51,12 @@ element locator), `$outbound`, `$scroll`, `$form`, `$vitals` — which a bundled
42
51
  does not need and a plain page cannot get. Manual API: `window.hanzo.track()` /
43
52
  `identify()` / `page()`.
44
53
 
54
+ It honours the same consent sources the bundled stack does: an explicit stored
55
+ choice (`hz_consent`, the key a Hanzo consent banner writes) outranks the browser
56
+ signal in both directions; otherwise Global Privacy Control and Do Not Track are
57
+ refusals. A React app does not need this file — mount `<Hanzo analytics>` from
58
+ `@hanzo/ui`, which wires this client and the capture engine together.
59
+
45
60
  > The tag used to live in `hanzoai/analytics` and post a **bare JSON array** of
46
61
  > `{site, ts, type, path, …}` to `analytics.hanzo.ai/v1/event` — a second protocol
47
62
  > behind an identical path spelling, served by a second collector with its own
@@ -108,9 +108,12 @@ interface AnalyticsConfig {
108
108
  * a reading principal — so it is safe to ship in a bundle. Mint one per org with
109
109
  * POST /v1/keys {"type":"publishable"}.
110
110
  *
111
- * Omit it and the client reads NEXT_PUBLIC_HANZO_EVENT_KEY, then HANZO_EVENT_KEY,
112
- * from the inlined build env the same resolution `dsn` uses, so a surface
113
- * declares BOTH planes the same way and neither needs code to switch on.
111
+ * Omit it and the client reads NEXT_PUBLIC_EVENT_INGEST_KEY from the inlined
112
+ * build env, the same way `dsn` falls back — so a surface declares BOTH planes
113
+ * in its build and neither needs code to switch on. That is the ONE spelling
114
+ * the fleet already carries: KMS holds deploy/EVENT_INGEST_KEY, and each
115
+ * Dockerfile takes EVENT_INGEST_KEY as a build-arg and re-exports it with the
116
+ * NEXT_PUBLIC_ prefix that makes Next inline it.
114
117
  *
115
118
  * A surface with no key at all still reports for whoever is SIGNED IN (the
116
119
  * session credential attributes them), and drops every logged-out visitor: the
@@ -108,9 +108,12 @@ interface AnalyticsConfig {
108
108
  * a reading principal — so it is safe to ship in a bundle. Mint one per org with
109
109
  * POST /v1/keys {"type":"publishable"}.
110
110
  *
111
- * Omit it and the client reads NEXT_PUBLIC_HANZO_EVENT_KEY, then HANZO_EVENT_KEY,
112
- * from the inlined build env the same resolution `dsn` uses, so a surface
113
- * declares BOTH planes the same way and neither needs code to switch on.
111
+ * Omit it and the client reads NEXT_PUBLIC_EVENT_INGEST_KEY from the inlined
112
+ * build env, the same way `dsn` falls back — so a surface declares BOTH planes
113
+ * in its build and neither needs code to switch on. That is the ONE spelling
114
+ * the fleet already carries: KMS holds deploy/EVENT_INGEST_KEY, and each
115
+ * Dockerfile takes EVENT_INGEST_KEY as a build-arg and re-exports it with the
116
+ * NEXT_PUBLIC_ prefix that makes Next inline it.
114
117
  *
115
118
  * A surface with no key at all still reports for whoever is SIGNED IN (the
116
119
  * session credential attributes them), and drops every logged-out visitor: the
package/dist/index.cjs CHANGED
@@ -246,7 +246,7 @@ function uuidv7Time(id) {
246
246
  }
247
247
 
248
248
  // src/version.ts
249
- var VERSION = "0.3.9";
249
+ var VERSION = "0.3.12";
250
250
 
251
251
  // src/sentry.ts
252
252
  var MAX_FRAMES = 50;
@@ -618,13 +618,21 @@ var Analytics = class {
618
618
  captureErrors: true,
619
619
  ...config,
620
620
  // The publishable key resolves the SAME way the DSN below does: an explicit
621
- // config wins, else the inlined build-time env. Without this the key was the
622
- // one piece of wiring a surface could not declare the way it declares every
623
- // other piece, so every surface that shipped without passing it in code sent
624
- // its beacons unattributed and an unattributed write is refused (401
625
- // ingest_key_required), which is silent in the page and invisible until you
626
- // read the warehouse and find the host missing entirely.
627
- ingestKey: config.ingestKey ?? readEnv("NEXT_PUBLIC_HANZO_EVENT_KEY") ?? readEnv("HANZO_EVENT_KEY")
621
+ // config wins, else the inlined build-time env.
622
+ //
623
+ // NEXT_PUBLIC_EVENT_INGEST_KEY is that env, and it is the name the fleet
624
+ // ALREADY carries end to end KMS holds deploy/EVENT_INGEST_KEY, each
625
+ // Dockerfile takes it as the EVENT_INGEST_KEY build-arg and re-exports it
626
+ // with the NEXT_PUBLIC_ prefix Next needs to inline it. Reading anything
627
+ // else here would add a fourth spelling of one value.
628
+ //
629
+ // Without this the key was the one piece of wiring a surface could not
630
+ // declare the way it declares every other piece, so a surface that shipped
631
+ // without passing it in code sent its beacons unattributed — and an
632
+ // unattributed write is refused (401 ingest_key_required), which is silent
633
+ // in the page and invisible until you read the warehouse and find the host
634
+ // missing entirely.
635
+ ingestKey: config.ingestKey ?? readEnv("NEXT_PUBLIC_EVENT_INGEST_KEY")
628
636
  };
629
637
  this.transport = config.transport ?? new DefaultTransport();
630
638
  this.dsn = parseDsn(config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product));
@@ -743,8 +751,8 @@ var Analytics = class {
743
751
  const batch = this.queue;
744
752
  this.queue = [];
745
753
  this.clearTimer();
746
- const key = this.cfg.ingestKey?.trim() || void 0;
747
- const token = key ? void 0 : this.cfg.getToken?.() ?? void 0;
754
+ const token = this.cfg.getToken?.() ?? void 0;
755
+ const key = token ? void 0 : this.cfg.ingestKey?.trim() || void 0;
748
756
  const useBeacon = beacon && !token;
749
757
  const body = serializeBatch(batch);
750
758
  if (body === null) {