@hanzo/event 0.3.36 → 0.3.37

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/LICENSE.md +21 -0
  2. package/README.md +8 -8
  3. package/TAXONOMY.md +1 -1
  4. package/dist/attribution.d.ts +14 -0
  5. package/dist/attribution.d.ts.map +1 -0
  6. package/dist/core.d.ts +102 -0
  7. package/dist/core.d.ts.map +1 -0
  8. package/dist/dsn.d.ts +43 -0
  9. package/dist/dsn.d.ts.map +1 -0
  10. package/dist/events.d.ts +21 -0
  11. package/dist/events.d.ts.map +1 -0
  12. package/dist/exception.d.ts +50 -0
  13. package/dist/exception.d.ts.map +1 -0
  14. package/dist/funnels.d.ts +14 -0
  15. package/dist/funnels.d.ts.map +1 -0
  16. package/dist/goals.d.ts +24 -0
  17. package/dist/goals.d.ts.map +1 -0
  18. package/dist/index.cjs +7 -7
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +22 -292
  21. package/dist/index.d.ts +22 -292
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.mjs +7 -7
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/org.d.ts +74 -0
  26. package/dist/org.d.ts.map +1 -0
  27. package/dist/react.cjs +7 -7
  28. package/dist/react.cjs.map +1 -1
  29. package/dist/react.d.cts +13 -14
  30. package/dist/react.d.ts +13 -14
  31. package/dist/react.d.ts.map +1 -0
  32. package/dist/react.mjs +7 -7
  33. package/dist/react.mjs.map +1 -1
  34. package/dist/scrub.d.ts +20 -0
  35. package/dist/scrub.d.ts.map +1 -0
  36. package/dist/sentry.d.ts +62 -0
  37. package/dist/sentry.d.ts.map +1 -0
  38. package/dist/stack.d.ts +25 -0
  39. package/dist/stack.d.ts.map +1 -0
  40. package/dist/storage.d.ts +32 -0
  41. package/dist/storage.d.ts.map +1 -0
  42. package/dist/{core-CIhI2R7S.d.cts → types.d.ts} +21 -119
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/uid.d.ts +10 -0
  45. package/dist/uid.d.ts.map +1 -0
  46. package/dist/version.d.ts +2 -0
  47. package/dist/version.d.ts.map +1 -0
  48. package/package.json +11 -11
  49. package/src/anon.d.ts +1 -1
  50. package/src/anon.js +3 -3
  51. package/src/anon.test.ts +3 -3
  52. package/src/core.test.ts +6 -6
  53. package/src/core.ts +11 -11
  54. package/src/events.ts +1 -1
  55. package/src/exception.ts +1 -1
  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
