@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.
- package/LICENSE.md +21 -0
- package/README.md +8 -8
- package/TAXONOMY.md +1 -1
- package/dist/attribution.d.ts +14 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/core.d.ts +102 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/dsn.d.ts +43 -0
- package/dist/dsn.d.ts.map +1 -0
- package/dist/events.d.ts +21 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/exception.d.ts +50 -0
- package/dist/exception.d.ts.map +1 -0
- package/dist/funnels.d.ts +14 -0
- package/dist/funnels.d.ts.map +1 -0
- package/dist/goals.d.ts +24 -0
- package/dist/goals.d.ts.map +1 -0
- package/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -292
- package/dist/index.d.ts +22 -292
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +7 -7
- package/dist/index.mjs.map +1 -1
- package/dist/org.d.ts +74 -0
- package/dist/org.d.ts.map +1 -0
- package/dist/react.cjs +7 -7
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +13 -14
- package/dist/react.d.ts +13 -14
- package/dist/react.d.ts.map +1 -0
- package/dist/react.mjs +7 -7
- package/dist/react.mjs.map +1 -1
- package/dist/scrub.d.ts +20 -0
- package/dist/scrub.d.ts.map +1 -0
- package/dist/sentry.d.ts +62 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/stack.d.ts +25 -0
- package/dist/stack.d.ts.map +1 -0
- package/dist/storage.d.ts +32 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/{core-CIhI2R7S.d.cts → types.d.ts} +21 -119
- package/dist/types.d.ts.map +1 -0
- package/dist/uid.d.ts +10 -0
- package/dist/uid.d.ts.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +11 -11
- package/src/anon.d.ts +1 -1
- package/src/anon.js +3 -3
- package/src/anon.test.ts +3 -3
- package/src/core.test.ts +6 -6
- package/src/core.ts +11 -11
- package/src/events.ts +1 -1
- package/src/exception.ts +1 -1
- package/src/stack.ts +1 -1
- package/src/types.ts +3 -3
- package/src/uid.ts +2 -2
- package/src/version.ts +1 -1
- package/dist/core-CIhI2R7S.d.ts +0 -380
package/dist/scrub.d.ts
ADDED
|
@@ -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"}
|
package/dist/sentry.d.ts
ADDED
|
@@ -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"}
|
package/dist/stack.d.ts
ADDED
|
@@ -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
|
|
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
|
|
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
|
-
*
|
|
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 @@
|
|
|
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.
|
|
4
|
-
"description": "Hanzo Event
|
|
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": "^
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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')
|