dev-inspector 1.0.0 → 1.0.1
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 +20 -0
- package/lib/logger/networkLogger.js +60 -11
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -28,6 +28,26 @@ initDevInspector({
|
|
|
28
28
|
});
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## Important: Browser-only (SSR)
|
|
32
|
+
|
|
33
|
+
Dev Inspector’s UI (`createPanel()` and the default `initDevInspector()` flow) requires a **browser environment** (it needs `document`).
|
|
34
|
+
|
|
35
|
+
If your app uses **SSR** (Next.js, Remix, Nuxt, SvelteKit, etc.), do not call `initDevInspector()` at module scope on the server. Initialize it **client-side only** (e.g. in an effect, lifecycle hook, or a client-only component).
|
|
36
|
+
|
|
37
|
+
Example (client-only init with dynamic import):
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
async function initInBrowser() {
|
|
41
|
+
if (typeof window === "undefined") return;
|
|
42
|
+
const { initDevInspector } = await import("dev-inspector");
|
|
43
|
+
initDevInspector({
|
|
44
|
+
panelOptions: { initiallyOpen: true, title: "Dev Inspector" },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
initInBrowser();
|
|
49
|
+
```
|
|
50
|
+
|
|
31
51
|
If you want manual control, you can keep the returned handles:
|
|
32
52
|
|
|
33
53
|
```ts
|
|
@@ -24,6 +24,34 @@ function truncateString(value, maxLen) {
|
|
|
24
24
|
return value;
|
|
25
25
|
return value.slice(0, maxLen);
|
|
26
26
|
}
|
|
27
|
+
function coerceUrl(input) {
|
|
28
|
+
if (typeof input === "string")
|
|
29
|
+
return input;
|
|
30
|
+
if (!input)
|
|
31
|
+
return undefined;
|
|
32
|
+
if (typeof input.href === "string")
|
|
33
|
+
return input.href;
|
|
34
|
+
if (typeof input.url === "string")
|
|
35
|
+
return input.url;
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
function formatUrlForMessage(raw) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
if (!raw)
|
|
41
|
+
return "";
|
|
42
|
+
try {
|
|
43
|
+
const loc = globalThis;
|
|
44
|
+
const base = typeof ((_a = loc.location) === null || _a === void 0 ? void 0 : _a.href) === "string" ? loc.location.href : undefined;
|
|
45
|
+
const u = new URL(raw, base);
|
|
46
|
+
const origin = typeof ((_b = loc.location) === null || _b === void 0 ? void 0 : _b.origin) === "string" ? loc.location.origin : undefined;
|
|
47
|
+
if (origin && u.origin === origin)
|
|
48
|
+
return `${u.pathname}${u.search}${u.hash}`;
|
|
49
|
+
return u.href;
|
|
50
|
+
}
|
|
51
|
+
catch (_c) {
|
|
52
|
+
return raw;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
27
55
|
async function readResponseBody(response, maxLen) {
|
|
28
56
|
try {
|
|
29
57
|
const text = await response.text();
|
|
@@ -34,13 +62,36 @@ async function readResponseBody(response, maxLen) {
|
|
|
34
62
|
}
|
|
35
63
|
}
|
|
36
64
|
function makeMessage(entry) {
|
|
37
|
-
var _a
|
|
65
|
+
var _a;
|
|
38
66
|
const m = (_a = entry.method) !== null && _a !== void 0 ? _a : "";
|
|
39
|
-
const u = (
|
|
67
|
+
const u = formatUrlForMessage(entry.url);
|
|
40
68
|
const s = typeof entry.status === "number" ? ` ${entry.status}` : "";
|
|
41
69
|
const d = typeof entry.durationMs === "number" ? ` ${Math.round(entry.durationMs)}ms` : "";
|
|
42
70
|
return `${m} ${u}${s}${d}`.trim();
|
|
43
71
|
}
|
|
72
|
+
function getFetchTimingDurationMs(url, startPerf) {
|
|
73
|
+
if (!url)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (typeof performance === "undefined")
|
|
76
|
+
return undefined;
|
|
77
|
+
if (typeof performance.getEntriesByName !== "function")
|
|
78
|
+
return undefined;
|
|
79
|
+
const entries = performance.getEntriesByName(url);
|
|
80
|
+
if (!entries || entries.length === 0)
|
|
81
|
+
return undefined;
|
|
82
|
+
const candidates = entries.filter((e) => {
|
|
83
|
+
const rt = e;
|
|
84
|
+
const initiatorOk = typeof rt.initiatorType === "string" ? rt.initiatorType === "fetch" : true;
|
|
85
|
+
const startOk = typeof rt.startTime === "number" ? Math.abs(rt.startTime - startPerf) < 1000 : true;
|
|
86
|
+
return initiatorOk && startOk;
|
|
87
|
+
});
|
|
88
|
+
const chosen = (candidates.length > 0 ? candidates : entries)[(candidates.length > 0 ? candidates : entries).length - 1];
|
|
89
|
+
if (typeof chosen.duration === "number" && chosen.duration > 0)
|
|
90
|
+
return chosen.duration;
|
|
91
|
+
if (typeof chosen.responseEnd === "number" && typeof chosen.startTime === "number" && chosen.responseEnd > 0)
|
|
92
|
+
return chosen.responseEnd - chosen.startTime;
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
44
95
|
const xhrMeta = new WeakMap();
|
|
45
96
|
function installNetworkLogger(options) {
|
|
46
97
|
var _a, _b;
|
|
@@ -55,30 +106,28 @@ function installNetworkLogger(options) {
|
|
|
55
106
|
const originalXhrSend = canXhr ? g.XMLHttpRequest.prototype.send : undefined;
|
|
56
107
|
if (canFetch) {
|
|
57
108
|
g.fetch = (async (...args) => {
|
|
58
|
-
var _a;
|
|
109
|
+
var _a, _b, _c;
|
|
59
110
|
const start = now();
|
|
111
|
+
const startPerf = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : start;
|
|
60
112
|
const id = createId();
|
|
61
113
|
let method;
|
|
62
114
|
let url;
|
|
63
115
|
let requestBody;
|
|
64
116
|
try {
|
|
65
117
|
const [input, init] = args;
|
|
66
|
-
|
|
67
|
-
url = input;
|
|
68
|
-
else if (input && typeof input.url === "string")
|
|
69
|
-
url = input.url;
|
|
118
|
+
url = coerceUrl(input);
|
|
70
119
|
const reqMethodFromInit = init === null || init === void 0 ? void 0 : init.method;
|
|
71
120
|
const reqMethodFromInput = input && typeof input.method === "string" ? input.method : undefined;
|
|
72
121
|
method = ((_a = reqMethodFromInit !== null && reqMethodFromInit !== void 0 ? reqMethodFromInit : reqMethodFromInput) !== null && _a !== void 0 ? _a : "GET").toUpperCase();
|
|
73
122
|
if (includeBodies && init && "body" in init)
|
|
74
123
|
requestBody = init.body;
|
|
75
124
|
}
|
|
76
|
-
catch (
|
|
125
|
+
catch (_d) {
|
|
77
126
|
void 0;
|
|
78
127
|
}
|
|
79
128
|
try {
|
|
80
|
-
const res = await originalFetch
|
|
81
|
-
const durationMs = now() - start;
|
|
129
|
+
const res = await Reflect.apply(originalFetch, globalThis, args);
|
|
130
|
+
const durationMs = (_b = getFetchTimingDurationMs(url, startPerf)) !== null && _b !== void 0 ? _b : (now() - start);
|
|
82
131
|
const status = res.status;
|
|
83
132
|
let responseBody;
|
|
84
133
|
if (includeBodies) {
|
|
@@ -103,7 +152,7 @@ function installNetworkLogger(options) {
|
|
|
103
152
|
return res;
|
|
104
153
|
}
|
|
105
154
|
catch (err) {
|
|
106
|
-
const durationMs = now() - start;
|
|
155
|
+
const durationMs = (_c = getFetchTimingDurationMs(url, startPerf)) !== null && _c !== void 0 ? _c : (now() - start);
|
|
107
156
|
if (active) {
|
|
108
157
|
const entry = {
|
|
109
158
|
id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dev-inspector",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "In-page devtools-style logger panel for web apps (console + network).",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"lib/**/*"
|
|
41
41
|
],
|
|
42
|
-
|
|
43
42
|
"devDependencies": {
|
|
44
43
|
"@eslint/js": "^9.39.2",
|
|
45
44
|
"eslint": "^9.39.2",
|