@hanzo/event 0.3.18 → 0.3.20
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/dist/{core-D4PaPlOB.d.cts → core-CHLpJy51.d.cts} +56 -1
- package/dist/{core-D4PaPlOB.d.ts → core-CHLpJy51.d.ts} +56 -1
- package/dist/index.cjs +119 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -10
- package/dist/index.d.ts +70 -10
- package/dist/index.mjs +114 -12
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +113 -11
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.mjs +113 -11
- package/dist/react.mjs.map +1 -1
- package/hz.js +1 -1
- package/package.json +10 -10
- package/src/core.test.ts +45 -12
- package/src/core.ts +30 -3
- package/src/dsn.test.ts +14 -11
- package/src/dsn.ts +19 -14
- package/src/events.ts +5 -0
- package/src/exception.test.ts +188 -0
- package/src/exception.ts +200 -0
- package/src/index.ts +6 -2
- package/src/types.ts +53 -0
- package/src/version.ts +1 -1
- package/LICENSE.md +0 -21
|
@@ -18,6 +18,61 @@ interface Exception {
|
|
|
18
18
|
* true = a caught error the app chose to report. Defaults true. */
|
|
19
19
|
handled?: boolean;
|
|
20
20
|
}
|
|
21
|
+
/** One stack frame as Error Tracking renders it. The key names are the product's
|
|
22
|
+
* POST-symbolication vocabulary (`mangled_name`/`source`/`line`/`column`), which
|
|
23
|
+
* is what the issue view reads straight off `$exception_list`. */
|
|
24
|
+
interface ExceptionFrame {
|
|
25
|
+
/** "<hash>/<part>" — stable per code location; the frame's identity. */
|
|
26
|
+
raw_id: string;
|
|
27
|
+
/** The function name as it appears in the shipped bundle. */
|
|
28
|
+
mangled_name: string;
|
|
29
|
+
/** File/URL the frame is in. */
|
|
30
|
+
source: string;
|
|
31
|
+
line: number;
|
|
32
|
+
column: number;
|
|
33
|
+
/** First-party code. Frames without this are hidden by default in the product. */
|
|
34
|
+
in_app: boolean;
|
|
35
|
+
lang: string;
|
|
36
|
+
/** Whether a symbol set mapped this frame back to original source. */
|
|
37
|
+
resolved: boolean;
|
|
38
|
+
resolve_failure?: string;
|
|
39
|
+
resolved_name?: string | null;
|
|
40
|
+
module?: string | null;
|
|
41
|
+
}
|
|
42
|
+
/** One exception in `$exception_list`. */
|
|
43
|
+
interface ExceptionEntry {
|
|
44
|
+
id: string;
|
|
45
|
+
type: string;
|
|
46
|
+
value: string;
|
|
47
|
+
mechanism?: {
|
|
48
|
+
type: 'generic';
|
|
49
|
+
handled: boolean;
|
|
50
|
+
synthetic?: boolean;
|
|
51
|
+
};
|
|
52
|
+
/** `type` MUST be 'resolved' — the renderer draws frames on no other value. */
|
|
53
|
+
stacktrace?: {
|
|
54
|
+
type: 'resolved';
|
|
55
|
+
frames: ExceptionFrame[];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The `$exception_*` property bag Error Tracking reads off a `$exception` event. */
|
|
59
|
+
interface ExceptionProperties {
|
|
60
|
+
$exception_list: ExceptionEntry[];
|
|
61
|
+
/** Issue grouping key. An event without one is dropped by the issue query. */
|
|
62
|
+
$exception_fingerprint: string;
|
|
63
|
+
$exception_fingerprint_record: {
|
|
64
|
+
type: 'manual';
|
|
65
|
+
}[];
|
|
66
|
+
$exception_type: string;
|
|
67
|
+
$exception_message: string;
|
|
68
|
+
$exception_level: SentryLevel;
|
|
69
|
+
$exception_handled: boolean;
|
|
70
|
+
$exception_synthetic: boolean;
|
|
71
|
+
$exception_types: string[];
|
|
72
|
+
$exception_values: string[];
|
|
73
|
+
$exception_sources: string[];
|
|
74
|
+
$exception_functions: string[];
|
|
75
|
+
}
|
|
21
76
|
/** First-touch marketing attribution, parsed once and persisted. */
|
|
22
77
|
interface Attribution {
|
|
23
78
|
utm: {
|
|
@@ -308,4 +363,4 @@ declare class Analytics {
|
|
|
308
363
|
/** createAnalytics builds a client instance. Most apps use one shared instance. */
|
|
309
364
|
declare function createAnalytics(config: AnalyticsConfig): Analytics;
|
|
310
365
|
|
|
311
|
-
export { type Attribution as A, type Cohort as C, type Dsn as D, type
|
|
366
|
+
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 };
|
|
@@ -18,6 +18,61 @@ interface Exception {
|
|
|
18
18
|
* true = a caught error the app chose to report. Defaults true. */
|
|
19
19
|
handled?: boolean;
|
|
20
20
|
}
|
|
21
|
+
/** One stack frame as Error Tracking renders it. The key names are the product's
|
|
22
|
+
* POST-symbolication vocabulary (`mangled_name`/`source`/`line`/`column`), which
|
|
23
|
+
* is what the issue view reads straight off `$exception_list`. */
|
|
24
|
+
interface ExceptionFrame {
|
|
25
|
+
/** "<hash>/<part>" — stable per code location; the frame's identity. */
|
|
26
|
+
raw_id: string;
|
|
27
|
+
/** The function name as it appears in the shipped bundle. */
|
|
28
|
+
mangled_name: string;
|
|
29
|
+
/** File/URL the frame is in. */
|
|
30
|
+
source: string;
|
|
31
|
+
line: number;
|
|
32
|
+
column: number;
|
|
33
|
+
/** First-party code. Frames without this are hidden by default in the product. */
|
|
34
|
+
in_app: boolean;
|
|
35
|
+
lang: string;
|
|
36
|
+
/** Whether a symbol set mapped this frame back to original source. */
|
|
37
|
+
resolved: boolean;
|
|
38
|
+
resolve_failure?: string;
|
|
39
|
+
resolved_name?: string | null;
|
|
40
|
+
module?: string | null;
|
|
41
|
+
}
|
|
42
|
+
/** One exception in `$exception_list`. */
|
|
43
|
+
interface ExceptionEntry {
|
|
44
|
+
id: string;
|
|
45
|
+
type: string;
|
|
46
|
+
value: string;
|
|
47
|
+
mechanism?: {
|
|
48
|
+
type: 'generic';
|
|
49
|
+
handled: boolean;
|
|
50
|
+
synthetic?: boolean;
|
|
51
|
+
};
|
|
52
|
+
/** `type` MUST be 'resolved' — the renderer draws frames on no other value. */
|
|
53
|
+
stacktrace?: {
|
|
54
|
+
type: 'resolved';
|
|
55
|
+
frames: ExceptionFrame[];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The `$exception_*` property bag Error Tracking reads off a `$exception` event. */
|
|
59
|
+
interface ExceptionProperties {
|
|
60
|
+
$exception_list: ExceptionEntry[];
|
|
61
|
+
/** Issue grouping key. An event without one is dropped by the issue query. */
|
|
62
|
+
$exception_fingerprint: string;
|
|
63
|
+
$exception_fingerprint_record: {
|
|
64
|
+
type: 'manual';
|
|
65
|
+
}[];
|
|
66
|
+
$exception_type: string;
|
|
67
|
+
$exception_message: string;
|
|
68
|
+
$exception_level: SentryLevel;
|
|
69
|
+
$exception_handled: boolean;
|
|
70
|
+
$exception_synthetic: boolean;
|
|
71
|
+
$exception_types: string[];
|
|
72
|
+
$exception_values: string[];
|
|
73
|
+
$exception_sources: string[];
|
|
74
|
+
$exception_functions: string[];
|
|
75
|
+
}
|
|
21
76
|
/** First-touch marketing attribution, parsed once and persisted. */
|
|
22
77
|
interface Attribution {
|
|
23
78
|
utm: {
|
|
@@ -308,4 +363,4 @@ declare class Analytics {
|
|
|
308
363
|
/** createAnalytics builds a client instance. Most apps use one shared instance. */
|
|
309
364
|
declare function createAnalytics(config: AnalyticsConfig): Analytics;
|
|
310
365
|
|
|
311
|
-
export { type Attribution as A, type Cohort as C, type Dsn as D, type
|
|
366
|
+
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 };
|
package/dist/index.cjs
CHANGED
|
@@ -74,17 +74,19 @@ function isoWeek(d) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// src/dsn.ts
|
|
77
|
-
var
|
|
78
|
-
|
|
79
|
-
console
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
var PRODUCT_PROJECT = Object.freeze({
|
|
78
|
+
console: "019fa40b-94ae-7f1d-8f7b-e92f123fad42",
|
|
79
|
+
// console.hanzo.ai
|
|
80
|
+
app: "019f9b1e-57eb-7171-9d92-72c0b85e4b4b",
|
|
81
|
+
// hanzo.app
|
|
82
|
+
site: "019f9b1e-5785-7359-ad0b-f75db8e58c99"
|
|
83
|
+
// hanzo.ai (marketing; product `site`)
|
|
84
84
|
});
|
|
85
85
|
function dsnForProduct(product) {
|
|
86
86
|
if (!product) return void 0;
|
|
87
|
-
|
|
87
|
+
const projectId = PRODUCT_PROJECT[product];
|
|
88
|
+
if (!projectId) return void 0;
|
|
89
|
+
return `https://${HANZO_PUBLISHABLE_KEY}@api.hanzo.ai/v1/sentry/${projectId}`;
|
|
88
90
|
}
|
|
89
91
|
var HANZO_PUBLISHABLE_KEY = "pk-live-c88649f1085fb6ad441d8a0072933a9b";
|
|
90
92
|
function defaultPublishableKey(host) {
|
|
@@ -142,6 +144,7 @@ var EVENTS = {
|
|
|
142
144
|
DEPLOY_FAILED: "deploy_failed"
|
|
143
145
|
};
|
|
144
146
|
var PAGEVIEW = "$pageview";
|
|
147
|
+
var EXCEPTION = "$exception";
|
|
145
148
|
|
|
146
149
|
// src/scrub.ts
|
|
147
150
|
var REDACTED = "[redacted]";
|
|
@@ -371,7 +374,7 @@ function uuidv7Time(id) {
|
|
|
371
374
|
}
|
|
372
375
|
|
|
373
376
|
// src/version.ts
|
|
374
|
-
var VERSION = "0.3.
|
|
377
|
+
var VERSION = "0.3.20";
|
|
375
378
|
|
|
376
379
|
// src/sentry.ts
|
|
377
380
|
var MAX_FRAMES = 50;
|
|
@@ -563,6 +566,94 @@ ${itemHeader}
|
|
|
563
566
|
${payload}
|
|
564
567
|
`;
|
|
565
568
|
}
|
|
569
|
+
var MAX_VALUE = 4096;
|
|
570
|
+
function digest(s2) {
|
|
571
|
+
let out = "";
|
|
572
|
+
for (const seed of [2166136261, 16777619, 2654435769, 2246822507]) {
|
|
573
|
+
let h = seed >>> 0;
|
|
574
|
+
for (let i = 0; i < s2.length; i++) {
|
|
575
|
+
h ^= s2.charCodeAt(i);
|
|
576
|
+
h = h + (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24) >>> 0;
|
|
577
|
+
}
|
|
578
|
+
out += h.toString(16).padStart(8, "0");
|
|
579
|
+
}
|
|
580
|
+
return out;
|
|
581
|
+
}
|
|
582
|
+
function frameOf(f) {
|
|
583
|
+
const source = f.filename ?? "";
|
|
584
|
+
const name = f.function ?? "<anonymous>";
|
|
585
|
+
const line = f.lineno ?? 0;
|
|
586
|
+
const column = f.colno ?? 0;
|
|
587
|
+
return {
|
|
588
|
+
// Stable per code location, so the same frame groups and re-renders under one
|
|
589
|
+
// key. The '/part' suffix is the product's format for one raw frame expanding
|
|
590
|
+
// into several resolved ones; a browser frame is always part 0.
|
|
591
|
+
raw_id: `${digest(`${source}|${name}|${line}|${column}`)}/0`,
|
|
592
|
+
mangled_name: name,
|
|
593
|
+
source,
|
|
594
|
+
line,
|
|
595
|
+
column,
|
|
596
|
+
in_app: f.in_app ?? false,
|
|
597
|
+
lang: "javascript",
|
|
598
|
+
resolved: false,
|
|
599
|
+
resolve_failure: "no symbol set uploaded for this release",
|
|
600
|
+
resolved_name: null,
|
|
601
|
+
module: null
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
function truncate2(s2, max) {
|
|
605
|
+
if (s2.length <= max) return s2;
|
|
606
|
+
let end = max;
|
|
607
|
+
const c = s2.charCodeAt(end - 1);
|
|
608
|
+
if (c >= 55296 && c <= 56319) end--;
|
|
609
|
+
return s2.slice(0, end);
|
|
610
|
+
}
|
|
611
|
+
function exceptionEntry(err, opts) {
|
|
612
|
+
const n = normalizeError(err);
|
|
613
|
+
const frames = framesFromStack(n.stack).slice(-50);
|
|
614
|
+
const entry = {
|
|
615
|
+
id: opts.id,
|
|
616
|
+
type: n.name,
|
|
617
|
+
value: truncate2(n.message, MAX_VALUE),
|
|
618
|
+
mechanism: {
|
|
619
|
+
type: "generic",
|
|
620
|
+
handled: opts.handled,
|
|
621
|
+
// An exception the app reported by hand was constructed, not thrown by the
|
|
622
|
+
// runtime at a real call site.
|
|
623
|
+
synthetic: !(err instanceof Error)
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
if (frames.length > 0) {
|
|
627
|
+
entry.stacktrace = { type: "resolved", frames: frames.map(frameOf) };
|
|
628
|
+
}
|
|
629
|
+
return entry;
|
|
630
|
+
}
|
|
631
|
+
function fingerprint(entry) {
|
|
632
|
+
const frames = entry.stacktrace?.frames ?? [];
|
|
633
|
+
const pieces = frames.filter((f) => f.in_app).map((f) => `${f.mangled_name}@${f.source}`);
|
|
634
|
+
return digest([entry.type, ...pieces].join("\n"));
|
|
635
|
+
}
|
|
636
|
+
function exceptionProperties(err, opts) {
|
|
637
|
+
const entry = exceptionEntry(err, opts);
|
|
638
|
+
const frames = entry.stacktrace?.frames ?? [];
|
|
639
|
+
const fp = fingerprint(entry);
|
|
640
|
+
return {
|
|
641
|
+
$exception_list: [entry],
|
|
642
|
+
$exception_fingerprint: fp,
|
|
643
|
+
// Records WHY this fingerprint holds. 'manual' is the honest value: the client
|
|
644
|
+
// supplied the key rather than a server grouper deriving one.
|
|
645
|
+
$exception_fingerprint_record: [{ type: "manual" }],
|
|
646
|
+
$exception_type: entry.type,
|
|
647
|
+
$exception_message: entry.value,
|
|
648
|
+
$exception_level: opts.level ?? "error",
|
|
649
|
+
$exception_handled: opts.handled,
|
|
650
|
+
$exception_synthetic: entry.mechanism?.synthetic ?? false,
|
|
651
|
+
$exception_types: [entry.type],
|
|
652
|
+
$exception_values: [entry.value],
|
|
653
|
+
$exception_sources: frames.map((f) => f.source).filter((s2) => !!s2),
|
|
654
|
+
$exception_functions: frames.map((f) => f.resolved_name ?? f.mangled_name).filter((s2) => !!s2)
|
|
655
|
+
};
|
|
656
|
+
}
|
|
566
657
|
|
|
567
658
|
// src/storage.ts
|
|
568
659
|
var KEY = {
|
|
@@ -839,9 +930,20 @@ var Analytics = class {
|
|
|
839
930
|
} catch {
|
|
840
931
|
}
|
|
841
932
|
try {
|
|
933
|
+
const handled = context?.handled ?? true;
|
|
842
934
|
const ex = normalizeError2(err);
|
|
843
|
-
ex.handled =
|
|
844
|
-
this.enqueue("
|
|
935
|
+
ex.handled = handled;
|
|
936
|
+
this.enqueue("event", EXCEPTION, {
|
|
937
|
+
error: ex,
|
|
938
|
+
properties: {
|
|
939
|
+
...context?.properties,
|
|
940
|
+
...exceptionProperties(err, {
|
|
941
|
+
handled,
|
|
942
|
+
id: hzUuidv7(),
|
|
943
|
+
level: context?.level
|
|
944
|
+
})
|
|
945
|
+
}
|
|
946
|
+
});
|
|
845
947
|
this.flush();
|
|
846
948
|
} catch {
|
|
847
949
|
}
|
|
@@ -1151,18 +1253,23 @@ var COHORTS = {
|
|
|
1151
1253
|
exports.Analytics = Analytics;
|
|
1152
1254
|
exports.COHORTS = COHORTS;
|
|
1153
1255
|
exports.EVENTS = EVENTS;
|
|
1256
|
+
exports.EXCEPTION = EXCEPTION;
|
|
1154
1257
|
exports.FUNNELS = FUNNELS;
|
|
1155
1258
|
exports.GOALS = GOALS;
|
|
1156
1259
|
exports.PAGEVIEW = PAGEVIEW;
|
|
1157
1260
|
exports.PRODUCTS = PRODUCTS;
|
|
1158
|
-
exports.
|
|
1261
|
+
exports.PRODUCT_PROJECT = PRODUCT_PROJECT;
|
|
1159
1262
|
exports.VERSION = VERSION;
|
|
1160
1263
|
exports.buildEnvelope = buildEnvelope;
|
|
1161
1264
|
exports.buildSentryEvent = buildSentryEvent;
|
|
1162
1265
|
exports.createAnalytics = createAnalytics;
|
|
1163
1266
|
exports.deriveChannel = deriveChannel;
|
|
1267
|
+
exports.digest = digest;
|
|
1164
1268
|
exports.dsnForProduct = dsnForProduct;
|
|
1165
1269
|
exports.eventsOf = eventsOf;
|
|
1270
|
+
exports.exceptionEntry = exceptionEntry;
|
|
1271
|
+
exports.exceptionProperties = exceptionProperties;
|
|
1272
|
+
exports.fingerprint = fingerprint;
|
|
1166
1273
|
exports.framesFromStack = framesFromStack;
|
|
1167
1274
|
exports.getCohort = getCohort;
|
|
1168
1275
|
exports.getFirstTouch = getFirstTouch;
|