dev-inspector 1.0.0
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 +21 -0
- package/README.md +101 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +14 -0
- package/lib/init.d.ts +24 -0
- package/lib/init.js +51 -0
- package/lib/logger/consoleLogger.d.ts +10 -0
- package/lib/logger/consoleLogger.js +75 -0
- package/lib/logger/networkLogger.d.ts +11 -0
- package/lib/logger/networkLogger.js +210 -0
- package/lib/storage/logStorage.d.ts +19 -0
- package/lib/storage/logStorage.js +61 -0
- package/lib/ui/logList.d.ts +7 -0
- package/lib/ui/logList.js +74 -0
- package/lib/ui/panel.d.ts +15 -0
- package/lib/ui/panel.js +342 -0
- package/lib/ui/panelStyles.d.ts +1 -0
- package/lib/ui/panelStyles.js +282 -0
- package/lib/utils/types.d.ts +23 -0
- package/lib/utils/types.js +2 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ali Buğatekin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Dev Inspector
|
|
2
|
+
|
|
3
|
+
In-page devtools-style logger panel for web apps. Capture **console** and **network** activity, store it in memory, and render it inside a lightweight UI panel.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Console interception: `log/info/warn/error/debug`
|
|
8
|
+
- Network interception: `fetch` + `XMLHttpRequest`
|
|
9
|
+
- In-memory storage with events (`EventTarget`)
|
|
10
|
+
- UI panel with tabs (Console / Network), counters, severity/status colors, and resize
|
|
11
|
+
- Vite demo playground
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i dev-inspector
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { initDevInspector } from "dev-inspector";
|
|
23
|
+
|
|
24
|
+
initDevInspector({
|
|
25
|
+
maxSize: 500,
|
|
26
|
+
networkOptions: { includeBodies: false },
|
|
27
|
+
panelOptions: { initiallyOpen: true, title: "Dev Inspector" },
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If you want manual control, you can keep the returned handles:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { initDevInspector } from "dev-inspector";
|
|
35
|
+
|
|
36
|
+
const { storage, destroy } = initDevInspector({
|
|
37
|
+
maxSize: 500,
|
|
38
|
+
networkOptions: { includeBodies: false },
|
|
39
|
+
panelOptions: { initiallyOpen: true, title: "Dev Inspector" },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
storage.clear();
|
|
43
|
+
destroy();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Demo (Development)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install
|
|
50
|
+
npm run demo
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The demo page includes interactive generators for console logs and network requests so you can verify the panel quickly.
|
|
54
|
+
|
|
55
|
+
## API (Summary)
|
|
56
|
+
|
|
57
|
+
### `initDevInspector(options)`
|
|
58
|
+
|
|
59
|
+
One-call integration that wires storage + loggers + UI.
|
|
60
|
+
|
|
61
|
+
- `initDevInspector({ maxSize?, console?, network?, panel?, consoleLevels?, networkOptions?, panelOptions?, storage? })`
|
|
62
|
+
- returns `{ storage, panel?, consoleLogger?, networkLogger?, destroy }`
|
|
63
|
+
|
|
64
|
+
### `LogStorage`
|
|
65
|
+
|
|
66
|
+
- `new LogStorage({ maxSize?: number })`
|
|
67
|
+
- `add(entry: LogEntry): void`
|
|
68
|
+
- `getAll(): LogEntry[]`
|
|
69
|
+
- `clear(): void` (emits `cleared`)
|
|
70
|
+
- `onNewLog((entry) => void): () => void` (subscribe/unsubscribe)
|
|
71
|
+
|
|
72
|
+
### `installConsoleLogger(options)`
|
|
73
|
+
|
|
74
|
+
Installs console interception and emits `LogEntry` objects.
|
|
75
|
+
|
|
76
|
+
- `installConsoleLogger({ emit, levels? }) -> { uninstall, installed }`
|
|
77
|
+
|
|
78
|
+
### `installNetworkLogger(options)`
|
|
79
|
+
|
|
80
|
+
Installs network interception for `fetch` and `XMLHttpRequest` and emits `LogEntry` objects.
|
|
81
|
+
|
|
82
|
+
- `installNetworkLogger({ emit, includeBodies?, maxBodyLength? }) -> { uninstall, installed }`
|
|
83
|
+
|
|
84
|
+
### `createPanel(options)`
|
|
85
|
+
|
|
86
|
+
Renders the UI panel and connects it to a `LogStorage`.
|
|
87
|
+
|
|
88
|
+
- `createPanel({ storage, title?, initiallyOpen?, mount? }) -> { open, close, toggle, destroy, isOpen }`
|
|
89
|
+
|
|
90
|
+
## Environment Notes
|
|
91
|
+
|
|
92
|
+
- `createPanel()` requires a browser-like environment (needs `document`).
|
|
93
|
+
- `installNetworkLogger()` only captures real browser `fetch` / `XMLHttpRequest` traffic.
|
|
94
|
+
|
|
95
|
+
## Contributing
|
|
96
|
+
|
|
97
|
+
See `CONTRIBUTING.md`.
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT (see `LICENSE`).
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { installConsoleLogger, type ConsoleLoggerHandle, type ConsoleLoggerOptions, } from "./logger/consoleLogger";
|
|
2
|
+
export { installNetworkLogger, type NetworkLoggerHandle, type NetworkLoggerOptions } from "./logger/networkLogger";
|
|
3
|
+
export { LogStorage, type LogStorageOptions } from "./storage/logStorage";
|
|
4
|
+
export { createPanel, type PanelHandle, type PanelOptions } from "./ui/panel";
|
|
5
|
+
export { init, initDevInspector, type DevInspectorHandle, type DevInspectorInitOptions } from "./init";
|
|
6
|
+
export type { ConsoleLogLevel, LogEntry, LogSource } from "./utils/types";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initDevInspector = exports.init = exports.createPanel = exports.LogStorage = exports.installNetworkLogger = exports.installConsoleLogger = void 0;
|
|
4
|
+
var consoleLogger_1 = require("./logger/consoleLogger");
|
|
5
|
+
Object.defineProperty(exports, "installConsoleLogger", { enumerable: true, get: function () { return consoleLogger_1.installConsoleLogger; } });
|
|
6
|
+
var networkLogger_1 = require("./logger/networkLogger");
|
|
7
|
+
Object.defineProperty(exports, "installNetworkLogger", { enumerable: true, get: function () { return networkLogger_1.installNetworkLogger; } });
|
|
8
|
+
var logStorage_1 = require("./storage/logStorage");
|
|
9
|
+
Object.defineProperty(exports, "LogStorage", { enumerable: true, get: function () { return logStorage_1.LogStorage; } });
|
|
10
|
+
var panel_1 = require("./ui/panel");
|
|
11
|
+
Object.defineProperty(exports, "createPanel", { enumerable: true, get: function () { return panel_1.createPanel; } });
|
|
12
|
+
var init_1 = require("./init");
|
|
13
|
+
Object.defineProperty(exports, "init", { enumerable: true, get: function () { return init_1.init; } });
|
|
14
|
+
Object.defineProperty(exports, "initDevInspector", { enumerable: true, get: function () { return init_1.initDevInspector; } });
|
package/lib/init.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ConsoleLoggerHandle } from "./logger/consoleLogger";
|
|
2
|
+
import { type NetworkLoggerHandle, type NetworkLoggerOptions } from "./logger/networkLogger";
|
|
3
|
+
import { LogStorage } from "./storage/logStorage";
|
|
4
|
+
import { type PanelHandle, type PanelOptions } from "./ui/panel";
|
|
5
|
+
import type { ConsoleLogLevel } from "./utils/types";
|
|
6
|
+
export type DevInspectorInitOptions = {
|
|
7
|
+
maxSize?: number;
|
|
8
|
+
storage?: LogStorage;
|
|
9
|
+
panel?: boolean;
|
|
10
|
+
panelOptions?: Omit<PanelOptions, "storage">;
|
|
11
|
+
console?: boolean;
|
|
12
|
+
consoleLevels?: ConsoleLogLevel[];
|
|
13
|
+
network?: boolean;
|
|
14
|
+
networkOptions?: Omit<NetworkLoggerOptions, "emit">;
|
|
15
|
+
};
|
|
16
|
+
export type DevInspectorHandle = {
|
|
17
|
+
storage: LogStorage;
|
|
18
|
+
panel?: PanelHandle;
|
|
19
|
+
consoleLogger?: ConsoleLoggerHandle;
|
|
20
|
+
networkLogger?: NetworkLoggerHandle;
|
|
21
|
+
destroy: () => void;
|
|
22
|
+
};
|
|
23
|
+
export declare function initDevInspector(options?: DevInspectorInitOptions): DevInspectorHandle;
|
|
24
|
+
export declare const init: typeof initDevInspector;
|
package/lib/init.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init = void 0;
|
|
4
|
+
exports.initDevInspector = initDevInspector;
|
|
5
|
+
const consoleLogger_1 = require("./logger/consoleLogger");
|
|
6
|
+
const networkLogger_1 = require("./logger/networkLogger");
|
|
7
|
+
const logStorage_1 = require("./storage/logStorage");
|
|
8
|
+
const panel_1 = require("./ui/panel");
|
|
9
|
+
function initDevInspector(options = {}) {
|
|
10
|
+
var _a, _b, _c, _d, _e, _f;
|
|
11
|
+
const storage = (_a = options.storage) !== null && _a !== void 0 ? _a : new logStorage_1.LogStorage({
|
|
12
|
+
maxSize: options.maxSize,
|
|
13
|
+
});
|
|
14
|
+
const consoleEnabled = (_b = options.console) !== null && _b !== void 0 ? _b : true;
|
|
15
|
+
const networkEnabled = (_c = options.network) !== null && _c !== void 0 ? _c : true;
|
|
16
|
+
const panelEnabled = (_d = options.panel) !== null && _d !== void 0 ? _d : true;
|
|
17
|
+
const consoleLogger = consoleEnabled
|
|
18
|
+
? (0, consoleLogger_1.installConsoleLogger)({
|
|
19
|
+
emit: (e) => storage.add(e),
|
|
20
|
+
levels: options.consoleLevels,
|
|
21
|
+
})
|
|
22
|
+
: undefined;
|
|
23
|
+
const networkLogger = networkEnabled
|
|
24
|
+
? (0, networkLogger_1.installNetworkLogger)(Object.assign({ emit: (e) => storage.add(e) }, ((_e = options.networkOptions) !== null && _e !== void 0 ? _e : {})))
|
|
25
|
+
: undefined;
|
|
26
|
+
const panel = panelEnabled
|
|
27
|
+
? (0, panel_1.createPanel)(Object.assign({ storage }, ((_f = options.panelOptions) !== null && _f !== void 0 ? _f : {})))
|
|
28
|
+
: undefined;
|
|
29
|
+
const destroy = () => {
|
|
30
|
+
try {
|
|
31
|
+
panel === null || panel === void 0 ? void 0 : panel.destroy();
|
|
32
|
+
}
|
|
33
|
+
catch (_a) {
|
|
34
|
+
void 0;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
consoleLogger === null || consoleLogger === void 0 ? void 0 : consoleLogger.uninstall();
|
|
38
|
+
}
|
|
39
|
+
catch (_b) {
|
|
40
|
+
void 0;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
networkLogger === null || networkLogger === void 0 ? void 0 : networkLogger.uninstall();
|
|
44
|
+
}
|
|
45
|
+
catch (_c) {
|
|
46
|
+
void 0;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return { storage, panel, consoleLogger, networkLogger, destroy };
|
|
50
|
+
}
|
|
51
|
+
exports.init = initDevInspector;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConsoleLogLevel, LogEntry } from "../utils/types";
|
|
2
|
+
export type ConsoleLoggerOptions = {
|
|
3
|
+
emit: (entry: LogEntry) => void;
|
|
4
|
+
levels?: ConsoleLogLevel[];
|
|
5
|
+
};
|
|
6
|
+
export type ConsoleLoggerHandle = {
|
|
7
|
+
uninstall: () => void;
|
|
8
|
+
installed: () => boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function installConsoleLogger(options: ConsoleLoggerOptions): ConsoleLoggerHandle;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installConsoleLogger = installConsoleLogger;
|
|
4
|
+
let seq = 0;
|
|
5
|
+
function createId() {
|
|
6
|
+
seq += 1;
|
|
7
|
+
return `${Date.now()}-${seq}`;
|
|
8
|
+
}
|
|
9
|
+
function safeStringify(value) {
|
|
10
|
+
try {
|
|
11
|
+
if (typeof value === "string")
|
|
12
|
+
return value;
|
|
13
|
+
return JSON.stringify(value, getCircularReplacer());
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
try {
|
|
17
|
+
return String(value);
|
|
18
|
+
}
|
|
19
|
+
catch (_b) {
|
|
20
|
+
return "[unserializable]";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function getCircularReplacer() {
|
|
25
|
+
const seen = new WeakSet();
|
|
26
|
+
return (_key, value) => {
|
|
27
|
+
if (typeof value === "object" && value !== null) {
|
|
28
|
+
const obj = value;
|
|
29
|
+
if (seen.has(obj))
|
|
30
|
+
return "[circular]";
|
|
31
|
+
seen.add(obj);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function formatArgs(args) {
|
|
37
|
+
return args.map(safeStringify).join(" ");
|
|
38
|
+
}
|
|
39
|
+
function installConsoleLogger(options) {
|
|
40
|
+
var _a;
|
|
41
|
+
const levels = (_a = options.levels) !== null && _a !== void 0 ? _a : ["log", "info", "warn", "error", "debug"];
|
|
42
|
+
const originals = new Map();
|
|
43
|
+
let active = true;
|
|
44
|
+
const installOne = (level) => {
|
|
45
|
+
const original = console[level];
|
|
46
|
+
originals.set(level, original);
|
|
47
|
+
const wrapped = (...args) => {
|
|
48
|
+
if (active) {
|
|
49
|
+
const entry = {
|
|
50
|
+
id: createId(),
|
|
51
|
+
source: "console",
|
|
52
|
+
level,
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
args,
|
|
55
|
+
message: formatArgs(args),
|
|
56
|
+
};
|
|
57
|
+
options.emit(entry);
|
|
58
|
+
}
|
|
59
|
+
return original.apply(console, args);
|
|
60
|
+
};
|
|
61
|
+
console[level] = wrapped;
|
|
62
|
+
};
|
|
63
|
+
levels.forEach(installOne);
|
|
64
|
+
const uninstall = () => {
|
|
65
|
+
active = false;
|
|
66
|
+
for (const [level, original] of originals.entries()) {
|
|
67
|
+
console[level] = original;
|
|
68
|
+
}
|
|
69
|
+
originals.clear();
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
uninstall,
|
|
73
|
+
installed: () => active,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LogEntry } from "../utils/types";
|
|
2
|
+
export type NetworkLoggerOptions = {
|
|
3
|
+
emit: (entry: LogEntry) => void;
|
|
4
|
+
includeBodies?: boolean;
|
|
5
|
+
maxBodyLength?: number;
|
|
6
|
+
};
|
|
7
|
+
export type NetworkLoggerHandle = {
|
|
8
|
+
uninstall: () => void;
|
|
9
|
+
installed: () => boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function installNetworkLogger(options: NetworkLoggerOptions): NetworkLoggerHandle;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installNetworkLogger = installNetworkLogger;
|
|
4
|
+
let seq = 0;
|
|
5
|
+
function createId() {
|
|
6
|
+
seq += 1;
|
|
7
|
+
return `${Date.now()}-${seq}`;
|
|
8
|
+
}
|
|
9
|
+
function now() {
|
|
10
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
11
|
+
}
|
|
12
|
+
function safeToString(value) {
|
|
13
|
+
try {
|
|
14
|
+
return String(value);
|
|
15
|
+
}
|
|
16
|
+
catch (_a) {
|
|
17
|
+
return "[unstringifiable]";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function truncateString(value, maxLen) {
|
|
21
|
+
if (maxLen <= 0)
|
|
22
|
+
return "";
|
|
23
|
+
if (value.length <= maxLen)
|
|
24
|
+
return value;
|
|
25
|
+
return value.slice(0, maxLen);
|
|
26
|
+
}
|
|
27
|
+
async function readResponseBody(response, maxLen) {
|
|
28
|
+
try {
|
|
29
|
+
const text = await response.text();
|
|
30
|
+
return truncateString(text, maxLen);
|
|
31
|
+
}
|
|
32
|
+
catch (_a) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function makeMessage(entry) {
|
|
37
|
+
var _a, _b;
|
|
38
|
+
const m = (_a = entry.method) !== null && _a !== void 0 ? _a : "";
|
|
39
|
+
const u = (_b = entry.url) !== null && _b !== void 0 ? _b : "";
|
|
40
|
+
const s = typeof entry.status === "number" ? ` ${entry.status}` : "";
|
|
41
|
+
const d = typeof entry.durationMs === "number" ? ` ${Math.round(entry.durationMs)}ms` : "";
|
|
42
|
+
return `${m} ${u}${s}${d}`.trim();
|
|
43
|
+
}
|
|
44
|
+
const xhrMeta = new WeakMap();
|
|
45
|
+
function installNetworkLogger(options) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
const includeBodies = (_a = options.includeBodies) !== null && _a !== void 0 ? _a : false;
|
|
48
|
+
const maxBodyLength = Math.max(0, (_b = options.maxBodyLength) !== null && _b !== void 0 ? _b : 20000);
|
|
49
|
+
let active = true;
|
|
50
|
+
const g = globalThis;
|
|
51
|
+
const canFetch = typeof g.fetch === "function";
|
|
52
|
+
const canXhr = typeof g.XMLHttpRequest === "function";
|
|
53
|
+
const originalFetch = g.fetch;
|
|
54
|
+
const originalXhrOpen = canXhr ? g.XMLHttpRequest.prototype.open : undefined;
|
|
55
|
+
const originalXhrSend = canXhr ? g.XMLHttpRequest.prototype.send : undefined;
|
|
56
|
+
if (canFetch) {
|
|
57
|
+
g.fetch = (async (...args) => {
|
|
58
|
+
var _a;
|
|
59
|
+
const start = now();
|
|
60
|
+
const id = createId();
|
|
61
|
+
let method;
|
|
62
|
+
let url;
|
|
63
|
+
let requestBody;
|
|
64
|
+
try {
|
|
65
|
+
const [input, init] = args;
|
|
66
|
+
if (typeof input === "string")
|
|
67
|
+
url = input;
|
|
68
|
+
else if (input && typeof input.url === "string")
|
|
69
|
+
url = input.url;
|
|
70
|
+
const reqMethodFromInit = init === null || init === void 0 ? void 0 : init.method;
|
|
71
|
+
const reqMethodFromInput = input && typeof input.method === "string" ? input.method : undefined;
|
|
72
|
+
method = ((_a = reqMethodFromInit !== null && reqMethodFromInit !== void 0 ? reqMethodFromInit : reqMethodFromInput) !== null && _a !== void 0 ? _a : "GET").toUpperCase();
|
|
73
|
+
if (includeBodies && init && "body" in init)
|
|
74
|
+
requestBody = init.body;
|
|
75
|
+
}
|
|
76
|
+
catch (_b) {
|
|
77
|
+
void 0;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const res = await originalFetch(...args);
|
|
81
|
+
const durationMs = now() - start;
|
|
82
|
+
const status = res.status;
|
|
83
|
+
let responseBody;
|
|
84
|
+
if (includeBodies) {
|
|
85
|
+
const cloned = res.clone();
|
|
86
|
+
responseBody = await readResponseBody(cloned, maxBodyLength);
|
|
87
|
+
}
|
|
88
|
+
if (active) {
|
|
89
|
+
const entry = {
|
|
90
|
+
id,
|
|
91
|
+
source: "network",
|
|
92
|
+
timestamp: Date.now(),
|
|
93
|
+
method,
|
|
94
|
+
url,
|
|
95
|
+
status,
|
|
96
|
+
durationMs,
|
|
97
|
+
requestBody: includeBodies ? requestBody : undefined,
|
|
98
|
+
responseBody: includeBodies ? responseBody : undefined,
|
|
99
|
+
message: makeMessage({ method, url, status, durationMs }),
|
|
100
|
+
};
|
|
101
|
+
options.emit(entry);
|
|
102
|
+
}
|
|
103
|
+
return res;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const durationMs = now() - start;
|
|
107
|
+
if (active) {
|
|
108
|
+
const entry = {
|
|
109
|
+
id,
|
|
110
|
+
source: "network",
|
|
111
|
+
timestamp: Date.now(),
|
|
112
|
+
method,
|
|
113
|
+
url,
|
|
114
|
+
status: undefined,
|
|
115
|
+
durationMs,
|
|
116
|
+
requestBody: includeBodies ? requestBody : undefined,
|
|
117
|
+
responseBody: includeBodies ? safeToString(err) : undefined,
|
|
118
|
+
message: makeMessage({ method, url, status: undefined, durationMs }),
|
|
119
|
+
};
|
|
120
|
+
options.emit(entry);
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (canXhr && originalXhrOpen && originalXhrSend) {
|
|
127
|
+
const proto = g.XMLHttpRequest.prototype;
|
|
128
|
+
const originalOpen = originalXhrOpen;
|
|
129
|
+
const originalSend = originalXhrSend;
|
|
130
|
+
proto.open = function (...args) {
|
|
131
|
+
try {
|
|
132
|
+
const [method, url] = args;
|
|
133
|
+
xhrMeta.set(this, { id: createId(), start: now(), method: safeToString(method).toUpperCase(), url: safeToString(url) });
|
|
134
|
+
}
|
|
135
|
+
catch (_a) {
|
|
136
|
+
xhrMeta.set(this, { id: createId(), start: now() });
|
|
137
|
+
}
|
|
138
|
+
return originalOpen.apply(this, args);
|
|
139
|
+
};
|
|
140
|
+
proto.send = function (...args) {
|
|
141
|
+
var _a;
|
|
142
|
+
const meta = (_a = xhrMeta.get(this)) !== null && _a !== void 0 ? _a : { id: createId(), start: now() };
|
|
143
|
+
if (includeBodies) {
|
|
144
|
+
try {
|
|
145
|
+
meta.requestBody = args[0];
|
|
146
|
+
}
|
|
147
|
+
catch (_b) {
|
|
148
|
+
void 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
xhrMeta.set(this, meta);
|
|
152
|
+
const onLoadEnd = () => {
|
|
153
|
+
const durationMs = now() - meta.start;
|
|
154
|
+
const status = typeof this.status === "number" ? this.status : undefined;
|
|
155
|
+
let responseBody;
|
|
156
|
+
if (includeBodies) {
|
|
157
|
+
try {
|
|
158
|
+
if (typeof this.responseText === "string")
|
|
159
|
+
responseBody = truncateString(this.responseText, maxBodyLength);
|
|
160
|
+
else
|
|
161
|
+
responseBody = undefined;
|
|
162
|
+
}
|
|
163
|
+
catch (_a) {
|
|
164
|
+
responseBody = undefined;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (active) {
|
|
168
|
+
const entry = {
|
|
169
|
+
id: meta.id,
|
|
170
|
+
source: "network",
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
method: meta.method,
|
|
173
|
+
url: meta.url,
|
|
174
|
+
status,
|
|
175
|
+
durationMs,
|
|
176
|
+
requestBody: includeBodies ? meta.requestBody : undefined,
|
|
177
|
+
responseBody: includeBodies ? responseBody : undefined,
|
|
178
|
+
message: makeMessage({ method: meta.method, url: meta.url, status, durationMs }),
|
|
179
|
+
};
|
|
180
|
+
options.emit(entry);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
try {
|
|
184
|
+
this.addEventListener("loadend", onLoadEnd, { once: true });
|
|
185
|
+
}
|
|
186
|
+
catch (_c) {
|
|
187
|
+
try {
|
|
188
|
+
this.addEventListener("loadend", onLoadEnd);
|
|
189
|
+
}
|
|
190
|
+
catch (_d) {
|
|
191
|
+
void 0;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return originalSend.apply(this, args);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const uninstall = () => {
|
|
198
|
+
active = false;
|
|
199
|
+
if (canFetch && originalFetch)
|
|
200
|
+
g.fetch = originalFetch;
|
|
201
|
+
if (canXhr && originalXhrOpen && originalXhrSend) {
|
|
202
|
+
g.XMLHttpRequest.prototype.open = originalXhrOpen;
|
|
203
|
+
g.XMLHttpRequest.prototype.send = originalXhrSend;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
return {
|
|
207
|
+
uninstall,
|
|
208
|
+
installed: () => active,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LogEntry } from "../utils/types";
|
|
2
|
+
export type LogStorageEvents = {
|
|
3
|
+
newLog: CustomEvent<LogEntry>;
|
|
4
|
+
cleared: Event;
|
|
5
|
+
};
|
|
6
|
+
export type LogStorageOptions = {
|
|
7
|
+
maxSize?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare class LogStorage extends EventTarget {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(options?: LogStorageOptions);
|
|
12
|
+
get maxSize(): number;
|
|
13
|
+
setMaxSize(next: number): void;
|
|
14
|
+
add(entry: LogEntry): void;
|
|
15
|
+
clear(): void;
|
|
16
|
+
getAll(): LogEntry[];
|
|
17
|
+
size(): number;
|
|
18
|
+
onNewLog(listener: (entry: LogEntry) => void): () => void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _LogStorage_logs, _LogStorage_maxSize;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.LogStorage = void 0;
|
|
16
|
+
class LogStorage extends EventTarget {
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
var _a;
|
|
19
|
+
super();
|
|
20
|
+
_LogStorage_logs.set(this, []);
|
|
21
|
+
_LogStorage_maxSize.set(this, void 0);
|
|
22
|
+
__classPrivateFieldSet(this, _LogStorage_maxSize, Math.max(1, (_a = options.maxSize) !== null && _a !== void 0 ? _a : 500), "f");
|
|
23
|
+
}
|
|
24
|
+
get maxSize() {
|
|
25
|
+
return __classPrivateFieldGet(this, _LogStorage_maxSize, "f");
|
|
26
|
+
}
|
|
27
|
+
setMaxSize(next) {
|
|
28
|
+
__classPrivateFieldSet(this, _LogStorage_maxSize, Math.max(1, next), "f");
|
|
29
|
+
if (__classPrivateFieldGet(this, _LogStorage_logs, "f").length > __classPrivateFieldGet(this, _LogStorage_maxSize, "f")) {
|
|
30
|
+
__classPrivateFieldSet(this, _LogStorage_logs, __classPrivateFieldGet(this, _LogStorage_logs, "f").slice(__classPrivateFieldGet(this, _LogStorage_logs, "f").length - __classPrivateFieldGet(this, _LogStorage_maxSize, "f")), "f");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
add(entry) {
|
|
34
|
+
__classPrivateFieldGet(this, _LogStorage_logs, "f").push(entry);
|
|
35
|
+
if (__classPrivateFieldGet(this, _LogStorage_logs, "f").length > __classPrivateFieldGet(this, _LogStorage_maxSize, "f")) {
|
|
36
|
+
__classPrivateFieldGet(this, _LogStorage_logs, "f").shift();
|
|
37
|
+
}
|
|
38
|
+
this.dispatchEvent(new CustomEvent("newLog", { detail: entry }));
|
|
39
|
+
}
|
|
40
|
+
clear() {
|
|
41
|
+
if (__classPrivateFieldGet(this, _LogStorage_logs, "f").length === 0)
|
|
42
|
+
return;
|
|
43
|
+
__classPrivateFieldSet(this, _LogStorage_logs, [], "f");
|
|
44
|
+
this.dispatchEvent(new Event("cleared"));
|
|
45
|
+
}
|
|
46
|
+
getAll() {
|
|
47
|
+
return __classPrivateFieldGet(this, _LogStorage_logs, "f").slice();
|
|
48
|
+
}
|
|
49
|
+
size() {
|
|
50
|
+
return __classPrivateFieldGet(this, _LogStorage_logs, "f").length;
|
|
51
|
+
}
|
|
52
|
+
onNewLog(listener) {
|
|
53
|
+
const handler = (ev) => {
|
|
54
|
+
listener(ev.detail);
|
|
55
|
+
};
|
|
56
|
+
this.addEventListener("newLog", handler);
|
|
57
|
+
return () => this.removeEventListener("newLog", handler);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.LogStorage = LogStorage;
|
|
61
|
+
_LogStorage_logs = new WeakMap(), _LogStorage_maxSize = new WeakMap();
|