@@ -0,0 +1,20 @@
1
+ /** redactCredentialParams removes the VALUE of any query parameter whose NAME
2
+ * says it carries a credential, leaving the name so the URL still reads. */
3
+ export declare function redactCredentialParams(s: string): string;
4
+ /** redactSecrets removes known secret shapes. Always applied. */
5
+ export declare function redactSecrets(s: string): string;
6
+ /** scrubPII masks emails and IPs. Applied unless PII capture is enabled. */
7
+ export declare function scrubPII(s: string): string;
8
+ /** Longest free-text field this module will scrub. Every pattern here is a regex
9
+ * run synchronously on the main thread, and the input is attacker-influenced —
10
+ * `throw new Error(await res.text())` against an HTML error page is a one-line
11
+ * way to hand us 128KB. Bounding the INPUT bounds the work regardless of which
12
+ * pattern is pathological. 8KB is far beyond any real error message. */
13
+ export declare const MAX_SCRUB_LEN = 8192;
14
+ /** truncate caps a string at MAX_SCRUB_LEN, marking that it was cut so a reader
15
+ * never mistakes a truncated message for the whole one. */
16
+ export declare function truncate(s: string, max?: number): string;
17
+ /** scrubText applies the redaction policy to a free-text field. Input is capped
18
+ * first: unbounded text is a denial-of-service surface, not just a size problem. */
19
+ export declare function scrubText(s: string | undefined, capturePII?: boolean): string;
20
+ //# sourceMappingURL=scrub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub.d.ts","sourceRoot":"","sources":["../src/scrub.ts"],"names":[],"mappings":"AA8GA;6EAC6E;AAC7E,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1C;AAED;;;;yEAIyE;AACzE,eAAO,MAAM,aAAa,OAAO,CAAA;AAEjC;4DAC4D;AAC5D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAgB,GAAG,MAAM,CAE/D;AAED;qFACqF;AACrF,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,UAAQ,GAAG,MAAM,CAM3E"}
@@ -0,0 +1,62 @@
1
+ import type { CaptureErrorOptions, Dsn, SentryEvent, SentryFrame } from './types';
2
+ /** eventId mints a 32-hex-char id (no dashes) — the Sentry event_id shape. Same
3
+ * minter as everything else, just formatted for Sentry's wire. */
4
+ export declare function eventId(): string;
5
+ /**
6
+ * parseDsn parses "https://<version>:<hmac>@<host>/v1/event/<projectId>" into its
7
+ * public key + the derived envelope ingest URL. The key (which itself contains a
8
+ * ':') is taken verbatim as the userinfo — we do NOT split it as user:pass. The
9
+ * key rides ?sentry_key= (not the DSN in the body) because that is the credential
10
+ * channel the server trusts AND the only one sendBeacon can carry on unload.
11
+ * Returns null for anything malformed (fail-safe: the caller then stays inert).
12
+ */
13
+ export declare function parseDsn(dsn: string | undefined | null): Dsn | null;
14
+ /**
15
+ * framesFromStack parses a browser Error.stack into Sentry frames, OLDEST-FIRST
16
+ * (Sentry orders caller->callee; the crash site is last — matching the server's
17
+ * pickCrashFrame). Handles both V8 ("at fn (file:li:co)") and
18
+ * Firefox/Safari ("fn@file:li:co"). Unparseable lines are skipped.
19
+ */
20
+ export declare function framesFromStack(stack: string | undefined): SentryFrame[];
21
+ /** normalizeError coerces an unknown throwable into {name, message, stack}. */
22
+ export declare function normalizeError(err: unknown): {
23
+ name: string;
24
+ message: string;
25
+ stack?: string;
26
+ };
27
+ /** Identity carried onto every error event — the SAME ids analytics uses. */
28
+ export interface ErrorIdentity {
29
+ /** OIDC sub (post-identify) or anon id. NEVER email/PII. */
30
+ userId?: string;
31
+ sessionId?: string;
32
+ product?: string;
33
+ release?: string;
34
+ environment?: string;
35
+ }
36
+ export interface BuildEventInput {
37
+ error: unknown;
38
+ options?: CaptureErrorOptions;
39
+ identity: ErrorIdentity;
40
+ capturePII?: boolean;
41
+ /** Injectable for deterministic tests. */
42
+ now?: number;
43
+ id?: string;
44
+ }
45
+ /**
46
+ * buildSentryEvent turns a throwable + identity into a Sentry `event`. The message
47
+ * (the leak surface) is scrubbed client-side; the user is ONLY the stable subject
48
+ * id — never email/username/ip. Level defaults to error, or fatal for uncaught.
49
+ */
50
+ export declare function buildSentryEvent(input: BuildEventInput): SentryEvent;
51
+ /**
52
+ * buildEnvelope frames a Sentry event into a newline-delimited envelope:
53
+ *
54
+ * {"event_id","dsn","sent_at"}\n
55
+ * {"type":"event","content_type":"application/json","length":N}\n
56
+ * <event json>\n
57
+ *
58
+ * The item is length-delimited (N = UTF-8 byte length) — the framing the server's
59
+ * parseEnvelope reads first (falling back to newline-delimited otherwise).
60
+ */
61
+ export declare function buildEnvelope(event: SentryEvent, dsn: Dsn, sentAt?: string): string;
62
+ //# sourceMappingURL=sentry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../src/sentry.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,mBAAmB,EACnB,GAAG,EACH,WAAW,EACX,WAAW,EAEZ,MAAM,SAAS,CAAA;AAahB;mEACmE;AACnE,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAUD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAqBnE;AAiBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,EAAE,CAkDxE;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB9F;AAsBD,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAiBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,WAAW,CAqDpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAanF"}
@@ -0,0 +1,25 @@
1
+ /** One parsed stack frame. */
2
+ export interface Frame {
3
+ filename?: string;
4
+ function?: string;
5
+ abs_path?: string;
6
+ lineno?: number;
7
+ colno?: number;
8
+ /** The app's own code, as opposed to vendor/runtime — the useful default filter. */
9
+ in_app?: boolean;
10
+ }
11
+ /**
12
+ * framesFromStack parses a browser Error.stack into frames, OLDEST-FIRST (caller
13
+ * → callee, so the crash site is LAST). Handles both V8 ("at fn (file:li:co)")
14
+ * and Firefox/Safari ("fn@file:li:co"). Unparseable lines are skipped.
15
+ */
16
+ export declare function framesFromStack(stack: string | undefined): Frame[];
17
+ /** normalizeError coerces an unknown throwable into {name, message, stack}.
18
+ * TOTAL: it returns a usable record for ANY input, including an object
19
+ * engineered to throw on property access. */
20
+ export declare function normalizeError(err: unknown): {
21
+ name: string;
22
+ message: string;
23
+ stack?: string;
24
+ };
25
+ //# sourceMappingURL=stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAkBA,8BAA8B;AAC9B,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAiBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,EAAE,CAkDlE;AAwBD;;8CAE8C;AAC9C,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAiB9F"}
@@ -0,0 +1,32 @@
1
+ import type { Attribution, Cohort } from './types';
2
+ /**
3
+ * Stable anonymous id, shared by every *.hanzo.ai surface AND by every Hanzo
4
+ * client on the page — the npm client and the hosted tag both run the one
5
+ * chain in ./anon.js, so which snippet a surface loaded no longer decides who the
6
+ * visitor is.
7
+ *
8
+ * It lives in a first-party cookie on the registrable domain because localStorage
9
+ * is ORIGIN-scoped: docs, cloud and console each minted their own id for the same
10
+ * person, so one marketing → docs → signup → checkout journey arrived as several
11
+ * strangers — 463 anonymous identities carried 545 events in a week, about 1.2
12
+ * events each.
13
+ */
14
+ export declare function anonId(): string | undefined;
15
+ /**
16
+ * Current session id, rotated after SESSION_TTL_MS of inactivity.
17
+ *
18
+ * Minted at `now`, so the v7 timestamp the id carries IS the session's start
19
+ * instant — which is what the session rollups partition and order on. Passing the
20
+ * caller's clock rather than reading Date.now() again keeps the id's embedded time
21
+ * and the recorded `last` from disagreeing.
22
+ */
23
+ export declare function sessionId(now?: number): string | undefined;
24
+ /** Read the persisted first-touch attribution. */
25
+ export declare function getFirstTouch(): Attribution | undefined;
26
+ /** Persist first-touch attribution ONCE — never overwrite an existing record. */
27
+ export declare function setFirstTouchOnce(a: Attribution): Attribution;
28
+ /** Read persisted cohort dimensions. */
29
+ export declare function getCohort(): Cohort | undefined;
30
+ /** Merge + persist cohort dimensions (signupWeek set once). */
31
+ export declare function mergeCohort(patch: Cohort): Cohort;
32
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAqBlD;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,IAAI,MAAM,GAAG,SAAS,CAE3C;AAOD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,SAAa,GAAG,MAAM,GAAG,SAAS,CAgB9D;AAED,kDAAkD;AAClD,wBAAgB,aAAa,IAAI,WAAW,GAAG,SAAS,CASvD;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,WAAW,CAM7D;AAED,wCAAwC;AACxC,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAS9C;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUjD"}
@@ -2,12 +2,12 @@
2
2
  * breadcrumb an exception leaves on the event stream (Cloud stamps
3
3
  * event_type='error' for the warehouse, GET /v1/errors). It does NOT reach the
