@mt0926/node-network-devtools 0.1.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/BUILD.md +204 -0
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/README.zh-CN.md +310 -0
- package/dist/esm/adapters/nextjs.js +123 -0
- package/dist/esm/adapters/nextjs.js.map +1 -0
- package/dist/esm/cdp/cdp-bridge.js +312 -0
- package/dist/esm/cdp/cdp-bridge.js.map +1 -0
- package/dist/esm/cli.js +203 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/config.js +136 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/context/context-manager.js +126 -0
- package/dist/esm/context/context-manager.js.map +1 -0
- package/dist/esm/gui/browser-launcher.js +165 -0
- package/dist/esm/gui/browser-launcher.js.map +1 -0
- package/dist/esm/gui/event-bridge.js +192 -0
- package/dist/esm/gui/event-bridge.js.map +1 -0
- package/dist/esm/gui/port-utils.js +80 -0
- package/dist/esm/gui/port-utils.js.map +1 -0
- package/dist/esm/gui/server.js +227 -0
- package/dist/esm/gui/server.js.map +1 -0
- package/dist/esm/gui/websocket-hub.js +326 -0
- package/dist/esm/gui/websocket-hub.js.map +1 -0
- package/dist/esm/index.js +90 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/interceptors/http-patcher.js +203 -0
- package/dist/esm/interceptors/http-patcher.js.map +1 -0
- package/dist/esm/interceptors/undici-patcher.js +324 -0
- package/dist/esm/interceptors/undici-patcher.js.map +1 -0
- package/dist/esm/register.js +132 -0
- package/dist/esm/register.js.map +1 -0
- package/dist/esm/store/ring-buffer.js +236 -0
- package/dist/esm/store/ring-buffer.js.map +1 -0
- package/dist/esm/test-setup.js +7 -0
- package/dist/esm/test-setup.js.map +1 -0
- package/dist/gui/assets/index.css +1 -0
- package/dist/gui/assets/index.js +40 -0
- package/dist/gui/index.html +14 -0
- package/dist/types/adapters/nextjs.d.ts +80 -0
- package/dist/types/adapters/nextjs.d.ts.map +1 -0
- package/dist/types/cdp/cdp-bridge.d.ts +86 -0
- package/dist/types/cdp/cdp-bridge.d.ts.map +1 -0
- package/dist/types/cli.d.ts +8 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/config.d.ts +57 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/context/context-manager.d.ts +96 -0
- package/dist/types/context/context-manager.d.ts.map +1 -0
- package/dist/types/gui/browser-launcher.d.ts +52 -0
- package/dist/types/gui/browser-launcher.d.ts.map +1 -0
- package/dist/types/gui/event-bridge.d.ts +36 -0
- package/dist/types/gui/event-bridge.d.ts.map +1 -0
- package/dist/types/gui/port-utils.d.ts +25 -0
- package/dist/types/gui/port-utils.d.ts.map +1 -0
- package/dist/types/gui/server.d.ts +50 -0
- package/dist/types/gui/server.d.ts.map +1 -0
- package/dist/types/gui/websocket-hub.d.ts +67 -0
- package/dist/types/gui/websocket-hub.d.ts.map +1 -0
- package/dist/types/index.d.ts +44 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/interceptors/http-patcher.d.ts +32 -0
- package/dist/types/interceptors/http-patcher.d.ts.map +1 -0
- package/dist/types/interceptors/undici-patcher.d.ts +37 -0
- package/dist/types/interceptors/undici-patcher.d.ts.map +1 -0
- package/dist/types/register.d.ts +18 -0
- package/dist/types/register.d.ts.map +1 -0
- package/dist/types/store/ring-buffer.d.ts +148 -0
- package/dist/types/store/ring-buffer.d.ts.map +1 -0
- package/dist/types/test-setup.d.ts +7 -0
- package/dist/types/test-setup.d.ts.map +1 -0
- package/package.json +103 -0
- package/templates/instrumentation.ts +32 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 模块拦截器
|
|
3
|
+
*
|
|
4
|
+
* 使用 @mswjs/interceptors 库拦截 HTTP 请求
|
|
5
|
+
* 这是一个专门为此设计的库,支持 ESM 和 CJS
|
|
6
|
+
*/
|
|
7
|
+
import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest';
|
|
8
|
+
import { nanoid } from 'nanoid';
|
|
9
|
+
import { getRequestStore } from '../store/ring-buffer.js';
|
|
10
|
+
import { getConfig } from '../config.js';
|
|
11
|
+
import { getEventBridge } from '../gui/event-bridge.js';
|
|
12
|
+
let installed = false;
|
|
13
|
+
let interceptor = null;
|
|
14
|
+
// 用于获取当前 TraceID 的回调(由 ContextManager 设置)
|
|
15
|
+
let getTraceIdCallback = null;
|
|
16
|
+
/**
|
|
17
|
+
* 设置获取 TraceID 的回调
|
|
18
|
+
*/
|
|
19
|
+
export function setTraceIdGetter(getter) {
|
|
20
|
+
getTraceIdCallback = getter;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 生成请求 ID
|
|
24
|
+
*/
|
|
25
|
+
function generateRequestId() {
|
|
26
|
+
return `req_${nanoid(12)}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 捕获堆栈追踪
|
|
30
|
+
*/
|
|
31
|
+
function captureStackTrace() {
|
|
32
|
+
const err = new Error();
|
|
33
|
+
const stack = err.stack || '';
|
|
34
|
+
// 过滤掉内部栈帧
|
|
35
|
+
const lines = stack.split('\n');
|
|
36
|
+
const filteredLines = lines.filter(line => {
|
|
37
|
+
if (line.trim().startsWith('Error'))
|
|
38
|
+
return false;
|
|
39
|
+
if (line.includes('http-patcher'))
|
|
40
|
+
return false;
|
|
41
|
+
if (line.includes('node:http'))
|
|
42
|
+
return false;
|
|
43
|
+
if (line.includes('node:https'))
|
|
44
|
+
return false;
|
|
45
|
+
if (line.includes('node:_http'))
|
|
46
|
+
return false;
|
|
47
|
+
if (line.includes('@mswjs/interceptors'))
|
|
48
|
+
return false;
|
|
49
|
+
return true;
|
|
50
|
+
});
|
|
51
|
+
return filteredLines.join('\n');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 将 headers 转换为 Record 格式
|
|
55
|
+
*/
|
|
56
|
+
function headersToRecord(headers) {
|
|
57
|
+
const result = {};
|
|
58
|
+
headers.forEach((value, key) => {
|
|
59
|
+
result[key.toLowerCase()] = value;
|
|
60
|
+
});
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
// 存储请求元数据
|
|
64
|
+
const requestMetadata = new Map();
|
|
65
|
+
/**
|
|
66
|
+
* 安装 HTTP 拦截器
|
|
67
|
+
*/
|
|
68
|
+
export function install() {
|
|
69
|
+
if (installed)
|
|
70
|
+
return;
|
|
71
|
+
interceptor = new ClientRequestInterceptor();
|
|
72
|
+
// 监听请求事件
|
|
73
|
+
interceptor.on('request', async ({ request, requestId }) => {
|
|
74
|
+
const config = getConfig();
|
|
75
|
+
if (!config.interceptHttp)
|
|
76
|
+
return;
|
|
77
|
+
const url = request.url;
|
|
78
|
+
// 检查是否应该忽略此 URL
|
|
79
|
+
for (const pattern of config.ignoreUrls) {
|
|
80
|
+
if (pattern.test(url)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const myRequestId = generateRequestId();
|
|
85
|
+
const stackTrace = captureStackTrace();
|
|
86
|
+
const traceId = getTraceIdCallback?.() || `trace_${nanoid(16)}`;
|
|
87
|
+
const timestamp = Date.now();
|
|
88
|
+
// 获取请求体
|
|
89
|
+
let body;
|
|
90
|
+
if (!config.disableBodyCapture && request.body) {
|
|
91
|
+
try {
|
|
92
|
+
const clonedRequest = request.clone();
|
|
93
|
+
const arrayBuffer = await clonedRequest.arrayBuffer();
|
|
94
|
+
body = Buffer.from(arrayBuffer);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// 忽略错误
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 存储请求数据
|
|
101
|
+
const requestData = {
|
|
102
|
+
id: myRequestId,
|
|
103
|
+
traceId,
|
|
104
|
+
url,
|
|
105
|
+
method: request.method,
|
|
106
|
+
headers: headersToRecord(request.headers),
|
|
107
|
+
body,
|
|
108
|
+
stackTrace,
|
|
109
|
+
timestamp,
|
|
110
|
+
};
|
|
111
|
+
getRequestStore().add(requestData);
|
|
112
|
+
// 通知 Event Bridge
|
|
113
|
+
try {
|
|
114
|
+
const eventBridge = getEventBridge();
|
|
115
|
+
if (eventBridge.isRunning()) {
|
|
116
|
+
eventBridge.emitRequestStart(requestData);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// 忽略 Event Bridge 错误
|
|
121
|
+
}
|
|
122
|
+
// 存储元数据以便在响应时使用
|
|
123
|
+
requestMetadata.set(requestId, {
|
|
124
|
+
requestId: myRequestId,
|
|
125
|
+
traceId,
|
|
126
|
+
stackTrace,
|
|
127
|
+
timestamp,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
// 监听响应事件
|
|
131
|
+
interceptor.on('response', async ({ response, requestId }) => {
|
|
132
|
+
const metadata = requestMetadata.get(requestId);
|
|
133
|
+
if (!metadata)
|
|
134
|
+
return;
|
|
135
|
+
const config = getConfig();
|
|
136
|
+
// 获取响应体
|
|
137
|
+
let responseBody;
|
|
138
|
+
if (!config.disableBodyCapture) {
|
|
139
|
+
try {
|
|
140
|
+
const clonedResponse = response.clone();
|
|
141
|
+
const arrayBuffer = await clonedResponse.arrayBuffer();
|
|
142
|
+
responseBody = Buffer.from(arrayBuffer);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// 忽略错误
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const responseData = {
|
|
149
|
+
statusCode: response.status,
|
|
150
|
+
statusMessage: response.statusText,
|
|
151
|
+
headers: headersToRecord(response.headers),
|
|
152
|
+
body: responseBody,
|
|
153
|
+
};
|
|
154
|
+
getRequestStore().updateResponse(metadata.requestId, responseData);
|
|
155
|
+
// 通知 Event Bridge
|
|
156
|
+
try {
|
|
157
|
+
const eventBridge = getEventBridge();
|
|
158
|
+
if (eventBridge.isRunning()) {
|
|
159
|
+
eventBridge.emitRequestComplete(metadata.requestId, responseData);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// 忽略 Event Bridge 错误
|
|
164
|
+
}
|
|
165
|
+
// 更新时序数据
|
|
166
|
+
getRequestStore().updateTiming(metadata.requestId, {
|
|
167
|
+
start: metadata.timestamp,
|
|
168
|
+
total: Date.now() - metadata.timestamp,
|
|
169
|
+
});
|
|
170
|
+
// 清理元数据
|
|
171
|
+
requestMetadata.delete(requestId);
|
|
172
|
+
});
|
|
173
|
+
// 启用拦截器
|
|
174
|
+
interceptor.apply();
|
|
175
|
+
installed = true;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 卸载 HTTP 拦截器
|
|
179
|
+
*/
|
|
180
|
+
export function uninstall() {
|
|
181
|
+
if (!installed || !interceptor)
|
|
182
|
+
return;
|
|
183
|
+
interceptor.dispose();
|
|
184
|
+
interceptor = null;
|
|
185
|
+
requestMetadata.clear();
|
|
186
|
+
installed = false;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* 检查是否已安装
|
|
190
|
+
*/
|
|
191
|
+
export function isInstalled() {
|
|
192
|
+
return installed;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* HttpPatcher 类(用于导出)
|
|
196
|
+
*/
|
|
197
|
+
export const HttpPatcher = {
|
|
198
|
+
install,
|
|
199
|
+
uninstall,
|
|
200
|
+
isInstalled,
|
|
201
|
+
setTraceIdGetter,
|
|
202
|
+
};
|
|
203
|
+
//# sourceMappingURL=http-patcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-patcher.js","sourceRoot":"","sources":["../../../src/interceptors/http-patcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undici/Fetch 拦截器
|
|
3
|
+
*
|
|
4
|
+
* 拦截 Node.js 原生 fetch API(基于 undici)
|
|
5
|
+
* 使用 Agent.compose() 方法添加拦截器
|
|
6
|
+
*/
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
import { nanoid } from 'nanoid';
|
|
9
|
+
import { getRequestStore } from '../store/ring-buffer.js';
|
|
10
|
+
import { getConfig } from '../config.js';
|
|
11
|
+
import { getCurrentTraceId } from '../context/context-manager.js';
|
|
12
|
+
import { getEventBridge } from '../gui/event-bridge.js';
|
|
13
|
+
// 使用 createRequire 获取 undici 模块
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
let undici = null;
|
|
16
|
+
let installed = false;
|
|
17
|
+
let originalDispatcher = null;
|
|
18
|
+
// 用于获取当前 TraceID 的回调
|
|
19
|
+
let getTraceIdCallback = null;
|
|
20
|
+
/**
|
|
21
|
+
* 设置获取 TraceID 的回调
|
|
22
|
+
*/
|
|
23
|
+
export function setTraceIdGetter(getter) {
|
|
24
|
+
getTraceIdCallback = getter;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 生成请求 ID
|
|
28
|
+
*/
|
|
29
|
+
function generateRequestId() {
|
|
30
|
+
return `req_${nanoid(12)}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 捕获堆栈追踪
|
|
34
|
+
*/
|
|
35
|
+
function captureStackTrace() {
|
|
36
|
+
const err = new Error();
|
|
37
|
+
const stack = err.stack || '';
|
|
38
|
+
const lines = stack.split('\n');
|
|
39
|
+
const filteredLines = lines.filter(line => {
|
|
40
|
+
if (line.trim().startsWith('Error'))
|
|
41
|
+
return false;
|
|
42
|
+
if (line.includes('undici-patcher'))
|
|
43
|
+
return false;
|
|
44
|
+
if (line.includes('node:internal'))
|
|
45
|
+
return false;
|
|
46
|
+
return true;
|
|
47
|
+
});
|
|
48
|
+
return filteredLines.join('\n');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 将 headers 转换为 Record 格式
|
|
52
|
+
*/
|
|
53
|
+
function headersToRecord(headers) {
|
|
54
|
+
const result = {};
|
|
55
|
+
if (!headers)
|
|
56
|
+
return result;
|
|
57
|
+
// 处理数组格式 [key1, value1, key2, value2, ...] 或 [Buffer, Buffer, ...]
|
|
58
|
+
// undici 的响应头是这种格式,元素可能是 Buffer
|
|
59
|
+
// 注意:必须先检查数组,因为数组也有 entries 方法
|
|
60
|
+
if (Array.isArray(headers)) {
|
|
61
|
+
for (let i = 0; i < headers.length; i += 2) {
|
|
62
|
+
const key = headers[i];
|
|
63
|
+
const value = headers[i + 1];
|
|
64
|
+
if (key !== undefined && value !== undefined) {
|
|
65
|
+
const keyStr = Buffer.isBuffer(key) ? key.toString('utf8') : String(key);
|
|
66
|
+
const valueStr = Buffer.isBuffer(value) ? value.toString('utf8') : String(value);
|
|
67
|
+
result[keyStr.toLowerCase()] = valueStr;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
// 处理 Headers 对象(有 entries 方法,但不是数组)
|
|
73
|
+
if (typeof headers.entries === 'function') {
|
|
74
|
+
for (const [key, value] of headers.entries()) {
|
|
75
|
+
result[String(key).toLowerCase()] = String(value);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
// 处理普通对象
|
|
80
|
+
if (typeof headers === 'object') {
|
|
81
|
+
for (const key of Object.keys(headers)) {
|
|
82
|
+
const value = headers[key];
|
|
83
|
+
if (value !== undefined) {
|
|
84
|
+
result[String(key).toLowerCase()] = String(value);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 创建拦截器函数
|
|
92
|
+
*/
|
|
93
|
+
function createInterceptor() {
|
|
94
|
+
return (dispatch) => {
|
|
95
|
+
return function interceptedDispatch(opts, handler) {
|
|
96
|
+
const config = getConfig();
|
|
97
|
+
// 如果禁用了 undici 拦截,直接调用原始 dispatch
|
|
98
|
+
if (!config.interceptUndici) {
|
|
99
|
+
return dispatch(opts, handler);
|
|
100
|
+
}
|
|
101
|
+
// 构建完整 URL
|
|
102
|
+
const origin = opts.origin?.toString() || '';
|
|
103
|
+
const path = opts.path || '/';
|
|
104
|
+
const url = `${origin}${path}`;
|
|
105
|
+
console.log('[undici-patcher] 拦截到请求:', opts.method || 'GET', url);
|
|
106
|
+
// 检查是否应该忽略此 URL
|
|
107
|
+
for (const pattern of config.ignoreUrls) {
|
|
108
|
+
if (pattern.test(url)) {
|
|
109
|
+
return dispatch(opts, handler);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const requestId = generateRequestId();
|
|
113
|
+
const stackTrace = captureStackTrace();
|
|
114
|
+
// 优先使用上下文管理器的 TraceID,其次使用回调,最后自动生成
|
|
115
|
+
const traceId = getCurrentTraceId() || getTraceIdCallback?.() || `trace_${nanoid(16)}`;
|
|
116
|
+
const timestamp = Date.now();
|
|
117
|
+
// 存储请求数据
|
|
118
|
+
const requestData = {
|
|
119
|
+
id: requestId,
|
|
120
|
+
traceId,
|
|
121
|
+
url,
|
|
122
|
+
method: opts.method || 'GET',
|
|
123
|
+
headers: headersToRecord(opts.headers),
|
|
124
|
+
stackTrace,
|
|
125
|
+
timestamp,
|
|
126
|
+
};
|
|
127
|
+
// 捕获请求体
|
|
128
|
+
if (!config.disableBodyCapture && opts.body) {
|
|
129
|
+
if (typeof opts.body === 'string') {
|
|
130
|
+
requestData.body = Buffer.from(opts.body);
|
|
131
|
+
}
|
|
132
|
+
else if (Buffer.isBuffer(opts.body)) {
|
|
133
|
+
requestData.body = opts.body;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
getRequestStore().add(requestData);
|
|
137
|
+
console.log('[undici-patcher] 请求已添加到存储:', requestId, url);
|
|
138
|
+
// 通知 Event Bridge
|
|
139
|
+
try {
|
|
140
|
+
const eventBridge = getEventBridge();
|
|
141
|
+
if (eventBridge.isRunning()) {
|
|
142
|
+
eventBridge.emitRequestStart(requestData);
|
|
143
|
+
console.log('[undici-patcher] 已通知 Event Bridge:', requestId);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
console.log('[undici-patcher] Event Bridge 通知失败:', err);
|
|
148
|
+
}
|
|
149
|
+
// 包装 handler 来捕获响应
|
|
150
|
+
const wrappedHandler = createWrappedHandler(handler, requestId, timestamp, config);
|
|
151
|
+
return dispatch(opts, wrappedHandler);
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 创建包装的 handler 来捕获响应数据
|
|
157
|
+
*/
|
|
158
|
+
function createWrappedHandler(originalHandler, requestId, timestamp, config) {
|
|
159
|
+
const responseChunks = [];
|
|
160
|
+
return {
|
|
161
|
+
onConnect(abort) {
|
|
162
|
+
if (originalHandler.onConnect) {
|
|
163
|
+
originalHandler.onConnect(abort);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
onError(error) {
|
|
167
|
+
// 记录错误
|
|
168
|
+
const errorData = {
|
|
169
|
+
message: error.message,
|
|
170
|
+
code: error.code,
|
|
171
|
+
};
|
|
172
|
+
getRequestStore().updateError(requestId, errorData);
|
|
173
|
+
// 通知 Event Bridge
|
|
174
|
+
try {
|
|
175
|
+
const eventBridge = getEventBridge();
|
|
176
|
+
if (eventBridge.isRunning()) {
|
|
177
|
+
eventBridge.emitRequestError(requestId, errorData);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// 忽略 Event Bridge 错误
|
|
182
|
+
}
|
|
183
|
+
if (originalHandler.onError) {
|
|
184
|
+
originalHandler.onError(error);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
onUpgrade(statusCode, headers, socket) {
|
|
188
|
+
if (originalHandler.onUpgrade) {
|
|
189
|
+
originalHandler.onUpgrade(statusCode, headers, socket);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
onHeaders(statusCode, headers, resume, statusText) {
|
|
193
|
+
// 记录响应头
|
|
194
|
+
const responseData = {
|
|
195
|
+
statusCode,
|
|
196
|
+
statusMessage: statusText || '',
|
|
197
|
+
headers: headersToRecord(headers),
|
|
198
|
+
};
|
|
199
|
+
getRequestStore().updateResponse(requestId, responseData);
|
|
200
|
+
// 通知 Event Bridge(响应头已接收)
|
|
201
|
+
try {
|
|
202
|
+
const eventBridge = getEventBridge();
|
|
203
|
+
if (eventBridge.isRunning()) {
|
|
204
|
+
eventBridge.emitRequestComplete(requestId, responseData);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// 忽略 Event Bridge 错误
|
|
209
|
+
}
|
|
210
|
+
if (originalHandler.onHeaders) {
|
|
211
|
+
return originalHandler.onHeaders(statusCode, headers, resume, statusText);
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
},
|
|
215
|
+
onData(chunk) {
|
|
216
|
+
// 捕获响应体
|
|
217
|
+
if (!config.disableBodyCapture) {
|
|
218
|
+
responseChunks.push(chunk);
|
|
219
|
+
}
|
|
220
|
+
if (originalHandler.onData) {
|
|
221
|
+
return originalHandler.onData(chunk);
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
},
|
|
225
|
+
onComplete(trailers) {
|
|
226
|
+
// 更新响应体和时序数据
|
|
227
|
+
if (responseChunks.length > 0) {
|
|
228
|
+
const storedReq = getRequestStore().get(requestId);
|
|
229
|
+
if (storedReq && storedReq.response) {
|
|
230
|
+
storedReq.response.body = Buffer.concat(responseChunks);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
getRequestStore().updateTiming(requestId, {
|
|
234
|
+
start: timestamp,
|
|
235
|
+
total: Date.now() - timestamp,
|
|
236
|
+
});
|
|
237
|
+
if (originalHandler.onComplete) {
|
|
238
|
+
originalHandler.onComplete(trailers);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
onBodySent(chunkSize, totalBytesSent) {
|
|
242
|
+
if (originalHandler.onBodySent) {
|
|
243
|
+
originalHandler.onBodySent(chunkSize, totalBytesSent);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 尝试加载 undici 模块
|
|
250
|
+
*/
|
|
251
|
+
function loadUndici() {
|
|
252
|
+
try {
|
|
253
|
+
return require('undici');
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// undici 可能未安装
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 安装 Undici/Fetch 拦截器
|
|
262
|
+
*/
|
|
263
|
+
export function install() {
|
|
264
|
+
if (installed)
|
|
265
|
+
return;
|
|
266
|
+
// 尝试加载 undici
|
|
267
|
+
if (!undici) {
|
|
268
|
+
undici = loadUndici();
|
|
269
|
+
}
|
|
270
|
+
if (!undici) {
|
|
271
|
+
console.warn('[node-network-devtools] undici 模块未找到,fetch 拦截将不可用');
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// 保存原始 dispatcher
|
|
275
|
+
originalDispatcher = undici.getGlobalDispatcher();
|
|
276
|
+
console.log('[undici-patcher] 原始 dispatcher:', originalDispatcher?.constructor?.name);
|
|
277
|
+
// 创建带拦截器的 Agent
|
|
278
|
+
const interceptingAgent = new undici.Agent().compose(createInterceptor());
|
|
279
|
+
console.log('[undici-patcher] 拦截 Agent 已创建');
|
|
280
|
+
// 设置全局 dispatcher
|
|
281
|
+
undici.setGlobalDispatcher(interceptingAgent);
|
|
282
|
+
console.log('[undici-patcher] 全局 dispatcher 已设置');
|
|
283
|
+
// 验证设置
|
|
284
|
+
const currentDispatcher = undici.getGlobalDispatcher();
|
|
285
|
+
console.log('[undici-patcher] 当前 dispatcher:', currentDispatcher?.constructor?.name);
|
|
286
|
+
installed = true;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 卸载 Undici/Fetch 拦截器
|
|
290
|
+
*/
|
|
291
|
+
export function uninstall() {
|
|
292
|
+
if (!installed || !undici || !originalDispatcher)
|
|
293
|
+
return;
|
|
294
|
+
// 恢复原始 dispatcher
|
|
295
|
+
undici.setGlobalDispatcher(originalDispatcher);
|
|
296
|
+
originalDispatcher = null;
|
|
297
|
+
installed = false;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 检查是否已安装
|
|
301
|
+
*/
|
|
302
|
+
export function isInstalled() {
|
|
303
|
+
return installed;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* 检查 undici 是否可用
|
|
307
|
+
*/
|
|
308
|
+
export function isUndiciAvailable() {
|
|
309
|
+
if (!undici) {
|
|
310
|
+
undici = loadUndici();
|
|
311
|
+
}
|
|
312
|
+
return undici !== null;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* UndiciPatcher 对象
|
|
316
|
+
*/
|
|
317
|
+
export const UndiciPatcher = {
|
|
318
|
+
install,
|
|
319
|
+
uninstall,
|
|
320
|
+
isInstalled,
|
|
321
|
+
isUndiciAvailable,
|
|
322
|
+
setTraceIdGetter,
|
|
323
|
+
};
|
|
324
|
+
//# sourceMappingURL=undici-patcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undici-patcher.js","sourceRoot":"","sources":["../../../src/interceptors/undici-patcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动注册入口
|
|
3
|
+
*
|
|
4
|
+
* 使用方式:node -r node-network-devtools/register your-script.js
|
|
5
|
+
* 或:node --import node-network-devtools/register your-script.js
|
|
6
|
+
*
|
|
7
|
+
* 注意:Network 面板功能需要:
|
|
8
|
+
* - Node.js 20.18.0+ 版本
|
|
9
|
+
* - --experimental-network-inspection 标志
|
|
10
|
+
* - Chrome DevTools 目前还不支持显示 Network 事件(功能待实现)
|
|
11
|
+
*/
|
|
12
|
+
import { getConfig } from './config.js';
|
|
13
|
+
import { HttpPatcher } from './interceptors/http-patcher.js';
|
|
14
|
+
import { UndiciPatcher } from './interceptors/undici-patcher.js';
|
|
15
|
+
import { getCDPBridge, isInspectorEnabled, getInspectorUrl } from './cdp/cdp-bridge.js';
|
|
16
|
+
import { getRequestStore } from './store/ring-buffer.js';
|
|
17
|
+
import { getGUIServer } from './gui/server.js';
|
|
18
|
+
import { getEventBridge } from './gui/event-bridge.js';
|
|
19
|
+
import { openBrowser } from './gui/browser-launcher.js';
|
|
20
|
+
import * as inspector from 'node:inspector';
|
|
21
|
+
// 检查 Network API 是否可用
|
|
22
|
+
const hasNetworkAPI = typeof inspector.Network?.requestWillBeSent === 'function';
|
|
23
|
+
/**
|
|
24
|
+
* 输出警告信息
|
|
25
|
+
*/
|
|
26
|
+
function warn(message) {
|
|
27
|
+
console.warn(`\x1b[33m[node-network-devtools]\x1b[0m ${message}`);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 输出信息
|
|
31
|
+
*/
|
|
32
|
+
function info(message) {
|
|
33
|
+
console.log(`\x1b[36m[node-network-devtools]\x1b[0m ${message}`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 初始化所有拦截器和 CDP 桥接
|
|
37
|
+
*/
|
|
38
|
+
async function initialize() {
|
|
39
|
+
const config = getConfig();
|
|
40
|
+
// 检查 Inspector 是否启用
|
|
41
|
+
if (!isInspectorEnabled()) {
|
|
42
|
+
warn('Inspector 未启用!请使用 --inspect 或 --inspect-brk 标志启动 Node.js');
|
|
43
|
+
warn('示例:node --inspect -r node-network-devtools/register your-script.js');
|
|
44
|
+
warn('或使用 CLI:npx node-network-devtools your-script.js');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const inspectorUrl = getInspectorUrl();
|
|
48
|
+
info(`Inspector 已启用: ${inspectorUrl}`);
|
|
49
|
+
// 安装 HTTP 拦截器
|
|
50
|
+
if (config.interceptHttp) {
|
|
51
|
+
try {
|
|
52
|
+
HttpPatcher.install();
|
|
53
|
+
info('HTTP/HTTPS 拦截器已安装');
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
warn(`HTTP 拦截器安装失败: ${error}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 安装 Undici/Fetch 拦截器
|
|
60
|
+
if (config.interceptUndici) {
|
|
61
|
+
try {
|
|
62
|
+
UndiciPatcher.install();
|
|
63
|
+
info('Undici/Fetch 拦截器已安装');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
warn(`Undici 拦截器安装失败: ${error}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 连接 CDP Bridge
|
|
70
|
+
if (config.autoConnect) {
|
|
71
|
+
try {
|
|
72
|
+
const bridge = getCDPBridge();
|
|
73
|
+
await bridge.connect();
|
|
74
|
+
info('CDP Bridge 已连接');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
warn(`CDP Bridge 连接失败: ${error}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 启动 GUI 服务器(如果启用)
|
|
81
|
+
if (config.guiEnabled) {
|
|
82
|
+
try {
|
|
83
|
+
const guiServer = getGUIServer();
|
|
84
|
+
const { url, wsPort } = await guiServer.start({
|
|
85
|
+
guiPort: config.guiPort,
|
|
86
|
+
wsPort: config.wsPort,
|
|
87
|
+
host: config.guiHost,
|
|
88
|
+
});
|
|
89
|
+
// 启动 Event Bridge
|
|
90
|
+
const eventBridge = getEventBridge();
|
|
91
|
+
eventBridge.start();
|
|
92
|
+
info(`GUI 服务器已启动: ${url}`);
|
|
93
|
+
// 自动打开浏览器
|
|
94
|
+
if (config.autoOpen) {
|
|
95
|
+
try {
|
|
96
|
+
await openBrowser(url, { usePuppeteer: config.usePuppeteer });
|
|
97
|
+
info('浏览器已打开');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
warn(`打开浏览器失败: ${error}`);
|
|
101
|
+
info(`请手动打开: ${url}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
warn(`GUI 服务器启动失败: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
info('初始化完成,网络请求将被捕获并发送到 DevTools');
|
|
110
|
+
// 检查 Network API 可用性
|
|
111
|
+
if (!hasNetworkAPI) {
|
|
112
|
+
warn('inspector.Network API 不可用');
|
|
113
|
+
warn('Network 面板功能需要:');
|
|
114
|
+
warn(' 1. Node.js 20.18.0+ 版本');
|
|
115
|
+
warn(' 2. --experimental-network-inspection 标志');
|
|
116
|
+
warn('示例:node --inspect --experimental-network-inspection your-script.js');
|
|
117
|
+
warn('');
|
|
118
|
+
warn('注意:Chrome DevTools Network 面板目前还不支持显示这些事件');
|
|
119
|
+
warn('请求数据仍会被捕获并可通过编程 API 访问');
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
info('inspector.Network API 可用,事件将广播到 DevTools');
|
|
123
|
+
warn('注意:Chrome DevTools Network 面板可能还不支持显示这些事件');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// 自动初始化
|
|
127
|
+
initialize().catch((error) => {
|
|
128
|
+
warn(`初始化失败: ${error}`);
|
|
129
|
+
});
|
|
130
|
+
// 导出供编程使用
|
|
131
|
+
export { getConfig, getRequestStore, getCDPBridge, getGUIServer, getEventBridge };
|
|
132
|
+
//# sourceMappingURL=register.js.map
|