@hanzo/event 0.3.36 → 0.3.38

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.
Files changed (60) hide show
  1. package/README.md +8 -8
  2. package/TAXONOMY.md +1 -1
  3. package/dist/attribution.d.ts +14 -0
  4. package/dist/attribution.d.ts.map +1 -0
  5. package/dist/core.d.ts +102 -0
  6. package/dist/core.d.ts.map +1 -0
  7. package/dist/dsn.d.ts +43 -0
  8. package/dist/dsn.d.ts.map +1 -0
  9. package/dist/events.d.ts +21 -0
  10. package/dist/events.d.ts.map +1 -0
  11. package/dist/exception.d.ts +50 -0
  12. package/dist/exception.d.ts.map +1 -0
  13. package/dist/funnels.d.ts +14 -0
  14. package/dist/funnels.d.ts.map +1 -0
  15. package/dist/goals.d.ts +24 -0
  16. package/dist/goals.d.ts.map +1 -0
  17. package/dist/index.cjs +11 -11
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +22 -292
  20. package/dist/index.d.ts +22 -292
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.mjs +11 -11
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/org.d.ts +77 -0
  25. package/dist/org.d.ts.map +1 -0
  26. package/dist/react.cjs +11 -11
  27. package/dist/react.cjs.map +1 -1
  28. package/dist/react.d.cts +13 -14
  29. package/dist/react.d.ts +13 -14
  30. package/dist/react.d.ts.map +1 -0
  31. package/dist/react.mjs +11 -11
  32. package/dist/react.mjs.map +1 -1
  33. package/dist/scrub.d.ts +20 -0
  34. package/dist/scrub.d.ts.map +1 -0
  35. package/dist/sentry.d.ts +62 -0
  36. package/dist/sentry.d.ts.map +1 -0
  37. package/dist/stack.d.ts +25 -0
  38. package/dist/stack.d.ts.map +1 -0
  39. package/dist/storage.d.ts +32 -0
  40. package/dist/storage.d.ts.map +1 -0
  41. package/dist/{core-CIhI2R7S.d.cts → types.d.ts} +21 -119
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/uid.d.ts +10 -0
  44. package/dist/uid.d.ts.map +1 -0
  45. package/dist/version.d.ts +2 -0
  46. package/dist/version.d.ts.map +1 -0
  47. package/package.json +12 -12
  48. package/src/anon.d.ts +1 -1
  49. package/src/anon.js +3 -3
  50. package/src/anon.test.ts +3 -3
  51. package/src/core.test.ts +6 -6
  52. package/src/core.ts +11 -11
  53. package/src/events.ts +1 -1
  54. package/src/exception.ts +1 -1
  55. package/src/org.ts +7 -4
  56. package/src/stack.ts +1 -1
  57. package/src/types.ts +3 -3
  58. package/src/uid.ts +2 -2
  59. package/src/version.ts +1 -1
  60. package/dist/core-CIhI2R7S.d.ts +0 -380