4
4
  * Sentry dashboard — the envelope on the error plane does that. */
5
- type EventKind = 'pageview' | 'event' | 'identify' | 'group' | 'error';
5
+ export type EventKind = 'pageview' | 'event' | 'identify' | 'group' | 'error';
6
6
  /** A captured exception as it rides the EVENT STREAM — Cloud folds it into
7
7
  * properties.$exception for the warehouse. The richer copy (parsed stack frames,
8
8
  * grouping, release) travels on the error plane as a Sentry envelope; see
9
9
  * AnalyticsConfig.dsn. */
10
- interface Exception {
10
+ export interface Exception {
11
11
  /** Constructor/class name, e.g. "TypeError". */
12
12
  type?: string;
13
13
  /** The error message. */
@@ -21,7 +21,7 @@ interface Exception {
21
21
  /** One stack frame as Error Tracking renders it. The key names are the product's
22
22
  * POST-symbolication vocabulary (`mangled_name`/`source`/`line`/`column`), which
23
23
  * is what the issue view reads straight off `$exception_list`. */
24
- interface ExceptionFrame {
24
+ export interface ExceptionFrame {
25
25
  /** "<hash>/<part>" — stable per code location; the frame's identity. */
26
26
  raw_id: string;
27
27
  /** The function name as it appears in the shipped bundle. */
@@ -40,7 +40,7 @@ interface ExceptionFrame {
40
40
  module?: string | null;
41
41
  }
42
42
  /** One exception in `$exception_list`. */
43
- interface ExceptionEntry {
43
+ export interface ExceptionEntry {
44
44
  id: string;
45
45
  type: string;
46
46
  value: string;
@@ -56,7 +56,7 @@ interface ExceptionEntry {
56
56
  };
57
57
  }
58
58
  /** The `$exception_*` property bag Error Tracking reads off a `$exception` event. */
59
- interface ExceptionProperties {
59
+ export interface ExceptionProperties {
60
60
  $exception_list: ExceptionEntry[];
61
61
  /** Issue grouping key. An event without one is dropped by the issue query. */
62
62
  $exception_fingerprint: string;
@@ -74,7 +74,7 @@ interface ExceptionProperties {
74
74
  $exception_functions: string[];
75
75
  }
76
76
  /** First-touch marketing attribution, parsed once and persisted. */
77
- interface Attribution {
77
+ export interface Attribution {
78
78
  utm: {
79
79
  source?: string;
80
80
  medium?: string;
@@ -88,7 +88,7 @@ interface Attribution {
88
88
  channel?: string;
89
89
  }
90
90
  /** Cohort dimensions carried on every event once known (see goals.ts COHORTS). */
91
- interface Cohort {
91
+ export interface Cohort {
92
92
  /** ISO week the person first signed up, e.g. "2026-W28". */
93
93
  signupWeek?: string;
94
94
  channel?: string;
@@ -96,9 +96,9 @@ interface Cohort {
96
96
  }
97
97
  /** One event as sent on the wire — the canonical Hanzo Cloud event. Maps 1:1 to
98
98
  * the cloud `CaptureEvent` (camelCase JSON keys); a batch of these is POSTed to
99
- * the ONE front door `/v1/event` as `{ batch: [WireEvent, …] }`. tenant/org is
99
+ * the ONE entry point `/v1/event` as `{ batch: [WireEvent, …] }`. tenant/org is
100
100
  * NEVER a field here — the server stamps it from the validated session/key. */
101
- interface WireEvent {
101
+ export interface WireEvent {
102
102
  messageId: string;
103
103
  type: EventKind;
104
104
  event?: string;
@@ -132,7 +132,7 @@ interface WireEvent {
132
132
  * (keepalive) and sendBeacon. A bearer JWT or a publishable pk_ key rides
133
133
  * Authorization on fetch; on a headerless beacon a publishable key rides the
134
134
  * ?ingest_key query. */
135
- interface Transport {
135
+ export interface Transport {
136
136
  /** Durable POST usable during page unload (fetch keepalive / sendBeacon).
137
137
  * `contentType` names the FETCH request's Content-Type — it defaults to
138
138
  * application/json, and the error plane sets application/x-sentry-envelope. A
@@ -147,7 +147,7 @@ interface Transport {
147
147
  debug?: boolean;
148
148
  }): void;
149
149
  }
150
- interface AnalyticsConfig {
150
+ export interface AnalyticsConfig {
151
151
  /** Cloud base URL. Defaults to "https://api.hanzo.ai" (the one edge). Set to
152
152
  * same-origin ("") for cookie-auth apps served behind the same edge
153
153
  * (console/admin/chat), so the browser rides the session cookie. */
@@ -158,7 +158,7 @@ interface AnalyticsConfig {
158
158
  * (the client then relies on same-origin credentials). */
159
159
  getToken?: () => string | undefined | null;
160
160
  /** Publishable ingest key (pk-…). When set, the client attributes writes to the
161
- * ONE front door `/v1/event` with this key instead of a bearer/cookie: it rides
161
+ * ONE entry point `/v1/event` with this key instead of a bearer/cookie: it rides
162
162
  * Authorization: Bearer pk-… on fetch and ?ingest_key=pk-… on a headerless
163
163
  * page-unload beacon, so ANONYMOUS traffic is attributed and unload beacons work
164
164
  * without a bearer. The key is write-only — it attributes a write and never mints
@@ -174,7 +174,7 @@ interface AnalyticsConfig {
174
174
  *
175
175
  * A surface with no key at all still reports for whoever is SIGNED IN (the
176
176
  * session credential attributes them), and drops every logged-out visitor: the
177
- * door refuses an unattributable write rather than filing it where its owner
177
+ * edge refuses an unattributable write rather than filing it where its owner
178
178
  * cannot read it. That failure is invisible from the page, which is why the key
179
179
  * belongs in the env next to the DSN and not in a checklist.
180
180
  *
@@ -211,8 +211,8 @@ interface AnalyticsConfig {
211
211
  * client-side before anything leaves the device (the server scrubs again). */
212
212
  capturePII?: boolean;
213
213
  }
214
- type SentryLevel = 'fatal' | 'error' | 'warning' | 'info' | 'debug';
215
- interface SentryFrame {
214
+ export type SentryLevel = 'fatal' | 'error' | 'warning' | 'info' | 'debug';
215
+ export interface SentryFrame {
216
216
  filename?: string;
217
217
  function?: string;
218
218
  module?: string;
@@ -221,7 +221,7 @@ interface SentryFrame {
221
221
  colno?: number;
222
222
  in_app?: boolean;
223
223
  }
224
- interface SentryExceptionValue {
224
+ export interface SentryExceptionValue {
225
225
  type?: string;
226
226
  value?: string;
227
227
  module?: string;
@@ -229,11 +229,11 @@ interface SentryExceptionValue {
229
229
  frames: SentryFrame[];
230
230
  };
231
231
  }
232
- interface SentryUser {
232
+ export interface SentryUser {
233
233
  /** Stable subject id (OIDC sub / anon id). NEVER email/username/ip. */
234
234
  id?: string;
235
235
  }
236
- interface SentryEvent {
236
+ export interface SentryEvent {
237
237
  event_id: string;
238
238
  timestamp: number;
239
239
  platform: 'javascript';
@@ -256,7 +256,7 @@ interface SentryEvent {
256
256
  };
257
257
  }
258
258
  /** Parsed DSN — the public key + the derived ingest URL. */
259
- interface Dsn {
259
+ export interface Dsn {
260
260
  /** "<version>:<hmac>" public key presented via ?sentry_key= (beacon-safe). */
261
261
  publicKey: string;
262
262
  /** Ingest origin, e.g. "https://sentry.hanzo.ai". */
@@ -271,7 +271,7 @@ interface Dsn {
271
271
  ingestUrl: string;
272
272
  }
273
273
  /** Options for Analytics.captureError. */
274
- interface CaptureErrorOptions {
274
+ export interface CaptureErrorOptions {
275
275
  /** false => uncaught (window.onerror / unhandledrejection / render crash). */
276
276
  handled?: boolean;
277
277
  /** Severity + free-form context; merged into the event's tags. */
@@ -279,102 +279,4 @@ interface CaptureErrorOptions {
279
279
  /** Override the event level (default: error, or fatal when handled === false). */
280
280
  level?: SentryLevel;
281
281
  }
282
-
283
- declare class Analytics {
284
- private cfg;
285
- private transport;
286
- private queue;
287
- private timer;
288
- private personId?;
289
- private attribution;
290
- private cohort;
291
- private started;
292
- /** The view pageview() last counted — path + location. */
293
- private counted?;
294
- /** Parsed error-plane DSN, or null when the plane is inert. */
295
- private dsn;
296
- /** Guards against an error thrown *inside* the error path re-entering it. */
297
- private reentrant;
298
- constructor(config: AnalyticsConfig);
299
- /** adopt gives this client a credential it does not have. The key belongs to
300
- * the stream, not to whichever caller happened to ask for the handle first,
301
- * so a later caller carrying one hands it over. The error plane derives from
302
- * the key, so it comes up here too when it was inert for want of one.
303
- * Present fields are never overwritten: the first caller's key stays. */
304
- adopt(config: AnalyticsConfig): void;
305
- /** errorPlaneEnabled reports whether captured exceptions can actually reach the
306
- * error host. False means a DSN was never configured — the documented
307
- * fail-safe. Exposed so an app (or a test) can assert its wiring instead of
308
- * discovering months later that nothing was ever reported. */
309
- get errorPlaneEnabled(): boolean;
310
- /** errorIngestUrl is the fully-derived envelope endpoint, or undefined when the
311
- * plane is inert. Diagnostics only. */
312
- get errorIngestUrl(): string | undefined;
313
- /** init is idempotent and browser-only for its side effects: capture first-touch
314
- * attribution, hydrate cohort, register the unload flush, and (unless opted out)
315
- * auto-capture unhandled errors. Safe to call from a React effect on every
316
- * render. */
317
- init(): void;
318
- /** identify binds the current visitor to a stable person id (post-login). */
319
- identify(personId: string, traits?: Record<string, unknown>): void;
320
- /** group associates the visitor with an org/team (analytics grouping, not the
321
- * server tenant — the server still derives tenant from the session). */
322
- group(groupId: string, traits?: Record<string, unknown>): void;
323
- /** pageview records a $pageview for the current (or given) location, once per
324
- * view. A view is the path plus the full location, so a query or hash change
325
- * is a new one and a repeat call for the same place is not. */
326
- pageview(path?: string, properties?: Record<string, unknown>): void;
327
- /** capture records a named product event with optional properties. Commerce
328
- * fields (productId/quantity/revenue/currency) may be passed for order events. */
329
- capture(event: string, properties?: Record<string, unknown>, commerce?: Pick<WireEvent, 'productId' | 'quantity' | 'revenue' | 'currency'>): void;
330
- /** captureError reports a caught error, an unhandled rejection, a React render
331
- * error, or a manual report to BOTH planes, from one call:
332
- *
333
- * - the ERROR PLANE — a real Sentry envelope to the DSN host. This is the one
334
- * that produces an issue in sentry.hanzo.ai (grouping, stack frames, AST).
335
- * Inert when no DSN is configured.
336
- * - the EVENT STREAM — a `type:'error'` row in the cloud event warehouse, so
337
- * an error stays correlated with the session's pageviews for product
338
- * analysis (readable via GET /v1/errors).
339
- *
340
- * Both carry the SAME session and subject id, so an error and the pageview
341
- * before it join up. Never throws back into the app; errors are higher-signal
342
- * than pageviews, so both planes flush promptly (a crash may unload the page
343
- * moments later). */
344
- captureError(err: unknown, context?: CaptureErrorOptions): void;
345
- /** setCohort persists cohort dimensions (e.g. signupWeek at signup) so they ride
346
- * every subsequent event. */
347
- setCohort(patch: Cohort): void;
348
- /** flush drains the buffer to the server as ONE batch through the ONE ingest
349
- * front door POST /v1/event, body { batch: [Event…] }. beacon=true selects the
350
- * unload-safe transport. Auth is orthogonal to the wire:
351
- *
352
- * • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each
353
- * a CORS-simple request that any origin may send.
354
- * • else a bearer JWT rides Authorization (fetch only — sendBeacon cannot
355
- * carry a header, so token apps fall back to keepalive fetch on unload).
356
- * • else a cookie app rides same-origin credentials (beacon carries the
357
- * cookie fine).
358
- */
359
- flush(beacon?: boolean): void;
360
- /** sendError frames one exception as a Sentry envelope and posts it to the DSN's
361
- * ingest URL. The DSN's own key rides ?sentry_key= (the credential channel the
362
- * server trusts, and the only one a headerless beacon can carry), so NO bearer
363
- * or publishable key is attached here — the two planes authenticate
364
- * independently. Errors are sent one envelope per event, immediately: batching
365
- * a crash report is how you lose it. */
366
- private sendError;
367
- /** errorIdentity is the SAME identity the event stream stamps — the OIDC subject
368
- * once identify() has run, else the anon id. Never email/PII. */
369
- private errorIdentity;
370
- private enqueue;
371
- private build;
372
- private schedule;
373
- private clearTimer;
374
- }
375
- /** createAnalytics returns the client for a stream, building it on first ask.
376
- * This is the ONE way to get a client: `new Analytics` bypasses the registry
377
- * and is for tests and for a deliberately separate instance. */
378
- declare function createAnalytics(config: AnalyticsConfig): Analytics;
379
-
380
- export { type Attribution as A, type Cohort as C, type Dsn as D, type ExceptionEntry as E, type SentryEvent as S, type Transport as T, type WireEvent as W, type CaptureErrorOptions as a, type SentryFrame as b, type SentryLevel as c, type ExceptionProperties as d, Analytics as e, type AnalyticsConfig as f, type EventKind as g, type Exception as h, type ExceptionFrame as i, createAnalytics as j };
282
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/uid.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * uuidv7 mints a time-ordered UUIDv7 for `now` (epoch milliseconds).
3
+ *
4
+ * Two ids minted in the same millisecond sort arbitrarily between themselves; ids
5
+ * from different milliseconds sort by time, lexically and numerically alike.
6
+ */
7
+ export { hzUuidv7 as uuidv7 } from './anon.js';
8
+ /** The millisecond timestamp a v7 id was minted at — the inverse of uuidv7. */
9
+ export declare function uuidv7Time(id: string): number;
10
+ //# sourceMappingURL=uid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uid.d.ts","sourceRoot":"","sources":["../src/uid.ts"],"names":[],"mappings":"AAgCA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,IAAI,MAAM,EAAE,MAAM,WAAW,CAAA;AAE9C,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE7C"}
@@ -0,0 +1,2 @@
1
+ export declare const VERSION = "0.3.37";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,WAAW,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hanzo/event",
3
- "version": "0.3.36",
4
- "description": "Hanzo Event \u2014 the ONE telemetry client. Emits pageview/event/identify/group to the Hanzo Cloud event stream (POST /v1/event), AND reports errors to Sentry as real Sentry envelopes \u2014 the error plane needs a DSN, without one nothing reaches Sentry. First-touch attribution, beacon-on-unload, auto error capture, client-side secret/PII scrubbing, a shared event + goal vocabulary. Subsumes @sentry.",
3
+ "version": "0.3.37",
4
+ "description": "Hanzo Event the ONE telemetry client. Emits pageview/event/identify/group to the Hanzo Cloud event stream (POST /v1/event), AND reports errors to Sentry as real Sentry envelopes the error plane needs a DSN, without one nothing reaches Sentry. First-touch attribution, beacon-on-unload, auto error capture, client-side secret/PII scrubbing, a shared event + goal vocabulary. Subsumes @sentry.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
7
7
  "access": "public",
@@ -36,13 +36,6 @@
36
36
  "funnel",
37
37
  "hanzo"
38
38
  ],
39
- "scripts": {
40
- "build": "tsup",
41
- "dev": "tsup --watch",
42
- "test": "vitest run",
43
- "typecheck": "tsgo --noEmit -p tsconfig.json",
44
- "clean": "rm -rf dist"
45
- },
46
39
  "exports": {
47
40
  ".": {
48
41
  "import": {
@@ -78,10 +71,17 @@
78
71
  "@types/react": "19.2.14",
79
72
  "react": "19.2.4",
80
73
  "tsup": "^8.5.1",
81
- "typescript": "^5.9.3",
74
+ "typescript": "^7.0.2",
82
75
  "vitest": "^4.1.0"
83
76
  },
84
77
  "dependencies": {
85
78
  "@hanzo/events": "^0.2.1"
79
+ },
80
+ "scripts": {
81
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --outDir dist && node ../../scripts/dts.mjs",
82
+ "dev": "tsup --watch",
83
+ "test": "vitest run",
84
+ "typecheck": "tsc --noEmit -p tsconfig.json",
85
+ "clean": "rm -rf dist"
86
86
  }
87
- }
87
+ }
package/src/anon.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Types for anon.js, which is hand-written ES5 rather than TypeScript because
2
- // The door's hosted tag inlines it VERBATIM and has no compiler.
2
+ // The hosted tag inlines it VERBATIM and has no compiler.
3
3
  // The declarations are here so the bundled client still imports it typed.
4
4
 
5
5
  /** Mints a time-ordered UUIDv7 (RFC 9562 §5.7) for `now` in epoch milliseconds. */
package/src/anon.js CHANGED
@@ -7,15 +7,15 @@
7
7
  *
8
8
  * The two call sites:
9
9
  * 1. src/storage.ts — the bundled npm client; IMPORTS this file.
10
- * 2. hanzoai/cloud apps/analytics/tag.js — the tag the door hosts at
10
+ * 2. hanzoai/cloud apps/analytics/tag.js — the tag served at
11
11
  * /v1/event.js; vendors this file and its tag.go serves the marked region
12
- * with the tag as one asset, so the door holds no second copy either.
12
+ * with the tag as one asset, so cloud holds no second copy either.
13
13
  *
14
14
  * (2) has no bundler and cannot import anything, which is why the chain lives in a
15
15
  * file that is plain ES5 rather than in a .ts: the region between the BEGIN and END
16
16
  * markers is COPIED VERBATIM. Keep it ES5, dependency-free, `hz`-prefixed (it is
17
17
  * spliced into other people's scopes) and unformatted — a reformat here is a diff
18
- * against the vendored copy. After editing, resync the door:
18
+ * against the vendored copy. After editing, resync that copy:
19
19
  *
20
20
  * curl -fsSL https://unpkg.com/@hanzo/event/src/anon.js -o apps/analytics/anon.js
21
21
  */
package/src/anon.test.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  //
8
8
  // The behavioural contract is proven where the chain runs: storage.test.ts. This
9
9
  // file proves the region is SPLICEABLE and that it ships, which is what lets the
10
- // door vendor it instead of writing a second one.
10
+ // cloud vendor it instead of writing a second one.
11
11
 
12
12
  import { describe, expect, it } from 'vitest'
13
13
  import { readFileSync } from 'node:fs'
@@ -33,7 +33,7 @@ const region = (src: string, what: string): string => src.slice(...span(src, wha
33
33
  const ANON = read('./anon.js')
34
34
 
35
35
  describe('the shared anon chain', () => {
36
- it('is marked, so the door can splice exactly it', () => {
36
+ it('is marked, so cloud can splice exactly it', () => {
37
37
  // cloud's tag.go slices between these markers and PANICS if it cannot find
38
38
  // them, so losing a marker ships a tag whose every event carries an undefined
39
39
  // identity. To change the chain, edit this file; never patch a copy.
@@ -50,7 +50,7 @@ describe('the shared anon chain', () => {
50
50
  }
51
51
  })
52
52
 
53
- it('ships in the published package, for the door to vendor', () => {
53
+ it('ships in the published package, for cloud to vendor', () => {
54
54
  // hanzoai/cloud vendors this file whole and its tag.go serves the region with
55
55
  // the tag as one asset, so the second call site holds no copy of its own.
56
56
  const pkg = JSON.parse(read('../package.json')) as { files: string[] }
package/src/core.test.ts CHANGED
@@ -68,7 +68,7 @@ class FakeTransport implements Transport {
68
68
  }
69
69
 
70
70
  let tx: FakeTransport
71
- // Default to same-origin (host:'') so path assertions read the bare /v1/event door;
71
+ // Default to same-origin (host:'') so path assertions read the bare /v1/event path;
72
72
  // tests that care about the edge host pass it explicitly.
73
73
  // `test-app` is deliberately NOT a product in the DSN registry (src/dsn.ts), so
74
74
  // the default client here has no error plane and these tests exercise the client
@@ -91,7 +91,7 @@ describe('Analytics capture', () => {
91
91
  a.flush()
92
92
  expect(tx.sent).toHaveLength(1)
93
93
  expect(tx.sent[0].url).toBe('/v1/event')
94
- // The body is the canonical { batch: [...] } envelope, exactly one door.
94
+ // The body is the canonical { batch: [...] } envelope, exactly one entry point.
95
95
  const parsed = JSON.parse(tx.sent[0].raw)
96
96
  expect(Array.isArray(parsed.batch)).toBe(true)
97
97
  const e = tx.all[0]
@@ -110,7 +110,7 @@ describe('Analytics capture', () => {
110
110
  a.identify('user-9')
111
111
  a.capture(EVENTS.ORDER_COMPLETED, { kind: 'plan' }, { productId: 'plan_pro', revenue: 49, quantity: 1, currency: 'usd' })
112
112
  a.flush()
113
- // ONE POST, ONE door, ONE batched envelope.
113
+ // ONE POST, ONE endpoint, ONE batched envelope.
114
114
  expect(tx.sent).toHaveLength(1)
115
115
  expect(tx.sent[0].url).toBe('https://api.hanzo.ai/v1/event')
116
116
  const parsed = JSON.parse(tx.sent[0].raw) as { batch: WireEvent[] }
@@ -358,7 +358,7 @@ describe('Analytics capture', () => {
358
358
 
359
359
  it('reads the ingest key from the build env when config omits it', () => {
360
360
  // The failure this closes is silent: a surface with no key attributes nothing
361
- // for a logged-out visitor, the door refuses the write, and the page shows no
361
+ // for a logged-out visitor, the edge refuses the write, and the page shows no
362
362
  // sign of it. The key must resolve from the env exactly as the DSN does.
363
363
  process.env.NEXT_PUBLIC_PUBLISHABLE_KEY = 'pk-live-from-env'
364
364
  try {
@@ -515,10 +515,10 @@ describe('Event error capture', () => {
515
515
  expect(p.$exception_fingerprint).not.toBe('forged')
516
516
  })
517
517
 
518
- it('an error is still an event on the ONE stream — same /v1/event door + product', () => {
518
+ it('an error is still an event on the ONE stream — same /v1/event endpoint + product', () => {
519
519
  const a = mk({ host: 'https://api.hanzo.ai' })
520
520
  a.captureError(new Error('x'))
521
- // Same batched door as any other event — one pipe, not a second SDK.
521
+ // Same batched endpoint as any other event — one pipe, not a second SDK.
522
522
  expect(tx.sent[0].url).toBe('https://api.hanzo.ai/v1/event')
523
523
  expect(tx.sent[0].raw.startsWith('{"batch":')).toBe(true)
524
524
  expect(tx.all[0].product).toBe('test-app')