@hanzo/event 0.3.33 → 0.3.35
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 +19 -7
- package/dist/index.cjs +80 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -2
- package/dist/index.d.ts +76 -2
- package/dist/index.mjs +76 -11
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +75 -10
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +75 -10
- package/dist/react.mjs.map +1 -1
- package/package.json +10 -10
- package/src/anon.d.ts +1 -1
- package/src/anon.js +29 -9
- package/src/anon.test.ts +1 -1
- package/src/core.ts +16 -6
- package/src/index.ts +7 -0
- package/src/org.test.ts +113 -0
- package/src/org.ts +129 -0
- package/src/storage.test.ts +1 -1
- package/src/version.ts +1 -1
- package/LICENSE.md +0 -21
package/README.md
CHANGED
|
@@ -30,21 +30,33 @@ starve the other:
|
|
|
30
30
|
### No bundler? Use the hosted tag
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
|
-
<script defer src="https://api.hanzo.ai/v1/event.js" data-key="pk-…"></script>
|
|
33
|
+
<script defer src="https://api.hanzo.ai/v1/event/tag.js" data-key="pk-…"></script>
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
That one line is the whole install, and it is the same line for a published site,
|
|
37
37
|
hanzo.team and a customer's own page. The tag is served by the door that eats the
|
|
38
38
|
events, so a caller allowlists ONE host and the tag can never drift from the wire
|
|
39
|
-
it posts to
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
it posts to — `.js` is part of a segment rather than a child of one, so the tag
|
|
40
|
+
sits UNDER `/v1/event` rather than beside it, and the old sibling `/v1/event.js`
|
|
41
|
+
404s.
|
|
42
|
+
|
|
43
|
+
It autocaptures pageviews — the first one and every SPA navigation, since
|
|
44
|
+
`history.pushState` fires no event of its own — plus uncaught errors and rejected
|
|
45
|
+
promises. Anything a page means on purpose goes through `window.hanzo`
|
|
46
|
+
(`track`, `identify`, `page`, `error`, `flush`). It also carries the site's tag
|
|
47
|
+
config from `/v1/projects/tags`, so a pixel is configured on the project rather
|
|
48
|
+
than pasted into the page.
|
|
49
|
+
|
|
50
|
+
> A bundled app should NOT add this tag. Both clients post pageviews to the same
|
|
51
|
+
> door, so a page running `@hanzo/event` and the tag counts every pageview twice.
|
|
52
|
+
> One surface, one client.
|
|
43
53
|
|
|
44
54
|
> **`data-key` is a publishable `pk-`, and without one the tag is INERT.** A write
|
|
45
55
|
> the door cannot attribute is refused (`401 ingest_key_required`) silently, so a
|
|
46
|
-
> keyless tag measures fine in the browser and files nothing.
|
|
47
|
-
> `POST /v1/
|
|
56
|
+
> keyless tag measures fine in the browser and files nothing. A project mints one
|
|
57
|
+
> with itself: `POST /v1/projects`. A key that names no project is refused
|
|
58
|
+
> `403 ingest_key_unknown` — the door fails closed rather than filing a write it
|
|
59
|
+
> cannot attribute.
|
|
48
60
|
|
|
49
61
|
This package is the client for a surface that **builds**. There is no second
|
|
50
62
|
script-tag distribution here: one wire, one door, one tag.
|
package/dist/index.cjs
CHANGED
|
@@ -91,6 +91,56 @@ function dsnForProduct(product, key) {
|
|
|
91
91
|
return `https://${key}@api.hanzo.ai/v1/event/${projectId}`;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// src/org.ts
|
|
95
|
+
var ORG_DOMAIN = Object.freeze({
|
|
96
|
+
hanzo: Object.freeze(["hanzo.ai", "hanzo.app", "hanzo.chat", "hanzo.id", "hanzo.bot", "hanzo.sh"]),
|
|
97
|
+
lux: Object.freeze([
|
|
98
|
+
"lux.network",
|
|
99
|
+
"lux.exchange",
|
|
100
|
+
"lux.market",
|
|
101
|
+
"lux.finance",
|
|
102
|
+
"lux.financial",
|
|
103
|
+
"lux.credit",
|
|
104
|
+
"lux.fund",
|
|
105
|
+
"lux.id"
|
|
106
|
+
]),
|
|
107
|
+
zoo: Object.freeze(["zoo.ngo", "zoo.network", "zoolabs.id"])
|
|
108
|
+
});
|
|
109
|
+
var ORG_KEY = Object.freeze({
|
|
110
|
+
hanzo: "pk-rM_CdaF2MQckGCrla113SrR1oH4zvqN8xh2I95Z9tY8",
|
|
111
|
+
lux: "pk-gUZp6ZVfhJzSwK-rb4oLbVkpCnMBx5uSCpxf_5yEhQk",
|
|
112
|
+
zoo: "pk-3TKpKnERV9AQSsBUERWkZejC1O1mUxc1jRzsP3MPbs4"
|
|
113
|
+
});
|
|
114
|
+
var PUBLISHABLE = "pk-";
|
|
115
|
+
var normalize = (host) => host.trim().toLowerCase().replace(/\.$/, "").split(":")[0] ?? "";
|
|
116
|
+
function orgOf(host) {
|
|
117
|
+
const h = normalize(host);
|
|
118
|
+
if (!h) return void 0;
|
|
119
|
+
let best;
|
|
120
|
+
let bestLen = 0;
|
|
121
|
+
for (const [org, domains] of Object.entries(ORG_DOMAIN)) {
|
|
122
|
+
for (const d of domains) {
|
|
123
|
+
if ((h === d || h.endsWith(`.${d}`)) && d.length > bestLen) {
|
|
124
|
+
best = org;
|
|
125
|
+
bestLen = d.length;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return best;
|
|
130
|
+
}
|
|
131
|
+
function keyFor(host, keyring = ORG_KEY) {
|
|
132
|
+
const org = orgOf(host);
|
|
133
|
+
if (!org) return void 0;
|
|
134
|
+
const key = keyring[org];
|
|
135
|
+
if (typeof key !== "string") return void 0;
|
|
136
|
+
const trimmed = key.trim();
|
|
137
|
+
return trimmed.startsWith(PUBLISHABLE) ? trimmed : void 0;
|
|
138
|
+
}
|
|
139
|
+
function keyForPage(keyring = ORG_KEY) {
|
|
140
|
+
if (typeof location === "undefined") return void 0;
|
|
141
|
+
return keyFor(location.hostname, keyring);
|
|
142
|
+
}
|
|
143
|
+
|
|
94
144
|
// src/scrub.ts
|
|
95
145
|
var REDACTED = "[redacted]";
|
|
96
146
|
var EMAIL_MARK = "[email]";
|
|
@@ -212,8 +262,8 @@ function scrubText(s, capturePII = false) {
|
|
|
212
262
|
}
|
|
213
263
|
|
|
214
264
|
// src/anon.js
|
|
215
|
-
var HZ_ANON_KEY = "
|
|
216
|
-
var
|
|
265
|
+
var HZ_ANON_KEY = "iam-anon-id";
|
|
266
|
+
var HZ_ANON_ADOPT = ["hz_anon_id", "hz_id"];
|
|
217
267
|
var HZ_ANON_DOMAIN = "hanzo.ai";
|
|
218
268
|
var HZ_ANON_MAX_AGE = 2 * 365 * 24 * 60 * 60;
|
|
219
269
|
var hzAnonMemo;
|
|
@@ -300,10 +350,17 @@ function hzAnonWrite(name, value) {
|
|
|
300
350
|
} catch (e) {
|
|
301
351
|
}
|
|
302
352
|
}
|
|
353
|
+
function hzAnonAdopted(s) {
|
|
354
|
+
for (var i = 0; i < HZ_ANON_ADOPT.length; i++) {
|
|
355
|
+
var id = hzAnonCookie(HZ_ANON_ADOPT[i]) || hzAnonItem(s, HZ_ANON_ADOPT[i]);
|
|
356
|
+
if (id) return id;
|
|
357
|
+
}
|
|
358
|
+
return "";
|
|
359
|
+
}
|
|
303
360
|
function hzAnonId() {
|
|
304
361
|
if (typeof window === "undefined") return "";
|
|
305
362
|
var s = hzAnonStore();
|
|
306
|
-
var id = hzAnonCookie(HZ_ANON_KEY) || hzAnonItem(s, HZ_ANON_KEY) ||
|
|
363
|
+
var id = hzAnonCookie(HZ_ANON_KEY) || hzAnonItem(s, HZ_ANON_KEY) || hzAnonAdopted(s) || hzAnonMemo || hzUuidv7();
|
|
307
364
|
hzAnonMemo = id;
|
|
308
365
|
hzAnonWrite(HZ_ANON_KEY, id);
|
|
309
366
|
try {
|
|
@@ -319,7 +376,7 @@ function uuidv7Time(id) {
|
|
|
319
376
|
}
|
|
320
377
|
|
|
321
378
|
// src/version.ts
|
|
322
|
-
var VERSION = "0.3.
|
|
379
|
+
var VERSION = "0.3.35";
|
|
323
380
|
|
|
324
381
|
// src/sentry.ts
|
|
325
382
|
var MAX_FRAMES = 50;
|
|
@@ -781,12 +838,20 @@ var Analytics = class {
|
|
|
781
838
|
// carries end to end — KMS `deploy/PUBLISHABLE_KEY` -> the PUBLISHABLE_KEY
|
|
782
839
|
// build-arg -> the NEXT_PUBLIC_ prefix Next inlines.
|
|
783
840
|
//
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
|
|
841
|
+
// 3. the org that owns the HOST this page is served from (`org.ts`).
|
|
842
|
+
//
|
|
843
|
+
// Step 3 fires only when a surface configured NOTHING, so it never overrides
|
|
844
|
+
// the KMS chain above — it replaces going dark with reporting to the right
|
|
845
|
+
// brand. That is what a static export needs: `output: export` inlines no env,
|
|
846
|
+
// so before this the only way to report was to commit a key literal, and the
|
|
847
|
+
// fleet grew one copy per site of a value with a single source.
|
|
848
|
+
//
|
|
849
|
+
// A surface no brand claims still has no key, and the door REFUSES an
|
|
850
|
+
// unattributed event (401 `ingest_key_required`) — the reserved `$public`
|
|
851
|
+
// tenant that once caught keyless beacons is retired, and anonymous ingest is
|
|
852
|
+
// refused at every door on every brand host. So there is no quiet fallback to
|
|
853
|
+
// rely on: an event lands in the org a credential names, or it does not land.
|
|
854
|
+
ingestKey: config.ingestKey ?? readEnv("NEXT_PUBLIC_PUBLISHABLE_KEY") ?? keyForPage()
|
|
790
855
|
};
|
|
791
856
|
this.transport = config.transport ?? new DefaultTransport();
|
|
792
857
|
this.dsn = parseDsn(
|
|
@@ -1159,6 +1224,8 @@ Object.defineProperty(exports, "eventsOf", {
|
|
|
1159
1224
|
exports.Analytics = Analytics;
|
|
1160
1225
|
exports.COHORTS = COHORTS;
|
|
1161
1226
|
exports.GOALS = GOALS;
|
|
1227
|
+
exports.ORG_DOMAIN = ORG_DOMAIN;
|
|
1228
|
+
exports.ORG_KEY = ORG_KEY;
|
|
1162
1229
|
exports.PRODUCT_PROJECT = PRODUCT_PROJECT;
|
|
1163
1230
|
exports.VERSION = VERSION;
|
|
1164
1231
|
exports.buildEnvelope = buildEnvelope;
|
|
@@ -1176,6 +1243,9 @@ exports.getFirstTouch = getFirstTouch;
|
|
|
1176
1243
|
exports.hasAttribution = hasAttribution;
|
|
1177
1244
|
exports.hostOf = hostOf;
|
|
1178
1245
|
exports.isoWeek = isoWeek;
|
|
1246
|
+
exports.keyFor = keyFor;
|
|
1247
|
+
exports.keyForPage = keyForPage;
|
|
1248
|
+
exports.orgOf = orgOf;
|
|
1179
1249
|
exports.parseAttribution = parseAttribution;
|
|
1180
1250
|
exports.parseDsn = parseDsn;
|
|
1181
1251
|
exports.redactSecrets = redactSecrets;
|