package/README.md CHANGED
@@ -34,8 +34,8 @@ starve the other:
34
34
  ```
35
35
 
36
36
  That one line is the whole install, and it is the same line for a published site,
37
- hanzo.team and a customer's own page. The tag is served by the door that eats the
38
- events, so a caller allowlists ONE host and the tag can never drift from the wire
37
+ hanzo.team and a customer's own page. The tag is served by the endpoint that eats
38
+ the events, so a caller allowlists ONE host and the tag can never drift from the wire
39
39
  it posts to — `.js` is part of a segment rather than a child of one, so the tag
40
40
  sits UNDER `/v1/event` rather than beside it, and the old sibling `/v1/event.js`
41
41
  404s.
@@ -48,18 +48,18 @@ config from `/v1/projects/tags`, so a pixel is configured on the project rather
48
48
  than pasted into the page.
49
49
 
50
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.
51
+ > endpoint, so a page running `@hanzo/event` and the tag counts every pageview twice.
52
52
  > One surface, one client.
53
53
 
54
54
  > **`data-key` is a publishable `pk-`, and without one the tag is INERT.** A write
55
- > the door cannot attribute is refused (`401 ingest_key_required`) silently, so a
55
+ > the edge cannot attribute is refused (`401 ingest_key_required`) silently, so a
56
56
  > keyless tag measures fine in the browser and files nothing. A project mints one
57
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
58
+ > `403 ingest_key_unknown` — the edge fails closed rather than filing a write it
59
59
  > cannot attribute.
60
60
 
61
61
  This package is the client for a surface that **builds**. There is no second
62
- script-tag distribution here: one wire, one door, one tag.
62
+ script-tag distribution here: one wire, one endpoint, one tag.
63
63
 
64
64
  It honours the same consent sources the bundled stack does: an explicit stored
65
65
  choice (`hz_consent`, the key a Hanzo consent banner writes) outranks the browser
@@ -72,8 +72,8 @@ refusals. A React app does not need this file — mount `<Hanzo analytics>` from
72
72
  > behind an identical path spelling, served by a second collector with its own
73
73
  > database. `POST api.hanzo.ai/v1/event {"batch":[]}` answered 200 while
74
74
  > `POST analytics.hanzo.ai/v1/event []` answered 204, and a client pointed at the
75
- > wrong host failed silently. Both the second door and the second collector are
76
- > deleted. One wire, one door, one client home — this package.
75
+ > wrong host failed silently. Both the second endpoint and the second collector
76
+ > are deleted. One wire, one endpoint, one client home — this package.
77
77
 
78
78
  - **Batched** with a size + interval flush, and **beacon-on-unload**
79
79
  (`sendBeacon` for cookie/publishable-key apps, `fetch(keepalive)` for token apps).
package/TAXONOMY.md CHANGED
@@ -238,7 +238,7 @@ Verified against the trees at the time of writing. ✅ = emitting, ❌ = not emi
238
238
  4. **No org on hanzo.chat.** `group()` is absent because the chat session carries
239
239
  no org today; chat funnels are person-scoped only.
240
240
  5. **Logged-out reach depends on a publishable key.** Anonymous events need
241
- `ingestKey` (`pk_…`, write-only) or they fail closed at the door.
241
+ `ingestKey` (`pk_…`, write-only) or they fail closed at the edge.
242
242
  `NEXT_PUBLIC_HANZO_INGEST_KEY` (hanzo.ai), `NEXT_PUBLIC_PUBLISHABLE_KEY`
243
243
  (hanzo.app), `VITE_HANZO_INGEST_KEY` (hanzo.chat) are read but must be
244
244
  provisioned per org via `POST /v1/ingest/keys`. Config, not code.
@@ -0,0 +1,14 @@
1
+ import type { Attribution } from './types';
2
+ /** parseAttribution reads UTM params + ref/refCode from a query string and pairs
3
+ * them with the referrer. `search` is a location.search value ("?utm_source=…"). */
4
+ export declare function parseAttribution(search: string, referrer: string): Attribution;
5
+ /** deriveChannel classifies the visit: paid | referral | social | organic | direct. */
6
+ export declare function deriveChannel(a: Attribution): string;
7
+ /** hostOf extracts a bare lowercase host from a URL; "" when unparseable. */
8
+ export declare function hostOf(raw?: string): string;
9
+ /** hasAttribution reports whether anything was captured (so we don't persist an
10
+ * empty first-touch that would shadow a later real one). */
11
+ export declare function hasAttribution(a: Attribution): boolean;
12
+ /** isoWeek returns the ISO-8601 week label, e.g. "2026-W28". */
13
+ export declare function isoWeek(d: Date): string;
14
+ //# sourceMappingURL=attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAQ1C;qFACqF;AACrF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmB9E;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAUpD;AAED,6EAA6E;AAC7E,wBAAgB,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAY3C;AAED;6DAC6D;AAC7D,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAKtD;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAQvC"}
package/dist/core.d.ts ADDED
@@ -0,0 +1,102 @@
1
+ import { getFirstTouch, getCohort } from './storage';
2
+ import type { AnalyticsConfig, CaptureErrorOptions, Cohort, WireEvent } from './types';
3
+ import { VERSION } from './version';
4
+ export { VERSION };
5
+ export declare class Analytics {
6
+ private cfg;
7
+ private transport;
8
+ private queue;
9
+ private timer;
10
+ private personId?;
11
+ private attribution;
12
+ private cohort;
13
+ private started;
14
+ /** The view pageview() last counted — path + location. */
15
+ private counted?;
16
+ /** Parsed error-plane DSN, or null when the plane is inert. */
17
+ private dsn;
18
+ /** Guards against an error thrown *inside* the error path re-entering it. */
19
+ private reentrant;
20
+ constructor(config: AnalyticsConfig);
21
+ /** adopt gives this client a credential it does not have. The key belongs to
22
+ * the stream, not to whichever caller happened to ask for the handle first,
23
+ * so a later caller carrying one hands it over. The error plane derives from
24
+ * the key, so it comes up here too when it was inert for want of one.
25
+ * Present fields are never overwritten: the first caller's key stays. */
26
+ adopt(config: AnalyticsConfig): void;
27
+ /** errorPlaneEnabled reports whether captured exceptions can actually reach the
28
+ * error host. False means a DSN was never configured — the documented
29
+ * fail-safe. Exposed so an app (or a test) can assert its wiring instead of
30
+ * discovering months later that nothing was ever reported. */
31
+ get errorPlaneEnabled(): boolean;
32
+ /** errorIngestUrl is the fully-derived envelope endpoint, or undefined when the
33
+ * plane is inert. Diagnostics only. */
34
+ get errorIngestUrl(): string | undefined;
35
+ /** init is idempotent and browser-only for its side effects: capture first-touch
36
+ * attribution, hydrate cohort, register the unload flush, and (unless opted out)
37
+ * auto-capture unhandled errors. Safe to call from a React effect on every
38
+ * render. */
39
+ init(): void;
40
+ /** identify binds the current visitor to a stable person id (post-login). */
41
+ identify(personId: string, traits?: Record<string, unknown>): void;
42
+ /** group associates the visitor with an org/team (analytics grouping, not the
43
+ * server tenant — the server still derives tenant from the session). */
44
+ group(groupId: string, traits?: Record<string, unknown>): void;
45
+ /** pageview records a $pageview for the current (or given) location, once per
46
+ * view. A view is the path plus the full location, so a query or hash change
47
+ * is a new one and a repeat call for the same place is not. */
48
+ pageview(path?: string, properties?: Record<string, unknown>): void;
49
+ /** capture records a named product event with optional properties. Commerce
50
+ * fields (productId/quantity/revenue/currency) may be passed for order events. */
51
+ capture(event: string, properties?: Record<string, unknown>, commerce?: Pick<WireEvent, 'productId' | 'quantity' | 'revenue' | 'currency'>): void;
52
+ /** captureError reports a caught error, an unhandled rejection, a React render
53
+ * error, or a manual report to BOTH planes, from one call:
54
+ *
55
+ * - the ERROR PLANE — a real Sentry envelope to the DSN host. This is the one
56
+ * that produces an issue in sentry.hanzo.ai (grouping, stack frames, AST).
57
+ * Inert when no DSN is configured.
58
+ * - the EVENT STREAM — a `type:'error'` row in the cloud event warehouse, so
59
+ * an error stays correlated with the session's pageviews for product
60
+ * analysis (readable via GET /v1/errors).
61
+ *
62
+ * Both carry the SAME session and subject id, so an error and the pageview
63
+ * before it join up. Never throws back into the app; errors are higher-signal
64
+ * than pageviews, so both planes flush promptly (a crash may unload the page
65
+ * moments later). */
66
+ captureError(err: unknown, context?: CaptureErrorOptions): void;
67
+ /** setCohort persists cohort dimensions (e.g. signupWeek at signup) so they ride
68
+ * every subsequent event. */
69
+ setCohort(patch: Cohort): void;
70
+ /** flush drains the buffer to the server as ONE batch through the ONE ingest
71
+ * endpoint POST /v1/event, body { batch: [Event…] }. beacon=true selects the
72
+ * unload-safe transport. Auth is orthogonal to the wire:
73
+ *
74
+ * • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each
75
+ * a CORS-simple request that any origin may send.
76
+ * • else a bearer JWT rides Authorization (fetch only — sendBeacon cannot
77
+ * carry a header, so token apps fall back to keepalive fetch on unload).
78
+ * • else a cookie app rides same-origin credentials (beacon carries the
79
+ * cookie fine).
80
+ */
81
+ flush(beacon?: boolean): void;
82
+ /** sendError frames one exception as a Sentry envelope and posts it to the DSN's
83
+ * ingest URL. The DSN's own key rides ?sentry_key= (the credential channel the
84
+ * server trusts, and the only one a headerless beacon can carry), so NO bearer
85
+ * or publishable key is attached here — the two planes authenticate
86
+ * independently. Errors are sent one envelope per event, immediately: batching
87
+ * a crash report is how you lose it. */
88
+ private sendError;
89
+ /** errorIdentity is the SAME identity the event stream stamps — the OIDC subject
90
+ * once identify() has run, else the anon id. Never email/PII. */
91
+ private errorIdentity;
92
+ private enqueue;
93
+ private build;
94
+ private schedule;
95
+ private clearTimer;
96
+ }
97
+ /** createAnalytics returns the client for a stream, building it on first ask.
98
+ * This is the ONE way to get a client: `new Analytics` bypasses the registry
99
+ * and is for tests and for a deliberately separate instance. */
100
+ export declare function createAnalytics(config: AnalyticsConfig): Analytics;
101
+ export { getCohort, getFirstTouch };
102
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AA2DA,OAAO,EAGL,aAAa,EAEb,SAAS,EAEV,MAAM,WAAW,CAAA;AAElB,OAAO,KAAK,EACV,eAAe,EAEf,mBAAmB,EACnB,MAAM,EAKN,SAAS,EACV,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,OAAO,EAAE,CAAA;AA2KlB,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAGM;IACjB,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,QAAQ,CAAC,CAAQ;IACzB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAQ;IACvB,0DAA0D;IAC1D,OAAO,CAAC,OAAO,CAAC,CAAQ;IACxB,+DAA+D;IAC/D,OAAO,CAAC,GAAG,CAAY;IACvB,6EAA6E;IAC7E,OAAO,CAAC,SAAS,CAAQ;IAEzB,YAAY,MAAM,EAAE,eAAe,EAwClC;IAED;;;;8EAI0E;IAC1E,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAQnC;IAED;;;mEAG+D;IAC/D,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAED;4CACwC;IACxC,IAAI,cAAc,IAAI,MAAM,GAAG,SAAS,CAEvC;IAED;;;kBAGc;IACd,IAAI,IAAI,IAAI,CAgCX;IAED,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAGjE;IAED;6EACyE;IACzE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE7D;IAED;;oEAEgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAclE;IAED;uFACmF;IACnF,OAAO,CACL,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC,GAC5E,IAAI,CAEN;IAED;;;;;;;;;;;;;0BAasB;IACtB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAuD9D;IAED;kCAC8B;IAC9B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,MAAM,UAAQ,GAAG,IAAI,CAmC1B;IAID;;;;;6CAKyC;IACzC,OAAO,CAAC,SAAS;IAiBjB;sEACkE;IAClE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,KAAK;IA+Db,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,UAAU;CAMnB;AAiCD;;iEAEiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAmBlE;AAID,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA"}
package/dist/dsn.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The product → Sentinel DSN registry.
3
+ *
4
+ * An app declares WHAT it is (`product: 'console'`); this module knows WHERE its
5
+ * errors go. That split is the whole point: no surface has to learn a DSN, carry
6
+ * a build argument, or grow a config file to report errors — declaring the
7
+ * product it already declares is enough.
8
+ *
9
+ * A Sentinel DSN is PUBLIC by construction. It ships inside the client bundle and
10
+ * is readable in devtools on any deployed page, and it grants exactly one
11
+ * capability: submitting new events. It cannot read issues, projects, or any
12
+ * other data. So committing it is not leaking a secret — it is recording a public
13
+ * identifier next to the code that needs it. (Contrast the server-side collector
14
+ * DSN in the `team-analytics-sentry` Secret, which is HMAC-derived and revocable
15
+ * precisely because a server-side credential is NOT public.)
16
+ *
17
+ * Why a literal map instead of deriving `hanzo-${product}`: the projects predate
18
+ * this registry and do not derive cleanly — `site` lives in `hanzo-ai`, not
19
+ * `hanzo-site`. An explicit map is honest about that; a derivation rule plus an
20
+ * exception table is the same data with a trap in it.
21
+ *
22
+ * Projects are org-scoped and named `<org>-<app>`. To add one: create the project
23
+ * (POST /v1/sentinel/projects with X-Org-Id), then add its `dsn` here keyed by the
24
+ * product name the app passes to `createAnalytics`.
25
+ */
26
+ /** PRODUCT_PROJECT maps a `product` to its Sentinel project id. The DSN's KEY is no
27
+ * longer a per-project secret — it is the ONE org publishable key (below), so a
28
+ * surface's errors ride the SAME key its events do. The id only names WHICH
29
+ * project the errors group under, and cloud auto-provisions that project on first
30
+ * keyed ingest, so a new id needs nothing minted. `site` lives in the `hanzo-ai`
31
+ * project — an explicit map, because the projects predate this and do not derive
32
+ * cleanly from the product name. */
33
+ export declare const PRODUCT_PROJECT: Readonly<Record<string, string>>;
34
+ /** dsnForProduct builds the product's Sentinel DSN from the caller's resolved
35
+ * publishable `key` and the product's project — the SAME key the event stream
36
+ * carries, at the product's envelope endpoint. Returns undefined when there is no
37
+ * key or no project for the product, leaving the error plane inert rather than
38
+ * posting into the wrong one. The key is NOT baked here: it is the value the
39
+ * surface resolved (an explicit `ingestKey`, or the KMS-sourced
40
+ * NEXT_PUBLIC_PUBLISHABLE_KEY the build inlines) — the ONE live source, never a
41
+ * literal committed beside the code. */
42
+ export declare function dsnForProduct(product: string | undefined, key: string | undefined): string | undefined;
43
+ //# sourceMappingURL=dsn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsn.d.ts","sourceRoot":"","sources":["../src/dsn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;qCAMqC;AACrC,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI3D,CAAA;AAEF;;;;;;;yCAOyC;AACzC,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,MAAM,GAAG,SAAS,CAKpB"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The vocabulary, re-exported from where it is now defined.
3
+ *
4
+ * These names used to be declared here. They moved to @hanzo/events — the
5
+ * plural package is the catalog (many events, what each means, what each
6
+ * carries), this one is the client (one call sends one event). Splitting them
7
+ * that way is what lets the ingest endpoint read the same catalog: it is Go
8
+ * and cannot import TypeScript, so @hanzo/events also ships the data as JSON,
9
+ * and there is still exactly one definition.
10
+ *
11
+ * Nothing about the API moved. Every existing import keeps working, the names
12
+ * and their values are unchanged, and `pkgs/events/test/catalog.mjs` fails the
13
+ * build if a name and its meaning ever disagree.
14
+ *
15
+ * The naming convention lives with the names, in @hanzo/events and TAXONOMY.md:
16
+ * snake_case `<object>_<verb-past>`, a dimension is a PROPERTY and never part
17
+ * of the name, one name per user-visible moment shared by every surface.
18
+ */
19
+ export { EVENTS, PAGEVIEW, EXCEPTION } from '@hanzo/events';
20
+ export type { EventName } from '@hanzo/events';
21
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,50 @@
1
+ import type { ExceptionEntry, ExceptionProperties, SentryLevel } from './types';
2
+ /**
3
+ * digest is a stable 32-hex-char (128-bit) content hash, computed synchronously.
4
+ *
5
+ * Grouping keys are needed on the capture path, which is synchronous and may be
6
+ * running inside an unload handler — SubtleCrypto is async and unavailable on
7
+ * insecure origins, so it cannot be used here. This is FNV-1a run over four seeds
8
+ * and concatenated. It is a GROUPING key, never a security boundary: it is not
9
+ * collision-resistant against a chosen-input adversary, and nothing authorizes or
10
+ * authenticates on it. The product treats the value as opaque.
11
+ */
12
+ export declare function digest(s: string): string;
13
+ /**
14
+ * exceptionEntry builds the single `$exception_list` entry for a throwable.
15
+ *
16
+ * One entry, not a chain: `Error.cause` chaining is a distinct fact with its own
17
+ * ordering rules, and emitting it wrongly is worse than not emitting it.
18
+ */
19
+ export declare function exceptionEntry(err: unknown, opts: {
20
+ handled: boolean;
21
+ id: string;
22
+ }): ExceptionEntry;
23
+ /**
24
+ * fingerprint is the issue grouping key.
25
+ *
26
+ * Keyed on exception type plus each in-app frame's function and source — the same
27
+ * pieces the server-side grouper records ("Exception Type", "Resolved function
28
+ * name", "Source file name"). Deliberately NOT the message: `Loading chunk 3324
29
+ * failed` and `Loading chunk 998 failed` are one bug, and grouping on message is
30
+ * precisely the mistake that made every distinct error string its own event name.
31
+ *
32
+ * Falls back to the type alone when no in-app frame survived, which keeps
33
+ * stackless errors (`Script error.`, cross-origin) in one issue instead of
34
+ * scattering them.
35
+ */
36
+ export declare function fingerprint(entry: ExceptionEntry): string;
37
+ /**
38
+ * exceptionProperties builds the full `$exception_*` property bag for one captured
39
+ * throwable — everything Error Tracking reads off the event.
40
+ *
41
+ * The denormalized arrays are ordered like `frames`: last element is the throw
42
+ * site, which is the element the issue list indexes at -1 for its source/function
43
+ * columns.
44
+ */
45
+ export declare function exceptionProperties(err: unknown, opts: {
46
+ handled: boolean;
47
+ id: string;
48
+ level?: SentryLevel;
49
+ }): ExceptionProperties;
50
+ //# sourceMappingURL=exception.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exception.d.ts","sourceRoot":"","sources":["../src/exception.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EACV,cAAc,EAEd,mBAAmB,EAEnB,WAAW,EACZ,MAAM,SAAS,CAAA;AAQhB;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAYxC;AA6CD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACrC,cAAc,CAqBhB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAMzD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,GAC1D,mBAAmB,CAsBrB"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The funnel registry, re-exported from where it is now defined.
3
+ *
4
+ * The funnels moved to @hanzo/events for the reason the names did: the read lens
5
+ * is Go and cannot import TypeScript, and that package already writes its data
6
+ * out as JSON. Defining a funnel beside the vocabulary also makes the anti-drift
7
+ * check local — a step naming an event that does not exist is caught where both
8
+ * are declared.
9
+ *
10
+ * Nothing about the API moved. Every existing import keeps working.
11
+ */
12
+ export { FUNNELS, PRODUCTS, eventsOf } from '@hanzo/events';
13
+ export type { FunnelDef, FunnelStep, FunnelId, ProductId } from '@hanzo/events';
14
+ //# sourceMappingURL=funnels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"funnels.d.ts","sourceRoot":"","sources":["../src/funnels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC3D,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { type FunnelId } from './funnels';
2
+ export interface GoalDef {
3
+ /** Human label shown in Insights. */
4
+ label: string;
5
+ /** The event whose occurrence counts as the goal conversion. */
6
+ event: string;
7
+ /** The funnel leading to the goal — an id into FUNNELS (see funnels.ts). */
8
+ funnelId?: FunnelId;
9
+ /** The ordered event names of `funnelId`, derived — never hand-written. */
10
+ funnel?: string[];
11
+ /** Optional property equality filter that qualifies the conversion. */
12
+ filter?: {
13
+ property: string;
14
+ equals: string;
15
+ };
16
+ }
17
+ export declare const GOALS: Record<'signup' | 'sale' | 'upgradeIntent' | 'activation', GoalDef>;
18
+ export interface CohortDef {
19
+ /** The hanzo.events column the cohort dimension maps to. */
20
+ field: string;
21
+ label: string;
22
+ }
23
+ export declare const COHORTS: Record<'signupWeek' | 'channel' | 'refCode', CohortDef>;
24
+ //# sourceMappingURL=goals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goals.d.ts","sourceRoot":"","sources":["../src/goals.ts"],"names":[],"mappings":"AAUA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEnD,MAAM,WAAW,OAAO;IACtB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,uEAAuE;IACvE,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9C;AAED,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,eAAe,GAAG,YAAY,EAAE,OAAO,CA+BrF,CAAA;AAED,MAAM,WAAW,SAAS;IACxB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,YAAY,GAAG,SAAS,GAAG,SAAS,EAAE,SAAS,CAI3E,CAAA"}
package/dist/index.cjs CHANGED
@@ -120,12 +120,12 @@ var ORG_DOMAIN = Object.freeze({
120
120
  "lux.fund",
121
121
  "lux.id"
122
122
  ]),
123
- zoo: Object.freeze(["zoo.ngo", "zoo.network", "zoolabs.id"])
123
+ zoo: Object.freeze(["zoo.ngo", "zoo.network", "zoolabs.id", "zoolabs.io"])
124
124
  });
125
125
  var ORG_KEY = Object.freeze({
126
- hanzo: "pk-rM_CdaF2MQckGCrla113SrR1oH4zvqN8xh2I95Z9tY8",
127
- lux: "pk-gUZp6ZVfhJzSwK-rb4oLbVkpCnMBx5uSCpxf_5yEhQk",
128
- zoo: "pk-3TKpKnERV9AQSsBUERWkZejC1O1mUxc1jRzsP3MPbs4"
126
+ hanzo: "pk-bCcNBDOLwdIovV349jVGH1ShEOq7rAcaKLsX_bLnRMo",
127
+ lux: "pk-GxCDhpEn89qajMyuMdMs6mcE4UVQBkv8F2swEKHC67Y",
128
+ zoo: "pk-bS-xKESMhuhK_wfSmpRf1H_IH8A3x6Y5NV70TD7ycWE"
129
129
  });
130
130
  var PUBLISHABLE = "pk-";
131
131
  var normalize = (host) => host.trim().toLowerCase().replace(/\.$/, "").split(":")[0] ?? "";
@@ -392,7 +392,7 @@ function uuidv7Time(id) {
392
392
  }
393
393
 
394
394
  // src/version.ts
395
- var VERSION = "0.3.36";
395
+ var VERSION = "0.3.37";
396
396
 
397
397
  // src/sentry.ts
398
398
  var MAX_FRAMES = 50;
@@ -862,10 +862,10 @@ var Analytics = class {
862
862
  // so before this the only way to report was to commit a key literal, and the
863
863
  // fleet grew one copy per site of a value with a single source.
864
864
  //
865
- // A surface no brand claims still has no key, and the door REFUSES an
865
+ // A surface no brand claims still has no key, and the edge REFUSES an
866
866
  // unattributed event (401 `ingest_key_required`) — the reserved `$public`
867
867
  // tenant that once caught keyless beacons is retired, and anonymous ingest is
868
- // refused at every door on every brand host. So there is no quiet fallback to
868
+ // refused at every edge on every brand host. So there is no quiet fallback to
869
869
  // rely on: an event lands in the org a credential names, or it does not land.
870
870
  ingestKey: config.ingestKey ?? readEnv("NEXT_PUBLIC_PUBLISHABLE_KEY") ?? keyForPage()
871
871
  };
@@ -1003,7 +1003,7 @@ var Analytics = class {
1003
1003
  this.cohort = mergeCohort(patch);
1004
1004
  }
1005
1005
  /** flush drains the buffer to the server as ONE batch through the ONE ingest
1006
- * front door POST /v1/event, body { batch: [Event…] }. beacon=true selects the
1006
+ * endpoint POST /v1/event, body { batch: [Event…] }. beacon=true selects the
1007
1007
  * unload-safe transport. Auth is orthogonal to the wire:
1008
1008
  *
1009
1009
  * • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each
@@ -1200,15 +1200,15 @@ var COHORTS = {
1200
1200
  *
1201
1201
  * The two call sites:
1202
1202
  * 1. src/storage.ts — the bundled npm client; IMPORTS this file.
1203
- * 2. hanzoai/cloud apps/analytics/tag.js — the tag the door hosts at
1203
+ * 2. hanzoai/cloud apps/analytics/tag.js — the tag served at
1204
1204
  * /v1/event.js; vendors this file and its tag.go serves the marked region
1205
- * with the tag as one asset, so the door holds no second copy either.
1205
+ * with the tag as one asset, so cloud holds no second copy either.
1206
1206
  *
1207
1207
  * (2) has no bundler and cannot import anything, which is why the chain lives in a
1208
1208
  * file that is plain ES5 rather than in a .ts: the region between the BEGIN and END
1209
1209
  * markers is COPIED VERBATIM. Keep it ES5, dependency-free, `hz`-prefixed (it is
1210
1210
  * spliced into other people's scopes) and unformatted — a reformat here is a diff
1211
- * against the vendored copy. After editing, resync the door:
1211
+ * against the vendored copy. After editing, resync that copy:
1212
1212
  *
1213
1213
  * curl -fsSL https://unpkg.com/@hanzo/event/src/anon.js -o apps/analytics/anon.js
1214
1214
  */