@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.
Files changed (73) hide show
  1. package/BUILD.md +204 -0
  2. package/LICENSE +21 -0
  3. package/README.md +310 -0
  4. package/README.zh-CN.md +310 -0
  5. package/dist/esm/adapters/nextjs.js +123 -0
  6. package/dist/esm/adapters/nextjs.js.map +1 -0
  7. package/dist/esm/cdp/cdp-bridge.js +312 -0
  8. package/dist/esm/cdp/cdp-bridge.js.map +1 -0
  9. package/dist/esm/cli.js +203 -0
  10. package/dist/esm/cli.js.map +1 -0
  11. package/dist/esm/config.js +136 -0
  12. package/dist/esm/config.js.map +1 -0
  13. package/dist/esm/context/context-manager.js +126 -0
  14. package/dist/esm/context/context-manager.js.map +1 -0
  15. package/dist/esm/gui/browser-launcher.js +165 -0
  16. package/dist/esm/gui/browser-launcher.js.map +1 -0
  17. package/dist/esm/gui/event-bridge.js +192 -0
  18. package/dist/esm/gui/event-bridge.js.map +1 -0
  19. package/dist/esm/gui/port-utils.js +80 -0
  20. package/dist/esm/gui/port-utils.js.map +1 -0
  21. package/dist/esm/gui/server.js +227 -0
  22. package/dist/esm/gui/server.js.map +1 -0
  23. package/dist/esm/gui/websocket-hub.js +326 -0
  24. package/dist/esm/gui/websocket-hub.js.map +1 -0
  25. package/dist/esm/index.js +90 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/interceptors/http-patcher.js +203 -0
  28. package/dist/esm/interceptors/http-patcher.js.map +1 -0
  29. package/dist/esm/interceptors/undici-patcher.js +324 -0
  30. package/dist/esm/interceptors/undici-patcher.js.map +1 -0
  31. package/dist/esm/register.js +132 -0
  32. package/dist/esm/register.js.map +1 -0
  33. package/dist/esm/store/ring-buffer.js +236 -0
  34. package/dist/esm/store/ring-buffer.js.map +1 -0
  35. package/dist/esm/test-setup.js +7 -0
  36. package/dist/esm/test-setup.js.map +1 -0
  37. package/dist/gui/assets/index.css +1 -0
  38. package/dist/gui/assets/index.js +40 -0
  39. package/dist/gui/index.html +14 -0
  40. package/dist/types/adapters/nextjs.d.ts +80 -0
  41. package/dist/types/adapters/nextjs.d.ts.map +1 -0
  42. package/dist/types/cdp/cdp-bridge.d.ts +86 -0
  43. package/dist/types/cdp/cdp-bridge.d.ts.map +1 -0
  44. package/dist/types/cli.d.ts +8 -0
  45. package/dist/types/cli.d.ts.map +1 -0
  46. package/dist/types/config.d.ts +57 -0
  47. package/dist/types/config.d.ts.map +1 -0
  48. package/dist/types/context/context-manager.d.ts +96 -0
  49. package/dist/types/context/context-manager.d.ts.map +1 -0
  50. package/dist/types/gui/browser-launcher.d.ts +52 -0
  51. package/dist/types/gui/browser-launcher.d.ts.map +1 -0
  52. package/dist/types/gui/event-bridge.d.ts +36 -0
  53. package/dist/types/gui/event-bridge.d.ts.map +1 -0
  54. package/dist/types/gui/port-utils.d.ts +25 -0
  55. package/dist/types/gui/port-utils.d.ts.map +1 -0
  56. package/dist/types/gui/server.d.ts +50 -0
  57. package/dist/types/gui/server.d.ts.map +1 -0
  58. package/dist/types/gui/websocket-hub.d.ts +67 -0
  59. package/dist/types/gui/websocket-hub.d.ts.map +1 -0
  60. package/dist/types/index.d.ts +44 -0
  61. package/dist/types/index.d.ts.map +1 -0
  62. package/dist/types/interceptors/http-patcher.d.ts +32 -0
  63. package/dist/types/interceptors/http-patcher.d.ts.map +1 -0
  64. package/dist/types/interceptors/undici-patcher.d.ts +37 -0
  65. package/dist/types/interceptors/undici-patcher.d.ts.map +1 -0
  66. package/dist/types/register.d.ts +18 -0
  67. package/dist/types/register.d.ts.map +1 -0
  68. package/dist/types/store/ring-buffer.d.ts +148 -0
  69. package/dist/types/store/ring-buffer.d.ts.map +1 -0
  70. package/dist/types/test-setup.d.ts +7 -0
  71. package/dist/types/test-setup.d.ts.map +1 -0
  72. package/package.json +103 -0
  73. package/templates/instrumentation.ts +32 -0
