@hanzo/event 0.3.44 → 0.3.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -50
- package/dist/core.d.ts +6 -36
- package/dist/core.d.ts.map +1 -1
- package/dist/index.cjs +242 -414
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +241 -411
- package/dist/index.mjs.map +1 -1
- package/dist/items.d.ts +22 -0
- package/dist/items.d.ts.map +1 -0
- package/dist/org.d.ts.map +1 -1
- package/dist/react.cjs +181 -415
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -0
- package/dist/react.d.ts +8 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.mjs +183 -418
- package/dist/react.mjs.map +1 -1
- package/dist/tags.d.ts +14 -1
- package/dist/tags.d.ts.map +1 -1
- package/dist/throwable.d.ts +15 -0
- package/dist/throwable.d.ts.map +1 -0
- package/dist/types.d.ts +19 -74
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core.test.ts +28 -300
- package/src/core.ts +36 -185
- package/src/exception.ts +1 -1
- package/src/index.ts +4 -6
- package/src/items.test.ts +22 -0
- package/src/items.ts +44 -0
- package/src/org.ts +4 -0
- package/src/react.tsx +22 -0
- package/src/stream.test.ts +0 -7
- package/src/tags.test.ts +18 -0
- package/src/tags.ts +38 -5
- package/src/throwable.test.ts +72 -0
- package/src/throwable.ts +126 -0
- package/src/types.ts +15 -75
- package/src/uid.test.ts +1 -1
- package/dist/dsn.d.ts +0 -43
- package/dist/dsn.d.ts.map +0 -1
- package/dist/sentry.d.ts +0 -62
- package/dist/sentry.d.ts.map +0 -1
- package/src/dsn.test.ts +0 -75
- package/src/dsn.ts +0 -56
- package/src/sentry.test.ts +0 -312
- package/src/sentry.ts +0 -311
package/dist/react.cjs
CHANGED
|
@@ -22,20 +22,20 @@ var SOCIAL_HOSTS = [
|
|
|
22
22
|
var SEARCH_HOSTS = ["google.", "bing.", "duckduckgo.", "yahoo.", "baidu.", "ecosia."];
|
|
23
23
|
function parseAttribution(search, referrer) {
|
|
24
24
|
const q = new URLSearchParams(search || "");
|
|
25
|
-
const
|
|
25
|
+
const get2 = (k) => {
|
|
26
26
|
const v = q.get(k);
|
|
27
27
|
return v ? v.trim() : void 0;
|
|
28
28
|
};
|
|
29
29
|
const a = {
|
|
30
30
|
utm: {
|
|
31
|
-
source:
|
|
32
|
-
medium:
|
|
33
|
-
campaign:
|
|
34
|
-
term:
|
|
35
|
-
content:
|
|
31
|
+
source: get2("utm_source"),
|
|
32
|
+
medium: get2("utm_medium"),
|
|
33
|
+
campaign: get2("utm_campaign"),
|
|
34
|
+
term: get2("utm_term"),
|
|
35
|
+
content: get2("utm_content")
|
|
36
36
|
},
|
|
37
37
|
referrer: referrer ? referrer.trim() : void 0,
|
|
38
|
-
refCode:
|
|
38
|
+
refCode: get2("ref") || get2("refCode") || get2("ref_code") || void 0
|
|
39
39
|
};
|
|
40
40
|
a.channel = deriveChannel(a);
|
|
41
41
|
return a;
|
|
@@ -70,22 +70,6 @@ function hasAttribution(a) {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// src/dsn.ts
|
|
74
|
-
var PRODUCT_PROJECT = Object.freeze({
|
|
75
|
-
console: "019fa40b-94ae-7f1d-8f7b-e92f123fad42",
|
|
76
|
-
// console.hanzo.ai
|
|
77
|
-
app: "019f9b1e-57eb-7171-9d92-72c0b85e4b4b",
|
|
78
|
-
// hanzo.app
|
|
79
|
-
site: "019f9b1e-5785-7359-ad0b-f75db8e58c99"
|
|
80
|
-
// hanzo.ai (marketing; product `site`)
|
|
81
|
-
});
|
|
82
|
-
function dsnForProduct(product, key) {
|
|
83
|
-
if (!product || !key) return void 0;
|
|
84
|
-
const projectId = PRODUCT_PROJECT[product];
|
|
85
|
-
if (!projectId) return void 0;
|
|
86
|
-
return `https://${key}@api.hanzo.ai/v1/event/${projectId}`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
73
|
// src/org.ts
|
|
90
74
|
var ORG_DOMAIN = Object.freeze({
|
|
91
75
|
// A brand's domains are not a pattern — `hanzoskills.com` is Hanzo's and does
|
|
@@ -94,11 +78,15 @@ var ORG_DOMAIN = Object.freeze({
|
|
|
94
78
|
// separate fact, which is the whole reason this is a table.
|
|
95
79
|
hanzo: Object.freeze([
|
|
96
80
|
"hanzo.ai",
|
|
81
|
+
"hanzo.agency",
|
|
97
82
|
"hanzo.app",
|
|
98
83
|
"hanzo.bot",
|
|
99
84
|
"hanzo.chat",
|
|
100
85
|
"hanzo.codes",
|
|
101
86
|
"hanzo.id",
|
|
87
|
+
"hanzo.industries",
|
|
88
|
+
"hanzo.market",
|
|
89
|
+
"hanzo.network",
|
|
102
90
|
"hanzo.sh",
|
|
103
91
|
"hanzo.team",
|
|
104
92
|
"hanzo.ventures",
|
|
@@ -278,6 +266,93 @@ function scrubText(s, capturePII = false) {
|
|
|
278
266
|
return s;
|
|
279
267
|
}
|
|
280
268
|
|
|
269
|
+
// src/throwable.ts
|
|
270
|
+
var MAX_FRAMES = 50;
|
|
271
|
+
var MAX_LINES = 500;
|
|
272
|
+
var MAX_LINE_LEN = 2048;
|
|
273
|
+
var V8_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+)|([^)]+))\)?\s*$/;
|
|
274
|
+
var MOZ_FRAME = /^\s*(?:(.*?)@)?(.+?):(\d+):(\d+)\s*$/;
|
|
275
|
+
function inApp(file) {
|
|
276
|
+
if (!file) return false;
|
|
277
|
+
return !(file.includes("node_modules") || file.startsWith("webpack-internal") || file.startsWith("webpack://") || file.startsWith("chrome-extension://") || file.startsWith("moz-extension://"));
|
|
278
|
+
}
|
|
279
|
+
function framesFromStack(stack) {
|
|
280
|
+
if (!stack) return [];
|
|
281
|
+
const lines = stack.split("\n", MAX_LINES);
|
|
282
|
+
const frames = [];
|
|
283
|
+
for (const raw of lines) {
|
|
284
|
+
if (raw.length > MAX_LINE_LEN) continue;
|
|
285
|
+
const line = raw.trimEnd();
|
|
286
|
+
if (!line) continue;
|
|
287
|
+
let fn;
|
|
288
|
+
let file = "";
|
|
289
|
+
let lineno = 0;
|
|
290
|
+
let colno = 0;
|
|
291
|
+
const v = V8_FRAME.exec(line);
|
|
292
|
+
if (v) {
|
|
293
|
+
fn = v[1];
|
|
294
|
+
if (v[2]) {
|
|
295
|
+
file = v[2];
|
|
296
|
+
lineno = Number(v[3]) || 0;
|
|
297
|
+
colno = Number(v[4]) || 0;
|
|
298
|
+
} else {
|
|
299
|
+
file = (v[5] || "").trim();
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
const f = MOZ_FRAME.exec(line);
|
|
303
|
+
if (!f) continue;
|
|
304
|
+
fn = f[1];
|
|
305
|
+
file = f[2];
|
|
306
|
+
lineno = Number(f[3]) || 0;
|
|
307
|
+
colno = Number(f[4]) || 0;
|
|
308
|
+
}
|
|
309
|
+
if (!file && !fn) continue;
|
|
310
|
+
frames.push({
|
|
311
|
+
function: fn || "<anonymous>",
|
|
312
|
+
filename: file,
|
|
313
|
+
abs_path: file,
|
|
314
|
+
lineno,
|
|
315
|
+
colno,
|
|
316
|
+
in_app: inApp(file)
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
frames.reverse();
|
|
320
|
+
if (frames.length > MAX_FRAMES) return frames.slice(frames.length - MAX_FRAMES);
|
|
321
|
+
return frames;
|
|
322
|
+
}
|
|
323
|
+
function normalizeError(err) {
|
|
324
|
+
if (err instanceof Error) {
|
|
325
|
+
const name = read(err, "name");
|
|
326
|
+
const message = read(err, "message");
|
|
327
|
+
const stack = read(err, "stack");
|
|
328
|
+
return {
|
|
329
|
+
name: typeof name === "string" && name ? name : "Error",
|
|
330
|
+
message: typeof message === "string" && message ? message : str(err),
|
|
331
|
+
stack: typeof stack === "string" ? stack : void 0
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (typeof err === "string") return { name: "Error", message: err };
|
|
335
|
+
try {
|
|
336
|
+
return { name: "Error", message: JSON.stringify(err) ?? str(err) };
|
|
337
|
+
} catch {
|
|
338
|
+
return { name: "Error", message: str(err) };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function read(o, k) {
|
|
342
|
+
try {
|
|
343
|
+
return o[k];
|
|
344
|
+
} catch {
|
|
345
|
+
return void 0;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function str(v) {
|
|
349
|
+
try {
|
|
350
|
+
return String(v);
|
|
351
|
+
} catch {
|
|
352
|
+
return "[unstringifiable]";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
281
356
|
// src/anon.js
|
|
282
357
|
var HZ_ANON_KEY = "iam-anon-id";
|
|
283
358
|
var HZ_ANON_ADOPT = ["hz_anon_id", "hz_id"];
|
|
@@ -396,292 +471,6 @@ function hzAnonAdopt(id) {
|
|
|
396
471
|
}
|
|
397
472
|
}
|
|
398
473
|
|
|
399
|
-
// package.json
|
|
400
|
-
var version = "0.3.44";
|
|
401
|
-
|
|
402
|
-
// src/version.ts
|
|
403
|
-
var VERSION = version;
|
|
404
|
-
|
|
405
|
-
// src/sentry.ts
|
|
406
|
-
var MAX_FRAMES = 50;
|
|
407
|
-
var MAX_LINES = 500;
|
|
408
|
-
var MAX_LINE_LEN = 2048;
|
|
409
|
-
var MAX_TAG_LEN = 1024;
|
|
410
|
-
var MAX_TAGS = 50;
|
|
411
|
-
function eventId() {
|
|
412
|
-
return hzUuidv7().replace(/-/g, "");
|
|
413
|
-
}
|
|
414
|
-
function byteLen(s) {
|
|
415
|
-
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(s).length;
|
|
416
|
-
if (typeof Buffer !== "undefined") return Buffer.byteLength(s, "utf8");
|
|
417
|
-
return s.length;
|
|
418
|
-
}
|
|
419
|
-
function parseDsn(dsn) {
|
|
420
|
-
if (!dsn) return null;
|
|
421
|
-
const m = /^(https?):\/\/([^@]+)@([^/]+)(\/.*)?$/.exec(dsn.trim());
|
|
422
|
-
if (!m) return null;
|
|
423
|
-
const scheme = m[1];
|
|
424
|
-
const publicKey = m[2];
|
|
425
|
-
const host = m[3];
|
|
426
|
-
const path = m[4] ?? "";
|
|
427
|
-
if (!publicKey || !host) return null;
|
|
428
|
-
const segs = path.split("/").filter(Boolean);
|
|
429
|
-
const projectId = segs.length > 0 ? segs[segs.length - 1] : "";
|
|
430
|
-
if (!projectId) return null;
|
|
431
|
-
const origin = `${scheme}://${host}`;
|
|
432
|
-
const base = `${origin}/${segs.map(encodeURIComponent).join("/")}`;
|
|
433
|
-
const ingestUrl = `${base}/envelope/?sentry_key=${encodeURIComponent(publicKey)}`;
|
|
434
|
-
return { publicKey, origin, projectId, base, ingestUrl };
|
|
435
|
-
}
|
|
436
|
-
var V8_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+)|([^)]+))\)?\s*$/;
|
|
437
|
-
var MOZ_FRAME = /^\s*(?:(.*?)@)?(.+?):(\d+):(\d+)\s*$/;
|
|
438
|
-
function inApp(file) {
|
|
439
|
-
if (!file) return false;
|
|
440
|
-
return !(file.includes("node_modules") || file.startsWith("webpack-internal") || file.startsWith("webpack://") || file.startsWith("chrome-extension://") || file.startsWith("moz-extension://"));
|
|
441
|
-
}
|
|
442
|
-
function framesFromStack(stack) {
|
|
443
|
-
if (!stack) return [];
|
|
444
|
-
const lines = stack.split("\n", MAX_LINES);
|
|
445
|
-
const frames = [];
|
|
446
|
-
for (const raw of lines) {
|
|
447
|
-
if (raw.length > MAX_LINE_LEN) continue;
|
|
448
|
-
const line = raw.trimEnd();
|
|
449
|
-
if (!line) continue;
|
|
450
|
-
let fn;
|
|
451
|
-
let file = "";
|
|
452
|
-
let lineno = 0;
|
|
453
|
-
let colno = 0;
|
|
454
|
-
const v = V8_FRAME.exec(line);
|
|
455
|
-
if (v) {
|
|
456
|
-
fn = v[1];
|
|
457
|
-
if (v[2]) {
|
|
458
|
-
file = v[2];
|
|
459
|
-
lineno = Number(v[3]) || 0;
|
|
460
|
-
colno = Number(v[4]) || 0;
|
|
461
|
-
} else {
|
|
462
|
-
file = (v[5] || "").trim();
|
|
463
|
-
}
|
|
464
|
-
} else {
|
|
465
|
-
const f = MOZ_FRAME.exec(line);
|
|
466
|
-
if (!f) continue;
|
|
467
|
-
fn = f[1];
|
|
468
|
-
file = f[2];
|
|
469
|
-
lineno = Number(f[3]) || 0;
|
|
470
|
-
colno = Number(f[4]) || 0;
|
|
471
|
-
}
|
|
472
|
-
if (!file && !fn) continue;
|
|
473
|
-
frames.push({
|
|
474
|
-
function: fn || "<anonymous>",
|
|
475
|
-
filename: file,
|
|
476
|
-
abs_path: file,
|
|
477
|
-
lineno,
|
|
478
|
-
colno,
|
|
479
|
-
in_app: inApp(file)
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
frames.reverse();
|
|
483
|
-
if (frames.length > MAX_FRAMES) return frames.slice(frames.length - MAX_FRAMES);
|
|
484
|
-
return frames;
|
|
485
|
-
}
|
|
486
|
-
function normalizeError(err) {
|
|
487
|
-
if (err instanceof Error) {
|
|
488
|
-
const name = read(err, "name");
|
|
489
|
-
const message = read(err, "message");
|
|
490
|
-
const stack = read(err, "stack");
|
|
491
|
-
return {
|
|
492
|
-
name: typeof name === "string" && name ? name : "Error",
|
|
493
|
-
message: typeof message === "string" && message ? message : str(err),
|
|
494
|
-
stack: typeof stack === "string" ? stack : void 0
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (typeof err === "string") return { name: "Error", message: err };
|
|
498
|
-
try {
|
|
499
|
-
return { name: "Error", message: JSON.stringify(err) ?? str(err) };
|
|
500
|
-
} catch {
|
|
501
|
-
return { name: "Error", message: str(err) };
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
function read(o, k) {
|
|
505
|
-
try {
|
|
506
|
-
return o[k];
|
|
507
|
-
} catch {
|
|
508
|
-
return void 0;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
function str(v) {
|
|
512
|
-
try {
|
|
513
|
-
return String(v);
|
|
514
|
-
} catch {
|
|
515
|
-
return "[unstringifiable]";
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
function coerceTag(v) {
|
|
519
|
-
const s = typeof v === "string" ? v : (() => {
|
|
520
|
-
try {
|
|
521
|
-
return JSON.stringify(v) ?? String(v);
|
|
522
|
-
} catch {
|
|
523
|
-
try {
|
|
524
|
-
return String(v);
|
|
525
|
-
} catch {
|
|
526
|
-
return "[unstringifiable]";
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
})();
|
|
530
|
-
return s.length > MAX_TAG_LEN ? s.slice(0, MAX_TAG_LEN) : s;
|
|
531
|
-
}
|
|
532
|
-
function buildSentryEvent(input) {
|
|
533
|
-
const { error, options = {}, identity, capturePII = false } = input;
|
|
534
|
-
const norm = normalizeError(error);
|
|
535
|
-
const handled = options.handled !== false;
|
|
536
|
-
const level = options.level ?? (handled ? "error" : "fatal");
|
|
537
|
-
const tags = { handled: String(handled) };
|
|
538
|
-
if (identity.product) tags.product = identity.product;
|
|
539
|
-
if (identity.sessionId) tags.session = identity.sessionId;
|
|
540
|
-
try {
|
|
541
|
-
const props = options.properties ?? {};
|
|
542
|
-
let n = 0;
|
|
543
|
-
for (const k of Object.keys(props)) {
|
|
544
|
-
if (n >= MAX_TAGS) break;
|
|
545
|
-
try {
|
|
546
|
-
const val = props[k];
|
|
547
|
-
if (val === void 0 || val === null) continue;
|
|
548
|
-
tags[k] = scrubText(coerceTag(val), capturePII);
|
|
549
|
-
n++;
|
|
550
|
-
} catch {
|
|
551
|
-
continue;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
} catch {
|
|
555
|
-
}
|
|
556
|
-
const event = {
|
|
557
|
-
event_id: input.id ?? eventId(),
|
|
558
|
-
timestamp: (input.now ?? Date.now()) / 1e3,
|
|
559
|
-
platform: "javascript",
|
|
560
|
-
level,
|
|
561
|
-
logger: identity.product,
|
|
562
|
-
environment: identity.environment,
|
|
563
|
-
release: identity.release,
|
|
564
|
-
exception: {
|
|
565
|
-
values: [
|
|
566
|
-
{
|
|
567
|
-
type: norm.name,
|
|
568
|
-
value: scrubText(norm.message, capturePII),
|
|
569
|
-
stacktrace: { frames: framesFromStack(norm.stack) }
|
|
570
|
-
}
|
|
571
|
-
]
|
|
572
|
-
},
|
|
573
|
-
tags,
|
|
574
|
-
sdk: { name: "@hanzo/event", version: VERSION }
|
|
575
|
-
};
|
|
576
|
-
if (identity.userId) event.user = { id: identity.userId };
|
|
577
|
-
return event;
|
|
578
|
-
}
|
|
579
|
-
function buildEnvelope(event, dsn, sentAt) {
|
|
580
|
-
const payload = JSON.stringify(event);
|
|
581
|
-
const header = JSON.stringify({
|
|
582
|
-
event_id: event.event_id,
|
|
583
|
-
dsn: dsn.base,
|
|
584
|
-
sent_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
585
|
-
});
|
|
586
|
-
const itemHeader = JSON.stringify({
|
|
587
|
-
type: "event",
|
|
588
|
-
content_type: "application/json",
|
|
589
|
-
length: byteLen(payload)
|
|
590
|
-
});
|
|
591
|
-
return `${header}
|
|
592
|
-
${itemHeader}
|
|
593
|
-
${payload}
|
|
594
|
-
`;
|
|
595
|
-
}
|
|
596
|
-
var MAX_VALUE = 4096;
|
|
597
|
-
function digest(s) {
|
|
598
|
-
let out = "";
|
|
599
|
-
for (const seed of [2166136261, 16777619, 2654435769, 2246822507]) {
|
|
600
|
-
let h = seed >>> 0;
|
|
601
|
-
for (let i = 0; i < s.length; i++) {
|
|
602
|
-
h ^= s.charCodeAt(i);
|
|
603
|
-
h = h + (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24) >>> 0;
|
|
604
|
-
}
|
|
605
|
-
out += h.toString(16).padStart(8, "0");
|
|
606
|
-
}
|
|
607
|
-
return out;
|
|
608
|
-
}
|
|
609
|
-
function frameOf(f) {
|
|
610
|
-
const source = f.filename ?? "";
|
|
611
|
-
const name = f.function ?? "<anonymous>";
|
|
612
|
-
const line = f.lineno ?? 0;
|
|
613
|
-
const column = f.colno ?? 0;
|
|
614
|
-
return {
|
|
615
|
-
// Stable per code location, so the same frame groups and re-renders under one
|
|
616
|
-
// key. The '/part' suffix is the product's format for one raw frame expanding
|
|
617
|
-
// into several resolved ones; a browser frame is always part 0.
|
|
618
|
-
raw_id: `${digest(`${source}|${name}|${line}|${column}`)}/0`,
|
|
619
|
-
mangled_name: name,
|
|
620
|
-
source,
|
|
621
|
-
line,
|
|
622
|
-
column,
|
|
623
|
-
in_app: f.in_app ?? false,
|
|
624
|
-
lang: "javascript",
|
|
625
|
-
resolved: false,
|
|
626
|
-
resolve_failure: "no symbol set uploaded for this release",
|
|
627
|
-
resolved_name: null,
|
|
628
|
-
module: null
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
function truncate2(s, max) {
|
|
632
|
-
if (s.length <= max) return s;
|
|
633
|
-
let end = max;
|
|
634
|
-
const c = s.charCodeAt(end - 1);
|
|
635
|
-
if (c >= 55296 && c <= 56319) end--;
|
|
636
|
-
return s.slice(0, end);
|
|
637
|
-
}
|
|
638
|
-
function exceptionEntry(err, opts) {
|
|
639
|
-
const n = normalizeError(err);
|
|
640
|
-
const frames = framesFromStack(n.stack).slice(-50);
|
|
641
|
-
const entry = {
|
|
642
|
-
id: opts.id,
|
|
643
|
-
type: n.name,
|
|
644
|
-
value: truncate2(n.message, MAX_VALUE),
|
|
645
|
-
mechanism: {
|
|
646
|
-
type: "generic",
|
|
647
|
-
handled: opts.handled,
|
|
648
|
-
// An exception the app reported by hand was constructed, not thrown by the
|
|
649
|
-
// runtime at a real call site.
|
|
650
|
-
synthetic: !(err instanceof Error)
|
|
651
|
-
}
|
|
652
|
-
};
|
|
653
|
-
if (frames.length > 0) {
|
|
654
|
-
entry.stacktrace = { type: "resolved", frames: frames.map(frameOf) };
|
|
655
|
-
}
|
|
656
|
-
return entry;
|
|
657
|
-
}
|
|
658
|
-
function fingerprint(entry) {
|
|
659
|
-
const frames = entry.stacktrace?.frames ?? [];
|
|
660
|
-
const pieces = frames.filter((f) => f.in_app).map((f) => `${f.mangled_name}@${f.source}`);
|
|
661
|
-
return digest([entry.type, ...pieces].join("\n"));
|
|
662
|
-
}
|
|
663
|
-
function exceptionProperties(err, opts) {
|
|
664
|
-
const entry = exceptionEntry(err, opts);
|
|
665
|
-
const frames = entry.stacktrace?.frames ?? [];
|
|
666
|
-
const fp = fingerprint(entry);
|
|
667
|
-
return {
|
|
668
|
-
$exception_list: [entry],
|
|
669
|
-
$exception_fingerprint: fp,
|
|
670
|
-
// Records WHY this fingerprint holds. 'manual' is the honest value: the client
|
|
671
|
-
// supplied the key rather than a server grouper deriving one.
|
|
672
|
-
$exception_fingerprint_record: [{ type: "manual" }],
|
|
673
|
-
$exception_type: entry.type,
|
|
674
|
-
$exception_message: entry.value,
|
|
675
|
-
$exception_level: opts.level ?? "error",
|
|
676
|
-
$exception_handled: opts.handled,
|
|
677
|
-
$exception_synthetic: entry.mechanism?.synthetic ?? false,
|
|
678
|
-
$exception_types: [entry.type],
|
|
679
|
-
$exception_values: [entry.value],
|
|
680
|
-
$exception_sources: frames.map((f) => f.source).filter((s) => !!s),
|
|
681
|
-
$exception_functions: frames.map((f) => f.resolved_name ?? f.mangled_name).filter((s) => !!s)
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
|
|
685
474
|
// src/storage.ts
|
|
686
475
|
var KEY = {
|
|
687
476
|
session: "hz_session",
|
|
@@ -805,10 +594,10 @@ function linkUrl(url, state, fromHost, base) {
|
|
|
805
594
|
}
|
|
806
595
|
if (u.protocol !== "https:" && u.protocol !== "http:") return url;
|
|
807
596
|
if (!orgOf(u.hostname) || u.hostname === fromHost) return url;
|
|
808
|
-
const
|
|
809
|
-
if (isId(state.anonId))
|
|
810
|
-
if (isId(state.sessionId))
|
|
811
|
-
if (state.firstTouch)
|
|
597
|
+
const set2 = (k, v) => v && u.searchParams.set(k, v);
|
|
598
|
+
if (isId(state.anonId)) set2(LINK_PARAMS.anon, state.anonId);
|
|
599
|
+
if (isId(state.sessionId)) set2(LINK_PARAMS.session, state.sessionId);
|
|
600
|
+
if (state.firstTouch) set2(LINK_PARAMS.firstTouch, encodeFirstTouch(state.firstTouch));
|
|
812
601
|
return u.toString();
|
|
813
602
|
}
|
|
814
603
|
function readLink(search) {
|
|
@@ -833,20 +622,16 @@ function stripLink(href) {
|
|
|
833
622
|
return hit ? u.pathname + u.search + u.hash : void 0;
|
|
834
623
|
}
|
|
835
624
|
|
|
625
|
+
// package.json
|
|
626
|
+
var version = "0.3.49";
|
|
627
|
+
|
|
628
|
+
// src/version.ts
|
|
629
|
+
var VERSION = version;
|
|
630
|
+
|
|
836
631
|
// src/core.ts
|
|
837
632
|
var EVENT_PATH = "/v1/event";
|
|
838
633
|
var DEFAULT_HOST = "https://api.hanzo.ai";
|
|
839
|
-
var ENVELOPE_CONTENT_TYPE = "application/x-sentry-envelope";
|
|
840
634
|
var BEACON_CONTENT_TYPE = "text/plain";
|
|
841
|
-
function readEnvDsn() {
|
|
842
|
-
try {
|
|
843
|
-
if (typeof process !== "undefined" && process.env) {
|
|
844
|
-
return process.env.NEXT_PUBLIC_HANZO_EVENT_DSN || process.env.HANZO_EVENT_DSN || void 0;
|
|
845
|
-
}
|
|
846
|
-
} catch {
|
|
847
|
-
}
|
|
848
|
-
return void 0;
|
|
849
|
-
}
|
|
850
635
|
function readEnv(name) {
|
|
851
636
|
try {
|
|
852
637
|
if (typeof process !== "undefined" && process.env) return process.env[name] || void 0;
|
|
@@ -859,7 +644,8 @@ function appendQuery(url, key, value) {
|
|
|
859
644
|
}
|
|
860
645
|
function normalizeError2(err) {
|
|
861
646
|
const n = normalizeError(err);
|
|
862
|
-
|
|
647
|
+
const frames = framesFromStack(n.stack).slice(-50).map((f) => ({ function: f.function, file: f.filename, line: f.lineno, column: f.colno }));
|
|
648
|
+
return { type: n.name, message: n.message, stack: n.stack, frames };
|
|
863
649
|
}
|
|
864
650
|
var isBrowser = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
865
651
|
function serializeBatch(batch) {
|
|
@@ -955,35 +741,13 @@ var Analytics = class {
|
|
|
955
741
|
ingestKey: config.ingestKey ?? readEnv("NEXT_PUBLIC_PUBLISHABLE_KEY") ?? keyForPage()
|
|
956
742
|
};
|
|
957
743
|
this.transport = config.transport ?? new DefaultTransport();
|
|
958
|
-
this.dsn = parseDsn(
|
|
959
|
-
config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product, this.cfg.ingestKey)
|
|
960
|
-
);
|
|
961
744
|
}
|
|
962
745
|
/** adopt gives this client a credential it does not have. The key belongs to
|
|
963
746
|
* the stream, not to whichever caller happened to ask for the handle first,
|
|
964
|
-
* so a later caller carrying one hands it over.
|
|
965
|
-
* the key, so it comes up here too when it was inert for want of one.
|
|
966
|
-
* Present fields are never overwritten: the first caller's key stays. */
|
|
747
|
+
* so a later caller carrying one hands it over. Present fields are never overwritten: the first caller's key stays. */
|
|
967
748
|
adopt(config) {
|
|
968
749
|
if (config.ingestKey && !this.cfg.ingestKey) this.cfg.ingestKey = config.ingestKey;
|
|
969
750
|
if (config.getToken && !this.cfg.getToken) this.cfg.getToken = config.getToken;
|
|
970
|
-
if (!this.dsn) {
|
|
971
|
-
this.dsn = parseDsn(
|
|
972
|
-
config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product, this.cfg.ingestKey)
|
|
973
|
-
);
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
/** errorPlaneEnabled reports whether captured exceptions can actually reach the
|
|
977
|
-
* error host. False means a DSN was never configured — the documented
|
|
978
|
-
* fail-safe. Exposed so an app (or a test) can assert its wiring instead of
|
|
979
|
-
* discovering months later that nothing was ever reported. */
|
|
980
|
-
get errorPlaneEnabled() {
|
|
981
|
-
return this.dsn !== null;
|
|
982
|
-
}
|
|
983
|
-
/** errorIngestUrl is the fully-derived envelope endpoint, or undefined when the
|
|
984
|
-
* plane is inert. Diagnostics only. */
|
|
985
|
-
get errorIngestUrl() {
|
|
986
|
-
return this.dsn?.ingestUrl;
|
|
987
751
|
}
|
|
988
752
|
/** init is idempotent and browser-only for its side effects: capture first-touch
|
|
989
753
|
* attribution, hydrate cohort, register the unload flush, and (unless opted out)
|
|
@@ -1071,45 +835,28 @@ var Analytics = class {
|
|
|
1071
835
|
this.enqueue("event", event, { properties, ...commerce });
|
|
1072
836
|
}
|
|
1073
837
|
/** captureError reports a caught error, an unhandled rejection, a React render
|
|
1074
|
-
* error, or a manual report
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
1079
|
-
* - the EVENT STREAM — a `type:'error'` row in the cloud event warehouse, so
|
|
1080
|
-
* an error stays correlated with the session's pageviews for product
|
|
1081
|
-
* analysis (readable via GET /v1/errors).
|
|
1082
|
-
*
|
|
1083
|
-
* Both carry the SAME session and subject id, so an error and the pageview
|
|
1084
|
-
* before it join up. Never throws back into the app; errors are higher-signal
|
|
1085
|
-
* than pageviews, so both planes flush promptly (a crash may unload the page
|
|
1086
|
-
* moments later). */
|
|
838
|
+
* error, or a manual report as ONE event of type 'error' on the one stream.
|
|
839
|
+
* It carries the exception (type, message, frames, stack, handled) and the
|
|
840
|
+
* release, environment, site, product and level that group and scope it, and
|
|
841
|
+
* flushes promptly (a crash may unload the page moments later). Never throws
|
|
842
|
+
* back into the app. */
|
|
1087
843
|
captureError(err, context) {
|
|
1088
844
|
if (this.reentrant) return;
|
|
1089
845
|
this.reentrant = true;
|
|
1090
846
|
try {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
handled,
|
|
1105
|
-
id: hzUuidv7(),
|
|
1106
|
-
level: context?.level
|
|
1107
|
-
})
|
|
1108
|
-
}
|
|
1109
|
-
});
|
|
1110
|
-
this.flush();
|
|
1111
|
-
} catch {
|
|
1112
|
-
}
|
|
847
|
+
const handled = context?.handled ?? true;
|
|
848
|
+
const ex = normalizeError2(err);
|
|
849
|
+
ex.handled = handled;
|
|
850
|
+
this.enqueue("error", void 0, {
|
|
851
|
+
error: ex,
|
|
852
|
+
level: context?.level ?? (handled ? "error" : "fatal"),
|
|
853
|
+
release: this.cfg.release ?? readEnv("NEXT_PUBLIC_HANZO_RELEASE"),
|
|
854
|
+
environment: this.cfg.environment ?? readEnv("NODE_ENV"),
|
|
855
|
+
site: isBrowser() ? window.location.hostname : void 0,
|
|
856
|
+
properties: context?.properties
|
|
857
|
+
});
|
|
858
|
+
this.flush();
|
|
859
|
+
} catch {
|
|
1113
860
|
} finally {
|
|
1114
861
|
this.reentrant = false;
|
|
1115
862
|
}
|
|
@@ -1152,39 +899,6 @@ var Analytics = class {
|
|
|
1152
899
|
});
|
|
1153
900
|
}
|
|
1154
901
|
// ── internals ────────────────────────────────────────────────────────────
|
|
1155
|
-
/** sendError frames one exception as a Sentry envelope and posts it to the DSN's
|
|
1156
|
-
* ingest URL. The DSN's own key rides ?sentry_key= (the credential channel the
|
|
1157
|
-
* server trusts, and the only one a headerless beacon can carry), so NO bearer
|
|
1158
|
-
* or publishable key is attached here — the two planes authenticate
|
|
1159
|
-
* independently. Errors are sent one envelope per event, immediately: batching
|
|
1160
|
-
* a crash report is how you lose it. */
|
|
1161
|
-
sendError(err, options) {
|
|
1162
|
-
if (!this.cfg.enabled || !this.dsn) return;
|
|
1163
|
-
const event = buildSentryEvent({
|
|
1164
|
-
error: err,
|
|
1165
|
-
options,
|
|
1166
|
-
identity: this.errorIdentity(),
|
|
1167
|
-
capturePII: this.cfg.capturePII ?? false
|
|
1168
|
-
});
|
|
1169
|
-
const body = buildEnvelope(event, this.dsn);
|
|
1170
|
-
if (this.cfg.debug) console.debug("[event] error \u2192", this.dsn.ingestUrl, event.event_id);
|
|
1171
|
-
this.transport.send(this.dsn.ingestUrl, body, {
|
|
1172
|
-
beacon: false,
|
|
1173
|
-
contentType: ENVELOPE_CONTENT_TYPE,
|
|
1174
|
-
debug: this.cfg.debug
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
/** errorIdentity is the SAME identity the event stream stamps — the OIDC subject
|
|
1178
|
-
* once identify() has run, else the anon id. Never email/PII. */
|
|
1179
|
-
errorIdentity() {
|
|
1180
|
-
return {
|
|
1181
|
-
userId: this.personId ?? anonId(),
|
|
1182
|
-
sessionId: sessionId(),
|
|
1183
|
-
product: this.cfg.product,
|
|
1184
|
-
release: this.cfg.release ?? readEnv("NEXT_PUBLIC_HANZO_RELEASE"),
|
|
1185
|
-
environment: this.cfg.environment ?? readEnv("NODE_ENV")
|
|
1186
|
-
};
|
|
1187
|
-
}
|
|
1188
902
|
enqueue(kind, event, extra) {
|
|
1189
903
|
if (!this.cfg.enabled) return;
|
|
1190
904
|
if (!this.started) this.init();
|
|
@@ -1270,6 +984,45 @@ function createAnalytics(config) {
|
|
|
1270
984
|
return fresh;
|
|
1271
985
|
}
|
|
1272
986
|
|
|
987
|
+
// src/cookie.ts
|
|
988
|
+
function get(name) {
|
|
989
|
+
if (typeof document === "undefined") return void 0;
|
|
990
|
+
const hit = document.cookie.split("; ").find((c) => c.startsWith(`${name}=`));
|
|
991
|
+
return hit ? decodeURIComponent(hit.slice(name.length + 1)) : void 0;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/consent.ts
|
|
995
|
+
var CONSENT_COOKIE = "hz_consent";
|
|
996
|
+
var CONSENT_EVENT = "hzconsent";
|
|
997
|
+
var US = /^(America\/(New_York|Chicago|Denver|Los_Angeles|Phoenix|Anchorage|Adak|Boise|Detroit|Juneau|Sitka|Nome|Yakutat|Menominee|Metlakatla|Indiana\/.*|Kentucky\/.*|North_Dakota\/.*)|Pacific\/Honolulu|US\/.*)$/;
|
|
998
|
+
function gpc() {
|
|
999
|
+
if (typeof navigator === "undefined") return false;
|
|
1000
|
+
return navigator.globalPrivacyControl === true;
|
|
1001
|
+
}
|
|
1002
|
+
function refused() {
|
|
1003
|
+
if (typeof window === "undefined") return false;
|
|
1004
|
+
const n = navigator;
|
|
1005
|
+
const dnt = n.doNotTrack ?? window.doNotTrack ?? n.msDoNotTrack;
|
|
1006
|
+
return gpc() || dnt === "1" || dnt === "yes";
|
|
1007
|
+
}
|
|
1008
|
+
function region() {
|
|
1009
|
+
try {
|
|
1010
|
+
const zone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "";
|
|
1011
|
+
return US.test(zone) ? "opt-out" : "opt-in";
|
|
1012
|
+
} catch {
|
|
1013
|
+
return "opt-in";
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
function read2() {
|
|
1017
|
+
const kept = get(CONSENT_COOKIE);
|
|
1018
|
+
const on = kept !== void 0 ? kept.split(",") : region() === "opt-in" || refused() ? [] : ["analytics", "marketing", "ads"];
|
|
1019
|
+
const has = (k) => on.includes(k);
|
|
1020
|
+
return { analytics: has("analytics"), marketing: has("marketing") && !gpc(), ads: has("ads") && !gpc() };
|
|
1021
|
+
}
|
|
1022
|
+
function render(c) {
|
|
1023
|
+
return ["analytics", "marketing", "ads"].filter((k) => c[k]).join(",");
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1273
1026
|
// src/react.tsx
|
|
1274
1027
|
var Ctx = react.createContext(null);
|
|
1275
1028
|
function AnalyticsProvider(props) {
|
|
@@ -1324,6 +1077,18 @@ var ErrorBoundary = class extends react.Component {
|
|
|
1324
1077
|
}
|
|
1325
1078
|
};
|
|
1326
1079
|
ErrorBoundary.contextType = Ctx;
|
|
1080
|
+
var listen = (on) => {
|
|
1081
|
+
window.addEventListener(CONSENT_EVENT, on);
|
|
1082
|
+
return () => window.removeEventListener(CONSENT_EVENT, on);
|
|
1083
|
+
};
|
|
1084
|
+
function useConsent() {
|
|
1085
|
+
const kept = react.useSyncExternalStore(listen, () => render(read2()), () => "");
|
|
1086
|
+
return {
|
|
1087
|
+
analytics: kept.includes("analytics"),
|
|
1088
|
+
marketing: kept.includes("marketing"),
|
|
1089
|
+
ads: kept.includes("ads")
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1327
1092
|
/*! anon.js — THE anonymous-identity chain. ONE implementation, two distributions.
|
|
1328
1093
|
*
|
|
1329
1094
|
* One browser is ONE person on every Hanzo surface, whichever client a page
|
|
@@ -1349,6 +1114,7 @@ ErrorBoundary.contextType = Ctx;
|
|
|
1349
1114
|
exports.AnalyticsProvider = AnalyticsProvider;
|
|
1350
1115
|
exports.ErrorBoundary = ErrorBoundary;
|
|
1351
1116
|
exports.useAnalytics = useAnalytics;
|
|
1117
|
+
exports.useConsent = useConsent;
|
|
1352
1118
|
exports.usePageview = usePageview;
|
|
1353
1119
|
//# sourceMappingURL=react.cjs.map
|
|
1354
1120
|
//# sourceMappingURL=react.cjs.map
|