@botim/mp-debug-sdk 0.4.1 → 0.5.2
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/index.cjs +69 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +69 -7
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +12 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -270,7 +270,7 @@ function resolveAgainstEndpoint(url, base) {
|
|
|
270
270
|
function detectDeviceInfo(app, override) {
|
|
271
271
|
const ua = typeof navigator !== "undefined" ? navigator.userAgent : void 0;
|
|
272
272
|
return {
|
|
273
|
-
deviceId: override?.deviceId ??
|
|
273
|
+
deviceId: override?.deviceId ?? loadOrCreateDeviceId(),
|
|
274
274
|
platform: override?.platform ?? detectPlatform(ua),
|
|
275
275
|
osVersion: override?.osVersion,
|
|
276
276
|
appName: override?.appName ?? app.name,
|
|
@@ -286,6 +286,21 @@ function detectPlatform(ua) {
|
|
|
286
286
|
if (/Mozilla|Chrome|Safari|Firefox/i.test(ua)) return "web";
|
|
287
287
|
return "unknown";
|
|
288
288
|
}
|
|
289
|
+
var DEVICE_ID_STORAGE_KEY = "botim-debug-sdk:device-id";
|
|
290
|
+
function loadOrCreateDeviceId() {
|
|
291
|
+
try {
|
|
292
|
+
const ls = typeof localStorage !== "undefined" ? localStorage : null;
|
|
293
|
+
if (ls) {
|
|
294
|
+
const stored = ls.getItem(DEVICE_ID_STORAGE_KEY);
|
|
295
|
+
if (stored && stored.length > 0) return stored;
|
|
296
|
+
const fresh = generateDeviceId();
|
|
297
|
+
ls.setItem(DEVICE_ID_STORAGE_KEY, fresh);
|
|
298
|
+
return fresh;
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
302
|
+
return generateDeviceId();
|
|
303
|
+
}
|
|
289
304
|
function generateDeviceId() {
|
|
290
305
|
const c = typeof crypto !== "undefined" ? crypto : void 0;
|
|
291
306
|
if (c?.randomUUID) return c.randomUUID();
|
|
@@ -560,6 +575,11 @@ function wrapFetch(opts) {
|
|
|
560
575
|
method,
|
|
561
576
|
url,
|
|
562
577
|
status: res.status,
|
|
578
|
+
// statusText carries the human label ("OK", "Not Found"). Pre-HTTP/2
|
|
579
|
+
// responses always have it; HTTP/2+ defines it as empty by spec but
|
|
580
|
+
// most browsers synthesize one from the code, so this is reliable
|
|
581
|
+
// enough to display alongside the status code.
|
|
582
|
+
statusText: res.statusText || void 0,
|
|
563
583
|
durationMs: Date.now() - start,
|
|
564
584
|
resHeaders: headersFromResponse(res),
|
|
565
585
|
resBody
|
|
@@ -573,7 +593,14 @@ function wrapFetch(opts) {
|
|
|
573
593
|
url,
|
|
574
594
|
durationMs: Date.now() - start,
|
|
575
595
|
errorMessage: err instanceof Error ? err.message : String(err),
|
|
576
|
-
errorName: err instanceof Error ? err.name : void 0
|
|
596
|
+
errorName: err instanceof Error ? err.name : void 0,
|
|
597
|
+
// Stack from the rejected promise — points into fetch internals
|
|
598
|
+
// and (when present) the call site that issued the request.
|
|
599
|
+
errorStack: err instanceof Error ? err.stack : void 0,
|
|
600
|
+
// undici frequently wraps the real reason in `cause` (e.g.
|
|
601
|
+
// `TypeError: fetch failed` outside, `Error: ECONNREFUSED` inside).
|
|
602
|
+
// Flatten the chain so operators don't have to dig.
|
|
603
|
+
errorCause: collectCauseChain(err)
|
|
577
604
|
});
|
|
578
605
|
throw err;
|
|
579
606
|
}
|
|
@@ -582,6 +609,24 @@ function wrapFetch(opts) {
|
|
|
582
609
|
target.fetch = original;
|
|
583
610
|
};
|
|
584
611
|
}
|
|
612
|
+
function collectCauseChain(err) {
|
|
613
|
+
if (!err || typeof err !== "object") return void 0;
|
|
614
|
+
const lines = [];
|
|
615
|
+
let cur = err.cause;
|
|
616
|
+
const seen = /* @__PURE__ */ new Set();
|
|
617
|
+
while (cur && lines.length < 5) {
|
|
618
|
+
if (seen.has(cur)) break;
|
|
619
|
+
seen.add(cur);
|
|
620
|
+
if (cur instanceof Error) {
|
|
621
|
+
lines.push(`${cur.name}: ${cur.message}`);
|
|
622
|
+
cur = cur.cause;
|
|
623
|
+
} else {
|
|
624
|
+
lines.push(String(cur));
|
|
625
|
+
cur = cur?.cause;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return lines.length ? lines.join("\n") : void 0;
|
|
629
|
+
}
|
|
585
630
|
function wrapXHR(opts) {
|
|
586
631
|
if (typeof XMLHttpRequest === "undefined") return () => {
|
|
587
632
|
};
|
|
@@ -617,6 +662,7 @@ function wrapXHR(opts) {
|
|
|
617
662
|
}
|
|
618
663
|
s.start = Date.now();
|
|
619
664
|
s.reqBody = typeof body === "string" ? body : void 0;
|
|
665
|
+
const sendSiteStack = captureCallSiteStack();
|
|
620
666
|
opts.emit({
|
|
621
667
|
phase: "request",
|
|
622
668
|
reqId: s.reqId,
|
|
@@ -634,25 +680,32 @@ function wrapXHR(opts) {
|
|
|
634
680
|
method: s.method,
|
|
635
681
|
url: s.url,
|
|
636
682
|
status: this.status,
|
|
683
|
+
// XHR exposes statusText directly; same display purpose as fetch.
|
|
684
|
+
statusText: this.statusText || void 0,
|
|
637
685
|
durationMs: Date.now() - s.start,
|
|
638
686
|
resHeaders: headers,
|
|
639
687
|
resBody
|
|
640
688
|
});
|
|
641
689
|
};
|
|
642
|
-
const onError = () => {
|
|
690
|
+
const onError = (kind) => () => {
|
|
643
691
|
opts.emit({
|
|
644
692
|
phase: "error",
|
|
645
693
|
reqId: s.reqId,
|
|
646
694
|
method: s.method,
|
|
647
695
|
url: s.url,
|
|
648
696
|
durationMs: Date.now() - s.start,
|
|
649
|
-
|
|
697
|
+
// Distinguish error/timeout/abort in the message — the standard
|
|
698
|
+
// XHR `statusText` is empty for `error` and unhelpful for the
|
|
699
|
+
// others, so we synthesise a clear label.
|
|
700
|
+
errorMessage: this.statusText || `xhr ${kind}`,
|
|
701
|
+
errorName: `XHR${kind[0].toUpperCase()}${kind.slice(1)}`,
|
|
702
|
+
errorStack: sendSiteStack
|
|
650
703
|
});
|
|
651
704
|
};
|
|
652
705
|
this.addEventListener("load", onLoad);
|
|
653
|
-
this.addEventListener("error", onError);
|
|
654
|
-
this.addEventListener("timeout", onError);
|
|
655
|
-
this.addEventListener("abort", onError);
|
|
706
|
+
this.addEventListener("error", onError("error"));
|
|
707
|
+
this.addEventListener("timeout", onError("timeout"));
|
|
708
|
+
this.addEventListener("abort", onError("abort"));
|
|
656
709
|
return origSend.apply(this, [body]);
|
|
657
710
|
};
|
|
658
711
|
return () => {
|
|
@@ -661,6 +714,15 @@ function wrapXHR(opts) {
|
|
|
661
714
|
proto.setRequestHeader = origSetReqHeader;
|
|
662
715
|
};
|
|
663
716
|
}
|
|
717
|
+
function captureCallSiteStack() {
|
|
718
|
+
try {
|
|
719
|
+
throw new Error("xhr-callsite");
|
|
720
|
+
} catch (err) {
|
|
721
|
+
if (!(err instanceof Error) || !err.stack) return void 0;
|
|
722
|
+
const lines = err.stack.split("\n");
|
|
723
|
+
return lines.slice(2).join("\n") || void 0;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
664
726
|
function parseXhrHeaders(raw) {
|
|
665
727
|
const out = {};
|
|
666
728
|
if (!raw) return out;
|