@@ -0,0 +1,310 @@
1
+ <div align="center">
2
+
3
+ # 🔍 Node Network DevTools
4
+
5
+ **Node.js 网络请求监控工具,集成 Chrome DevTools 和内置 Web GUI**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/node-network-devtools.svg)](https://www.npmjs.com/package/node-network-devtools)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Node.js Version](https://img.shields.io/node/v/node-network-devtools.svg)](https://nodejs.org)
10
+
11
+ [English](./README.md) | [中文文档](#)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## ✨ 特性
18
+
19
+ - 🔍 **双栈拦截** - 同时支持 `http/https` 模块和 `undici/fetch` API
20
+ - 🎯 **零侵入** - 通过 `-r` 或 `--import` 自动注入,无需修改业务代码
21
+ - 📊 **DevTools 集成** - 在 Chrome DevTools Network 面板中查看所有请求
22
+ - 🖥️ **内置 Web GUI** - 类似 Chrome DevTools 的 Web 界面,实时更新
23
+ - 🔗 **请求追踪** - 基于 AsyncLocalStorage 的请求关联
24
+ - 🛡️ **安全脱敏** - 自动脱敏敏感头(Authorization、Cookie 等)
25
+ - ⚡ **Next.js 兼容** - 保留 `next.revalidate`、`next.tags` 等选项
26
+ - 📦 **TypeScript** - 完整的 TypeScript 支持和类型定义
27
+
28
+ ## 📸 截图
29
+
30
+ ### Web GUI 界面
31
+ ![Web GUI](https://via.placeholder.com/800x450?text=Web+GUI+Screenshot)
32
+
33
+ ### Chrome DevTools 集成
34
+ ![Chrome DevTools](https://via.placeholder.com/800x450?text=Chrome+DevTools+Screenshot)
35
+
36
+ ## 🚀 快速开始
37
+
38
+ ### 安装
39
+
40
+ ```bash
41
+ npm install node-network-devtools
42
+ # 或
43
+ pnpm add node-network-devtools
44
+ # 或
45
+ yarn add node-network-devtools
46
+ ```
47
+
48
+ ### 使用
49
+
50
+ #### 方式一:CLI(推荐)
51
+
52
+ ```bash
53
+ npx node-network-devtools your-script.js
54
+ # 或使用短别名
55
+ npx nnd your-script.js
56
+ ```
57
+
58
+ CLI 会自动添加 `--inspect` 标志并注入拦截器。
59
+
60
+ #### 方式二:使用 `-r` 标志
61
+
62
+ ```bash
63
+ node --inspect -r node-network-devtools/register your-script.js
64
+ ```
65
+
66
+ #### 方式三:编程方式
67
+
68
+ ```typescript
69
+ import { install } from 'node-network-devtools';
70
+
71
+ await install();
72
+
73
+ // 你的应用代码
74
+ import express from 'express';
75
+ const app = express();
76
+ // ...
77
+ ```
78
+
79
+ ### 查看请求
80
+
81
+ 启动应用后:
82
+
83
+ 1. **Web GUI**(默认):浏览器会自动打开显示 GUI 界面
84
+ 2. **Chrome DevTools**:打开 Chrome 浏览器,访问 `chrome://inspect`,然后点击 "Open dedicated DevTools for Node"
85
+
86
+ ## 🖥️ Web GUI
87
+
88
+ 内置的 Web GUI 提供类似 Chrome DevTools 的网络请求监控体验。
89
+
90
+ ### 功能特性
91
+
92
+ - 📋 **请求列表** - 实时显示所有网络请求
93
+ - 🔍 **搜索过滤** - 按 URL、方法、状态码和类型过滤
94
+ - 📝 **详情面板** - 查看请求头、请求体、响应体和时序信息
95
+ - 🎨 **主题切换** - 支持深色/浅色主题
96
+ - ⏸️ **暂停/恢复** - 暂停请求捕获以便分析
97
+ - 🔄 **实时更新** - 基于 WebSocket 的实时更新
98
+
99
+ ### GUI 配置
100
+
101
+ ```bash
102
+ # 指定 GUI 端口
103
+ NND_GUI_PORT=9230 npx nnd your-script.js
104
+
105
+ # 指定 WebSocket 端口
106
+ NND_WS_PORT=9231 npx nnd your-script.js
107
+
108
+ # 禁用 GUI
109
+ NND_GUI_ENABLED=false npx nnd your-script.js
110
+
111
+ # 禁用自动打开浏览器
112
+ NND_AUTO_OPEN=false npx nnd your-script.js
113
+ ```
114
+
115
+ ## 🔧 配置
116
+
117
+ ### 环境变量
118
+
119
+ #### 核心设置
120
+
121
+ | 变量名 | 说明 | 默认值 |
122
+ |--------|------|--------|
123
+ | `NND_MAX_REQUESTS` | 最大存储请求数 | 1000 |
124
+ | `NND_MAX_BODY_SIZE` | 最大 body 大小(字节) | 1048576 (1MB) |
125
+ | `NND_INTERCEPT_HTTP` | 拦截 http/https | true |
126
+ | `NND_INTERCEPT_UNDICI` | 拦截 undici/fetch | true |
127
+ | `NND_REDACT_HEADERS` | 要脱敏的头(逗号分隔) | authorization,cookie |
128
+ | `NND_AUTO_CONNECT` | 自动连接 CDP | true |
129
+
130
+ #### GUI 设置
131
+
132
+ | 变量名 | 说明 | 默认值 |
133
+ |--------|------|--------|
134
+ | `NND_GUI_ENABLED` | 启用 GUI 服务器 | true |
135
+ | `NND_GUI_PORT` | GUI 服务器端口 | 自动 |
136
+ | `NND_WS_PORT` | WebSocket 端口 | 自动 |
137
+ | `NND_AUTO_OPEN` | 自动打开浏览器 | true |
138
+
139
+ ### 编程配置
140
+
141
+ ```typescript
142
+ import { setConfig } from 'node-network-devtools';
143
+
144
+ setConfig({
145
+ maxRequests: 500,
146
+ maxBodySize: 512 * 1024,
147
+ redactHeaders: ['authorization', 'cookie', 'x-api-key'],
148
+ guiEnabled: true,
149
+ autoOpen: false,
150
+ });
151
+ ```
152
+
153
+ ## 🎯 框架集成
154
+
155
+ ### Next.js
156
+
157
+ 1. 复制 `templates/instrumentation.ts` 到项目根目录
158
+ 2. 在 `next.config.js` 中启用 instrumentation:
159
+
160
+ ```javascript
161
+ module.exports = {
162
+ experimental: {
163
+ instrumentationHook: true,
164
+ },
165
+ };
166
+ ```
167
+
168
+ 3. 使用 `--inspect` 启动:
169
+
170
+ ```bash
171
+ NODE_OPTIONS='--inspect' npm run dev
172
+ ```
173
+
174
+ 或在 `package.json` 中配置:
175
+
176
+ ```json
177
+ {
178
+ "scripts": {
179
+ "dev:debug": "NODE_OPTIONS='--inspect' next dev"
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Express
185
+
186
+ ```typescript
187
+ import express from 'express';
188
+ import { install } from 'node-network-devtools';
189
+
190
+ await install();
191
+
192
+ const app = express();
193
+ // 你的路由...
194
+ ```
195
+
196
+ ### 其他框架
197
+
198
+ 适用于任何 Node.js 框架!只需在应用代码之前安装拦截器即可。
199
+
200
+ ## 📚 API 参考
201
+
202
+ ### 主要导出
203
+
204
+ ```typescript
205
+ // 快速安装
206
+ import { install, startGUI, stopGUI } from 'node-network-devtools';
207
+
208
+ // 配置
209
+ import { getConfig, setConfig, resetConfig } from 'node-network-devtools';
210
+
211
+ // 请求存储
212
+ import { getRequestStore } from 'node-network-devtools';
213
+
214
+ // 上下文追踪
215
+ import {
216
+ runWithTrace,
217
+ getCurrentTraceId,
218
+ generateTraceId
219
+ } from 'node-network-devtools';
220
+
221
+ // 拦截器
222
+ import { HttpPatcher, UndiciPatcher } from 'node-network-devtools';
223
+
224
+ // CDP 桥接
225
+ import { getCDPBridge, isInspectorEnabled } from 'node-network-devtools';
226
+ ```
227
+
228
+ ### 请求追踪
229
+
230
+ 关联同一业务流程中的多个请求:
231
+
232
+ ```typescript
233
+ import { runWithTrace, getRequestStore } from 'node-network-devtools';
234
+
235
+ await runWithTrace('user-login', async () => {
236
+ // 这些请求会被关联到同一个 traceId
237
+ await fetch('https://api.example.com/auth');
238
+ await fetch('https://api.example.com/user');
239
+ });
240
+
241
+ // 查询关联的请求
242
+ const store = getRequestStore();
243
+ const requests = store.getByTraceId('user-login');
244
+ ```
245
+
246
+ ## 📖 示例
247
+
248
+ 查看 [examples](./examples) 目录获取更多使用示例:
249
+
250
+ - [basic-http](./examples/basic-http) - 基础 HTTP 请求监听
251
+ - [fetch-api](./examples/fetch-api) - Fetch API 监听
252
+ - [request-tracing](./examples/request-tracing) - 请求追踪
253
+ - [express-server](./examples/express-server) - Express 服务器示例
254
+ - [programmatic-api](./examples/programmatic-api) - 编程式 API 使用
255
+ - [nextjs-app](./examples/nextjs-app) - Next.js App Router 集成
256
+
257
+ ## 🔬 工作原理
258
+
259
+ 1. **HTTP 拦截**:使用 `@mswjs/interceptors` 拦截 http/https 模块请求
260
+ 2. **Undici 拦截**:使用 `Agent.compose()` 注册拦截器捕获 fetch 请求
261
+ 3. **CDP 桥接**:通过 `node:inspector` 模块连接到 V8 Inspector 并发送 Network 域事件
262
+ 4. **上下文传递**:使用 `AsyncLocalStorage` 在异步调用链中传递 TraceID
263
+ 5. **事件桥接**:将拦截的请求转发到 WebSocket 客户端以实现 GUI 实时更新
264
+
265
+ ## 🤝 贡献
266
+
267
+ 欢迎贡献!请阅读我们的[贡献指南](./CONTRIBUTING.md)了解详情。
268
+
269
+ ### 开发设置
270
+
271
+ ```bash
272
+ # 克隆仓库
273
+ git clone https://github.com/dong0926/node-network-devtools.git
274
+ cd node-network-devtools
275
+
276
+ # 安装依赖
277
+ pnpm install
278
+
279
+ # 构建项目
280
+ pnpm build
281
+
282
+ # 运行测试
283
+ pnpm test:all
284
+ ```
285
+
286
+ ## 📝 许可证
287
+
288
+ MIT © [ddddd](https://github.com/dong0926)
289
+
290
+ ## 🙏 致谢
291
+
292
+ - [@mswjs/interceptors](https://github.com/mswjs/interceptors) - HTTP 请求拦截
293
+ - [undici](https://github.com/nodejs/undici) - HTTP/1.1 客户端
294
+ - [ws](https://github.com/websockets/ws) - WebSocket 实现
295
+
296
+ ## 📮 支持
297
+
298
+ - 🐛 [报告问题](https://github.com/dong0926/node-network-devtools/issues)
299
+ - 💬 [讨论](https://github.com/dong0926/node-network-devtools/discussions)
300
+ - 📧 邮箱:your.email@example.com
301
+
302
+ ---
303
+
304
+ <div align="center">
305
+
306
+ **如果这个项目对你有帮助,请给它一个 ⭐️!**
307
+
308
+ 用 ❤️ 制作 by [ddddd](https://github.com/dong0926)
309
+
310
+ </div>
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Next.js 框架适配器
3
+ *
4
+ * 确保与 Next.js 的 Data Cache 和 Request Memoization 兼容
5
+ * 保留 next.revalidate、next.tags 等选项
6
+ */
7
+ import { AsyncLocalStorage } from 'node:async_hooks';
8
+ // 用于存储当前路由信息的 AsyncLocalStorage
9
+ const routeStorage = new AsyncLocalStorage();
10
+ /**
11
+ * 检测是否在 Next.js 环境中
12
+ */
13
+ export function isNextJsEnvironment() {
14
+ // 检查 Next.js 特有的环境变量
15
+ return !!(process.env.NEXT_RUNTIME ||
16
+ process.env.__NEXT_PRIVATE_PREBUNDLED_REACT ||
17
+ process.env.NEXT_DEPLOYMENT_ID);
18
+ }
19
+ /**
20
+ * 获取当前路由信息
21
+ */
22
+ export function getCurrentRoute() {
23
+ return routeStorage.getStore();
24
+ }
25
+ /**
26
+ * 在指定路由上下文中运行函数
27
+ */
28
+ export function runWithRoute(route, type, fn) {
29
+ return routeStorage.run({ route, type }, fn);
30
+ }
31
+ /**
32
+ * 异步版本的 runWithRoute
33
+ */
34
+ export async function runWithRouteAsync(route, type, fn) {
35
+ return routeStorage.run({ route, type }, fn);
36
+ }
37
+ /**
38
+ * 提取 Next.js fetch 选项
39
+ */
40
+ export function extractNextJsOptions(init) {
41
+ if (!init)
42
+ return undefined;
43
+ const options = {};
44
+ // 提取 next 选项
45
+ if ('next' in init && init.next) {
46
+ const nextOpts = init.next;
47
+ options.next = {
48
+ revalidate: nextOpts.revalidate,
49
+ tags: nextOpts.tags,
50
+ };
51
+ }
52
+ // 提取 cache 选项
53
+ if ('cache' in init && init.cache) {
54
+ options.cache = init.cache;
55
+ }
56
+ return Object.keys(options).length > 0 ? options : undefined;
57
+ }
58
+ /**
59
+ * 从请求中提取 Next.js 元数据
60
+ */
61
+ export function extractNextJsMetadata(init, responseHeaders) {
62
+ const metadata = {};
63
+ // 获取当前路由
64
+ const routeInfo = getCurrentRoute();
65
+ if (routeInfo) {
66
+ metadata.route = routeInfo.route;
67
+ metadata.requestType = routeInfo.type;
68
+ }
69
+ // 提取 fetch 选项
70
+ const nextOptions = extractNextJsOptions(init);
71
+ if (nextOptions?.next) {
72
+ metadata.revalidate = nextOptions.next.revalidate;
73
+ metadata.tags = nextOptions.next.tags;
74
+ }
75
+ // 从响应头提取缓存状态
76
+ if (responseHeaders) {
77
+ const cacheStatus = getHeader(responseHeaders, 'x-nextjs-cache');
78
+ if (cacheStatus) {
79
+ metadata.cacheStatus = cacheStatus;
80
+ }
81
+ }
82
+ return metadata;
83
+ }
84
+ /**
85
+ * 辅助函数:获取响应头
86
+ */
87
+ function getHeader(headers, name) {
88
+ if (headers instanceof Headers) {
89
+ return headers.get(name) ?? undefined;
90
+ }
91
+ return headers[name] || headers[name.toLowerCase()];
92
+ }
93
+ /**
94
+ * 创建 Next.js instrumentation 配置
95
+ */
96
+ export function createInstrumentationConfig() {
97
+ return `// instrumentation.ts
98
+ // 将此文件放在 Next.js 项目根目录
99
+
100
+ export async function register() {
101
+ // 仅在服务端运行
102
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
103
+ // 动态导入以避免客户端打包
104
+ const { install } = await import('node-network-devtools');
105
+ await install();
106
+ console.log('[node-network-devtools] 已在 Next.js 服务端初始化');
107
+ }
108
+ }
109
+ `;
110
+ }
111
+ /**
112
+ * Next.js 适配器对象
113
+ */
114
+ export const NextJsAdapter = {
115
+ isNextJsEnvironment,
116
+ getCurrentRoute,
117
+ runWithRoute,
118
+ runWithRouteAsync,
119
+ extractNextJsOptions,
120
+ extractNextJsMetadata,
121
+ createInstrumentationConfig,
122
+ };
123
+ //# sourceMappingURL=nextjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../../src/adapters/nextjs.ts"],"names":[],"mappings":"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"}