peep-proxy 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/dist/app.d.ts +9 -0
- package/dist/app.js +162 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +98 -0
- package/dist/components/BorderedBox.d.ts +10 -0
- package/dist/components/BorderedBox.js +13 -0
- package/dist/components/DetailPanel.d.ts +13 -0
- package/dist/components/DetailPanel.js +8 -0
- package/dist/components/DetailView.d.ts +12 -0
- package/dist/components/DetailView.js +151 -0
- package/dist/components/DomainSidebar.d.ts +12 -0
- package/dist/components/DomainSidebar.js +36 -0
- package/dist/components/RequestList.d.ts +22 -0
- package/dist/components/RequestList.js +30 -0
- package/dist/components/RequestRow.d.ts +15 -0
- package/dist/components/RequestRow.js +84 -0
- package/dist/components/SortModal.d.ts +8 -0
- package/dist/components/SortModal.js +56 -0
- package/dist/components/SpinnerContext.d.ts +5 -0
- package/dist/components/SpinnerContext.js +20 -0
- package/dist/components/StatusBar.d.ts +11 -0
- package/dist/components/StatusBar.js +21 -0
- package/dist/hooks/useActivePanel.d.ts +11 -0
- package/dist/hooks/useActivePanel.js +36 -0
- package/dist/hooks/useDetailScroll.d.ts +11 -0
- package/dist/hooks/useDetailScroll.js +59 -0
- package/dist/hooks/useDetailTabs.d.ts +14 -0
- package/dist/hooks/useDetailTabs.js +50 -0
- package/dist/hooks/useDomainFilter.d.ts +30 -0
- package/dist/hooks/useDomainFilter.js +103 -0
- package/dist/hooks/useListNavigation.d.ts +12 -0
- package/dist/hooks/useListNavigation.js +105 -0
- package/dist/hooks/useSorting.d.ts +20 -0
- package/dist/hooks/useSorting.js +71 -0
- package/dist/hooks/useTerminalDimensions.d.ts +6 -0
- package/dist/hooks/useTerminalDimensions.js +25 -0
- package/dist/hooks/useTrafficEntries.d.ts +2 -0
- package/dist/hooks/useTrafficEntries.js +16 -0
- package/dist/proxy/ca.d.ts +4 -0
- package/dist/proxy/ca.js +72 -0
- package/dist/proxy/cert-trust.d.ts +20 -0
- package/dist/proxy/cert-trust.js +181 -0
- package/dist/proxy/index.d.ts +3 -0
- package/dist/proxy/index.js +2 -0
- package/dist/proxy/proxy-server.d.ts +9 -0
- package/dist/proxy/proxy-server.js +262 -0
- package/dist/proxy/system-proxy.d.ts +2 -0
- package/dist/proxy/system-proxy.js +64 -0
- package/dist/proxy/types.d.ts +45 -0
- package/dist/proxy/types.js +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.js +1 -0
- package/dist/store/traffic-store.d.ts +14 -0
- package/dist/store/traffic-store.js +87 -0
- package/dist/store/types.d.ts +14 -0
- package/dist/store/types.js +1 -0
- package/dist/theme.d.ts +1 -0
- package/dist/theme.js +1 -0
- package/dist/utils/contentType.d.ts +7 -0
- package/dist/utils/contentType.js +46 -0
- package/dist/utils/copyToClipboard.d.ts +1 -0
- package/dist/utils/copyToClipboard.js +21 -0
- package/dist/utils/decompress.d.ts +2 -0
- package/dist/utils/decompress.js +25 -0
- package/dist/utils/formatBytes.d.ts +1 -0
- package/dist/utils/formatBytes.js +14 -0
- package/dist/utils/getTabText.d.ts +3 -0
- package/dist/utils/getTabText.js +96 -0
- package/dist/utils/highlightBody.d.ts +3 -0
- package/dist/utils/highlightBody.js +43 -0
- package/package.json +57 -0
- package/readme.md +73 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
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");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
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");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _ProxyServer_instances, _ProxyServer_config, _ProxyServer_server, _ProxyServer_emitter, _ProxyServer_certCache, _ProxyServer_sockets, _ProxyServer_tunnelSockets, _ProxyServer_mitmServers, _ProxyServer_handleRequest, _ProxyServer_handleConnect, _ProxyServer_handleTunnel, _ProxyServer_handleMitm;
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import * as http from "node:http";
|
|
15
|
+
import * as https from "node:https";
|
|
16
|
+
import * as net from "node:net";
|
|
17
|
+
import * as tls from "node:tls";
|
|
18
|
+
import { EventEmitter } from "node:events";
|
|
19
|
+
import { generateHostCert } from "./ca.js";
|
|
20
|
+
export class ProxyServer {
|
|
21
|
+
constructor(config) {
|
|
22
|
+
_ProxyServer_instances.add(this);
|
|
23
|
+
_ProxyServer_config.set(this, void 0);
|
|
24
|
+
_ProxyServer_server.set(this, void 0);
|
|
25
|
+
_ProxyServer_emitter.set(this, void 0);
|
|
26
|
+
_ProxyServer_certCache.set(this, new Map());
|
|
27
|
+
_ProxyServer_sockets.set(this, new Set());
|
|
28
|
+
_ProxyServer_tunnelSockets.set(this, new Set());
|
|
29
|
+
_ProxyServer_mitmServers.set(this, new Set());
|
|
30
|
+
_ProxyServer_handleRequest.set(this, (clientReq, clientRes) => {
|
|
31
|
+
const id = randomUUID();
|
|
32
|
+
const timestamp = Date.now();
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = new URL(clientReq.url ?? "");
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
clientRes.writeHead(400, { "Content-Type": "text/plain" });
|
|
39
|
+
clientRes.end("Bad Request: invalid URL");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const reqChunks = [];
|
|
43
|
+
clientReq.on("data", (chunk) => {
|
|
44
|
+
reqChunks.push(chunk);
|
|
45
|
+
});
|
|
46
|
+
clientReq.on("end", () => {
|
|
47
|
+
const reqBody = Buffer.concat(reqChunks);
|
|
48
|
+
const requestEvent = {
|
|
49
|
+
id,
|
|
50
|
+
method: clientReq.method ?? "GET",
|
|
51
|
+
url: clientReq.url ?? "",
|
|
52
|
+
host: parsed.hostname,
|
|
53
|
+
path: parsed.pathname + parsed.search,
|
|
54
|
+
headers: clientReq.headers,
|
|
55
|
+
body: reqBody,
|
|
56
|
+
timestamp,
|
|
57
|
+
};
|
|
58
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("request", requestEvent);
|
|
59
|
+
const upstreamOptions = {
|
|
60
|
+
hostname: parsed.hostname,
|
|
61
|
+
port: parsed.port || 80,
|
|
62
|
+
path: parsed.pathname + parsed.search,
|
|
63
|
+
method: clientReq.method,
|
|
64
|
+
headers: clientReq.headers,
|
|
65
|
+
};
|
|
66
|
+
const upstreamReq = http.request(upstreamOptions, (upstreamRes) => {
|
|
67
|
+
const chunks = [];
|
|
68
|
+
upstreamRes.on("data", (chunk) => {
|
|
69
|
+
chunks.push(chunk);
|
|
70
|
+
});
|
|
71
|
+
upstreamRes.on("end", () => {
|
|
72
|
+
const body = Buffer.concat(chunks);
|
|
73
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("response", {
|
|
74
|
+
id,
|
|
75
|
+
statusCode: upstreamRes.statusCode ?? 0,
|
|
76
|
+
headers: upstreamRes.headers,
|
|
77
|
+
body,
|
|
78
|
+
duration: Date.now() - timestamp,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
clientRes.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.headers);
|
|
82
|
+
upstreamRes.pipe(clientRes);
|
|
83
|
+
});
|
|
84
|
+
upstreamReq.on("error", (error) => {
|
|
85
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("error", { id, error, phase: "request" });
|
|
86
|
+
if (!clientRes.headersSent) {
|
|
87
|
+
clientRes.writeHead(502, { "Content-Type": "text/plain" });
|
|
88
|
+
}
|
|
89
|
+
clientRes.end("Bad Gateway");
|
|
90
|
+
});
|
|
91
|
+
upstreamReq.end(reqBody);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
_ProxyServer_handleConnect.set(this, (req, clientSocket, head) => {
|
|
95
|
+
const [host, portStr] = (req.url ?? "").split(":");
|
|
96
|
+
const port = Number.parseInt(portStr ?? "443", 10);
|
|
97
|
+
const hostname = host ?? "";
|
|
98
|
+
if (__classPrivateFieldGet(this, _ProxyServer_config, "f").ca) {
|
|
99
|
+
__classPrivateFieldGet(this, _ProxyServer_instances, "m", _ProxyServer_handleMitm).call(this, hostname, port, clientSocket, head);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
__classPrivateFieldGet(this, _ProxyServer_instances, "m", _ProxyServer_handleTunnel).call(this, hostname, port, clientSocket, head);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
__classPrivateFieldSet(this, _ProxyServer_config, config, "f");
|
|
106
|
+
__classPrivateFieldSet(this, _ProxyServer_emitter, new EventEmitter(), "f");
|
|
107
|
+
__classPrivateFieldSet(this, _ProxyServer_server, http.createServer(__classPrivateFieldGet(this, _ProxyServer_handleRequest, "f")), "f");
|
|
108
|
+
__classPrivateFieldGet(this, _ProxyServer_server, "f").on("connect", __classPrivateFieldGet(this, _ProxyServer_handleConnect, "f"));
|
|
109
|
+
__classPrivateFieldGet(this, _ProxyServer_server, "f").on("connection", (socket) => {
|
|
110
|
+
__classPrivateFieldGet(this, _ProxyServer_sockets, "f").add(socket);
|
|
111
|
+
socket.once("close", () => __classPrivateFieldGet(this, _ProxyServer_sockets, "f").delete(socket));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
start() {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
__classPrivateFieldGet(this, _ProxyServer_server, "f").once("error", reject);
|
|
117
|
+
__classPrivateFieldGet(this, _ProxyServer_server, "f").listen(__classPrivateFieldGet(this, _ProxyServer_config, "f").port, __classPrivateFieldGet(this, _ProxyServer_config, "f").hostname, () => {
|
|
118
|
+
__classPrivateFieldGet(this, _ProxyServer_server, "f").removeListener("error", reject);
|
|
119
|
+
resolve();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
stop() {
|
|
124
|
+
for (const socket of __classPrivateFieldGet(this, _ProxyServer_tunnelSockets, "f")) {
|
|
125
|
+
socket.destroy();
|
|
126
|
+
}
|
|
127
|
+
__classPrivateFieldGet(this, _ProxyServer_tunnelSockets, "f").clear();
|
|
128
|
+
for (const server of __classPrivateFieldGet(this, _ProxyServer_mitmServers, "f")) {
|
|
129
|
+
server.closeAllConnections();
|
|
130
|
+
server.close();
|
|
131
|
+
}
|
|
132
|
+
__classPrivateFieldGet(this, _ProxyServer_mitmServers, "f").clear();
|
|
133
|
+
for (const socket of __classPrivateFieldGet(this, _ProxyServer_sockets, "f")) {
|
|
134
|
+
socket.destroy();
|
|
135
|
+
}
|
|
136
|
+
__classPrivateFieldGet(this, _ProxyServer_sockets, "f").clear();
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
__classPrivateFieldGet(this, _ProxyServer_server, "f").close((error) => {
|
|
139
|
+
if (error) {
|
|
140
|
+
reject(error);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
resolve();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
on(event, listener) {
|
|
149
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").on(event, listener);
|
|
150
|
+
}
|
|
151
|
+
off(event, listener) {
|
|
152
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").off(event, listener);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
_ProxyServer_config = new WeakMap(), _ProxyServer_server = new WeakMap(), _ProxyServer_emitter = new WeakMap(), _ProxyServer_certCache = new WeakMap(), _ProxyServer_sockets = new WeakMap(), _ProxyServer_tunnelSockets = new WeakMap(), _ProxyServer_mitmServers = new WeakMap(), _ProxyServer_handleRequest = new WeakMap(), _ProxyServer_handleConnect = new WeakMap(), _ProxyServer_instances = new WeakSet(), _ProxyServer_handleTunnel = function _ProxyServer_handleTunnel(host, port, clientSocket, head) {
|
|
156
|
+
const id = randomUUID();
|
|
157
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("connect", {
|
|
158
|
+
id,
|
|
159
|
+
host,
|
|
160
|
+
port,
|
|
161
|
+
timestamp: Date.now(),
|
|
162
|
+
});
|
|
163
|
+
const upstreamSocket = net.connect(port, host, () => {
|
|
164
|
+
clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
165
|
+
if (head.length > 0) {
|
|
166
|
+
upstreamSocket.write(head);
|
|
167
|
+
}
|
|
168
|
+
upstreamSocket.pipe(clientSocket);
|
|
169
|
+
clientSocket.pipe(upstreamSocket);
|
|
170
|
+
});
|
|
171
|
+
__classPrivateFieldGet(this, _ProxyServer_tunnelSockets, "f").add(upstreamSocket);
|
|
172
|
+
upstreamSocket.once("close", () => __classPrivateFieldGet(this, _ProxyServer_tunnelSockets, "f").delete(upstreamSocket));
|
|
173
|
+
upstreamSocket.on("error", (error) => {
|
|
174
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("error", { id, error, phase: "connect" });
|
|
175
|
+
clientSocket.end();
|
|
176
|
+
});
|
|
177
|
+
clientSocket.on("error", () => {
|
|
178
|
+
upstreamSocket.end();
|
|
179
|
+
});
|
|
180
|
+
}, _ProxyServer_handleMitm = function _ProxyServer_handleMitm(host, port, clientSocket, _head) {
|
|
181
|
+
const ca = __classPrivateFieldGet(this, _ProxyServer_config, "f").ca;
|
|
182
|
+
let hostCert = __classPrivateFieldGet(this, _ProxyServer_certCache, "f").get(host);
|
|
183
|
+
if (!hostCert) {
|
|
184
|
+
hostCert = generateHostCert(host, ca);
|
|
185
|
+
__classPrivateFieldGet(this, _ProxyServer_certCache, "f").set(host, hostCert);
|
|
186
|
+
}
|
|
187
|
+
const secureContext = tls.createSecureContext({
|
|
188
|
+
cert: hostCert.certPem,
|
|
189
|
+
key: hostCert.keyPem,
|
|
190
|
+
});
|
|
191
|
+
clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
192
|
+
const tlsSocket = new tls.TLSSocket(clientSocket, {
|
|
193
|
+
isServer: true,
|
|
194
|
+
secureContext,
|
|
195
|
+
});
|
|
196
|
+
const mitmServer = http.createServer((clientReq, clientRes) => {
|
|
197
|
+
const id = randomUUID();
|
|
198
|
+
const timestamp = Date.now();
|
|
199
|
+
const url = `https://${host}${clientReq.url ?? "/"}`;
|
|
200
|
+
const pathAndSearch = clientReq.url ?? "/";
|
|
201
|
+
const reqChunks = [];
|
|
202
|
+
clientReq.on("data", (chunk) => {
|
|
203
|
+
reqChunks.push(chunk);
|
|
204
|
+
});
|
|
205
|
+
clientReq.on("end", () => {
|
|
206
|
+
const reqBody = Buffer.concat(reqChunks);
|
|
207
|
+
const requestEvent = {
|
|
208
|
+
id,
|
|
209
|
+
method: clientReq.method ?? "GET",
|
|
210
|
+
url,
|
|
211
|
+
host,
|
|
212
|
+
path: pathAndSearch,
|
|
213
|
+
headers: clientReq.headers,
|
|
214
|
+
body: reqBody,
|
|
215
|
+
timestamp,
|
|
216
|
+
};
|
|
217
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("request", requestEvent);
|
|
218
|
+
const upstreamOptions = {
|
|
219
|
+
hostname: host,
|
|
220
|
+
port,
|
|
221
|
+
path: pathAndSearch,
|
|
222
|
+
method: clientReq.method,
|
|
223
|
+
headers: { ...clientReq.headers, host },
|
|
224
|
+
};
|
|
225
|
+
const upstreamReq = https.request(upstreamOptions, (upstreamRes) => {
|
|
226
|
+
const chunks = [];
|
|
227
|
+
upstreamRes.on("data", (chunk) => {
|
|
228
|
+
chunks.push(chunk);
|
|
229
|
+
});
|
|
230
|
+
upstreamRes.on("end", () => {
|
|
231
|
+
const body = Buffer.concat(chunks);
|
|
232
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("response", {
|
|
233
|
+
id,
|
|
234
|
+
statusCode: upstreamRes.statusCode ?? 0,
|
|
235
|
+
headers: upstreamRes.headers,
|
|
236
|
+
body,
|
|
237
|
+
duration: Date.now() - timestamp,
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
clientRes.writeHead(upstreamRes.statusCode ?? 502, upstreamRes.headers);
|
|
241
|
+
upstreamRes.pipe(clientRes);
|
|
242
|
+
});
|
|
243
|
+
upstreamReq.on("error", (error) => {
|
|
244
|
+
__classPrivateFieldGet(this, _ProxyServer_emitter, "f").emit("error", { id, error, phase: "request" });
|
|
245
|
+
if (!clientRes.headersSent) {
|
|
246
|
+
clientRes.writeHead(502, { "Content-Type": "text/plain" });
|
|
247
|
+
}
|
|
248
|
+
clientRes.end("Bad Gateway");
|
|
249
|
+
});
|
|
250
|
+
upstreamReq.end(reqBody);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
__classPrivateFieldGet(this, _ProxyServer_mitmServers, "f").add(mitmServer);
|
|
254
|
+
mitmServer.emit("connection", tlsSocket);
|
|
255
|
+
const cleanup = () => {
|
|
256
|
+
__classPrivateFieldGet(this, _ProxyServer_mitmServers, "f").delete(mitmServer);
|
|
257
|
+
mitmServer.close();
|
|
258
|
+
};
|
|
259
|
+
tlsSocket.on("close", cleanup);
|
|
260
|
+
tlsSocket.on("error", cleanup);
|
|
261
|
+
clientSocket.on("error", cleanup);
|
|
262
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
function getActiveService() {
|
|
4
|
+
if (platform() !== "darwin")
|
|
5
|
+
return null;
|
|
6
|
+
try {
|
|
7
|
+
const route = execFileSync("route", ["-n", "get", "default"], {
|
|
8
|
+
stdio: "pipe",
|
|
9
|
+
}).toString();
|
|
10
|
+
const ifaceMatch = route.match(/interface:\s*(\S+)/);
|
|
11
|
+
if (!ifaceMatch)
|
|
12
|
+
return null;
|
|
13
|
+
const iface = ifaceMatch[1];
|
|
14
|
+
const ports = execFileSync("networksetup", ["-listallhardwareports"], {
|
|
15
|
+
stdio: "pipe",
|
|
16
|
+
}).toString();
|
|
17
|
+
const lines = ports.split("\n");
|
|
18
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19
|
+
if (lines[i]?.includes(`Device: ${iface}`)) {
|
|
20
|
+
const serviceMatch = lines[i - 1]?.match(/Hardware Port:\s*(.+)/);
|
|
21
|
+
if (serviceMatch?.[1])
|
|
22
|
+
return serviceMatch[1];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function enableSystemProxy(port) {
|
|
30
|
+
const service = getActiveService();
|
|
31
|
+
if (!service)
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
const addr = "127.0.0.1";
|
|
35
|
+
const p = String(port);
|
|
36
|
+
execFileSync("networksetup", ["-setwebproxy", service, addr, p], {
|
|
37
|
+
stdio: "pipe",
|
|
38
|
+
});
|
|
39
|
+
execFileSync("networksetup", ["-setsecurewebproxy", service, addr, p], {
|
|
40
|
+
stdio: "pipe",
|
|
41
|
+
});
|
|
42
|
+
execFileSync("networksetup", ["-setwebproxystate", service, "on"], {
|
|
43
|
+
stdio: "pipe",
|
|
44
|
+
});
|
|
45
|
+
execFileSync("networksetup", ["-setsecurewebproxystate", service, "on"], {
|
|
46
|
+
stdio: "pipe",
|
|
47
|
+
});
|
|
48
|
+
return service;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function disableSystemProxy(service) {
|
|
55
|
+
try {
|
|
56
|
+
execFileSync("networksetup", ["-setwebproxystate", service, "off"], {
|
|
57
|
+
stdio: "pipe",
|
|
58
|
+
});
|
|
59
|
+
execFileSync("networksetup", ["-setsecurewebproxystate", service, "off"], {
|
|
60
|
+
stdio: "pipe",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch { }
|
|
64
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from "node:http";
|
|
2
|
+
export type CaConfig = {
|
|
3
|
+
certPem: string;
|
|
4
|
+
keyPem: string;
|
|
5
|
+
};
|
|
6
|
+
export type ProxyConfig = {
|
|
7
|
+
port: number;
|
|
8
|
+
hostname?: string;
|
|
9
|
+
ca?: CaConfig;
|
|
10
|
+
};
|
|
11
|
+
export type RequestId = string;
|
|
12
|
+
export type ProxyRequestEvent = {
|
|
13
|
+
id: RequestId;
|
|
14
|
+
method: string;
|
|
15
|
+
url: string;
|
|
16
|
+
host: string;
|
|
17
|
+
path: string;
|
|
18
|
+
headers: IncomingHttpHeaders;
|
|
19
|
+
body: Buffer;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
};
|
|
22
|
+
export type ProxyResponseEvent = {
|
|
23
|
+
id: RequestId;
|
|
24
|
+
statusCode: number;
|
|
25
|
+
headers: IncomingHttpHeaders;
|
|
26
|
+
body: Buffer;
|
|
27
|
+
duration: number;
|
|
28
|
+
};
|
|
29
|
+
export type ProxyConnectEvent = {
|
|
30
|
+
id: RequestId;
|
|
31
|
+
host: string;
|
|
32
|
+
port: number;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
};
|
|
35
|
+
export type ProxyErrorEvent = {
|
|
36
|
+
id?: RequestId;
|
|
37
|
+
error: Error;
|
|
38
|
+
phase: "request" | "response" | "connect";
|
|
39
|
+
};
|
|
40
|
+
export type ProxyEventMap = {
|
|
41
|
+
request: [event: ProxyRequestEvent];
|
|
42
|
+
response: [event: ProxyResponseEvent];
|
|
43
|
+
connect: [event: ProxyConnectEvent];
|
|
44
|
+
error: [event: ProxyErrorEvent];
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TrafficStore } from "./traffic-store.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProxyServer } from "../proxy/proxy-server.js";
|
|
2
|
+
import type { RequestId } from "../proxy/types.js";
|
|
3
|
+
import type { TrafficEntry, TrafficStoreEventMap } from "./types.js";
|
|
4
|
+
export declare class TrafficStore {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(proxy: ProxyServer);
|
|
7
|
+
get entries(): TrafficEntry[];
|
|
8
|
+
get size(): number;
|
|
9
|
+
get(id: RequestId): TrafficEntry | undefined;
|
|
10
|
+
clear(): void;
|
|
11
|
+
destroy(): void;
|
|
12
|
+
on<K extends keyof TrafficStoreEventMap>(event: K, listener: (...args: TrafficStoreEventMap[K]) => void): void;
|
|
13
|
+
off<K extends keyof TrafficStoreEventMap>(event: K, listener: (...args: TrafficStoreEventMap[K]) => void): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
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");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
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");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _TrafficStore_entries, _TrafficStore_order, _TrafficStore_emitter, _TrafficStore_proxy, _TrafficStore_nextSeq, _TrafficStore_onRequest, _TrafficStore_onResponse, _TrafficStore_onError;
|
|
13
|
+
import { EventEmitter } from "node:events";
|
|
14
|
+
export class TrafficStore {
|
|
15
|
+
constructor(proxy) {
|
|
16
|
+
_TrafficStore_entries.set(this, new Map());
|
|
17
|
+
_TrafficStore_order.set(this, []);
|
|
18
|
+
_TrafficStore_emitter.set(this, void 0);
|
|
19
|
+
_TrafficStore_proxy.set(this, void 0);
|
|
20
|
+
_TrafficStore_nextSeq.set(this, 1);
|
|
21
|
+
_TrafficStore_onRequest.set(this, (event) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
const entry = {
|
|
24
|
+
id: event.id,
|
|
25
|
+
seq: (__classPrivateFieldSet(this, _TrafficStore_nextSeq, (_b = __classPrivateFieldGet(this, _TrafficStore_nextSeq, "f"), _a = _b++, _b), "f"), _a),
|
|
26
|
+
request: event,
|
|
27
|
+
state: "pending",
|
|
28
|
+
};
|
|
29
|
+
__classPrivateFieldGet(this, _TrafficStore_entries, "f").set(event.id, entry);
|
|
30
|
+
__classPrivateFieldGet(this, _TrafficStore_order, "f").push(event.id);
|
|
31
|
+
__classPrivateFieldGet(this, _TrafficStore_emitter, "f").emit("add", entry);
|
|
32
|
+
});
|
|
33
|
+
_TrafficStore_onResponse.set(this, (event) => {
|
|
34
|
+
const entry = __classPrivateFieldGet(this, _TrafficStore_entries, "f").get(event.id);
|
|
35
|
+
if (!entry)
|
|
36
|
+
return;
|
|
37
|
+
entry.response = event;
|
|
38
|
+
entry.state = "complete";
|
|
39
|
+
__classPrivateFieldGet(this, _TrafficStore_emitter, "f").emit("update", entry);
|
|
40
|
+
});
|
|
41
|
+
_TrafficStore_onError.set(this, (event) => {
|
|
42
|
+
if (!event.id)
|
|
43
|
+
return;
|
|
44
|
+
const entry = __classPrivateFieldGet(this, _TrafficStore_entries, "f").get(event.id);
|
|
45
|
+
if (!entry)
|
|
46
|
+
return;
|
|
47
|
+
entry.state = "error";
|
|
48
|
+
entry.error = event.error;
|
|
49
|
+
__classPrivateFieldGet(this, _TrafficStore_emitter, "f").emit("update", entry);
|
|
50
|
+
});
|
|
51
|
+
__classPrivateFieldSet(this, _TrafficStore_proxy, proxy, "f");
|
|
52
|
+
__classPrivateFieldSet(this, _TrafficStore_emitter, new EventEmitter(), "f");
|
|
53
|
+
__classPrivateFieldGet(this, _TrafficStore_proxy, "f").on("request", __classPrivateFieldGet(this, _TrafficStore_onRequest, "f"));
|
|
54
|
+
__classPrivateFieldGet(this, _TrafficStore_proxy, "f").on("response", __classPrivateFieldGet(this, _TrafficStore_onResponse, "f"));
|
|
55
|
+
__classPrivateFieldGet(this, _TrafficStore_proxy, "f").on("error", __classPrivateFieldGet(this, _TrafficStore_onError, "f"));
|
|
56
|
+
}
|
|
57
|
+
get entries() {
|
|
58
|
+
return __classPrivateFieldGet(this, _TrafficStore_order, "f").flatMap((id) => {
|
|
59
|
+
const entry = __classPrivateFieldGet(this, _TrafficStore_entries, "f").get(id);
|
|
60
|
+
return entry ? [entry] : [];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
get size() {
|
|
64
|
+
return __classPrivateFieldGet(this, _TrafficStore_entries, "f").size;
|
|
65
|
+
}
|
|
66
|
+
get(id) {
|
|
67
|
+
return __classPrivateFieldGet(this, _TrafficStore_entries, "f").get(id);
|
|
68
|
+
}
|
|
69
|
+
clear() {
|
|
70
|
+
__classPrivateFieldGet(this, _TrafficStore_entries, "f").clear();
|
|
71
|
+
__classPrivateFieldGet(this, _TrafficStore_order, "f").length = 0;
|
|
72
|
+
__classPrivateFieldSet(this, _TrafficStore_nextSeq, 1, "f");
|
|
73
|
+
__classPrivateFieldGet(this, _TrafficStore_emitter, "f").emit("clear");
|
|
74
|
+
}
|
|
75
|
+
destroy() {
|
|
76
|
+
__classPrivateFieldGet(this, _TrafficStore_proxy, "f").off("request", __classPrivateFieldGet(this, _TrafficStore_onRequest, "f"));
|
|
77
|
+
__classPrivateFieldGet(this, _TrafficStore_proxy, "f").off("response", __classPrivateFieldGet(this, _TrafficStore_onResponse, "f"));
|
|
78
|
+
__classPrivateFieldGet(this, _TrafficStore_proxy, "f").off("error", __classPrivateFieldGet(this, _TrafficStore_onError, "f"));
|
|
79
|
+
}
|
|
80
|
+
on(event, listener) {
|
|
81
|
+
__classPrivateFieldGet(this, _TrafficStore_emitter, "f").on(event, listener);
|
|
82
|
+
}
|
|
83
|
+
off(event, listener) {
|
|
84
|
+
__classPrivateFieldGet(this, _TrafficStore_emitter, "f").off(event, listener);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
_TrafficStore_entries = new WeakMap(), _TrafficStore_order = new WeakMap(), _TrafficStore_emitter = new WeakMap(), _TrafficStore_proxy = new WeakMap(), _TrafficStore_nextSeq = new WeakMap(), _TrafficStore_onRequest = new WeakMap(), _TrafficStore_onResponse = new WeakMap(), _TrafficStore_onError = new WeakMap();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProxyRequestEvent, ProxyResponseEvent, RequestId } from "../proxy/types.js";
|
|
2
|
+
export type TrafficEntry = {
|
|
3
|
+
id: RequestId;
|
|
4
|
+
seq: number;
|
|
5
|
+
request: ProxyRequestEvent;
|
|
6
|
+
response?: ProxyResponseEvent;
|
|
7
|
+
state: "pending" | "complete" | "error";
|
|
8
|
+
error?: Error;
|
|
9
|
+
};
|
|
10
|
+
export type TrafficStoreEventMap = {
|
|
11
|
+
add: [entry: TrafficEntry];
|
|
12
|
+
update: [entry: TrafficEntry];
|
|
13
|
+
clear: [];
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PRIMARY_COLOR = "green";
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PRIMARY_COLOR = "green";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IncomingHttpHeaders } from "node:http";
|
|
2
|
+
type HighlightLanguage = "json" | "html" | "xml" | "plain";
|
|
3
|
+
export declare function parseContentType(headers: IncomingHttpHeaders): string;
|
|
4
|
+
export declare function isBinaryContentType(mimeType: string): boolean;
|
|
5
|
+
export declare function hasBinaryBytes(buffer: Buffer): boolean;
|
|
6
|
+
export declare function getHighlightLanguage(mimeType: string): HighlightLanguage;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const BINARY_PREFIXES = [
|
|
2
|
+
"image/",
|
|
3
|
+
"audio/",
|
|
4
|
+
"video/",
|
|
5
|
+
"font/",
|
|
6
|
+
"application/octet-stream",
|
|
7
|
+
"application/zip",
|
|
8
|
+
"application/gzip",
|
|
9
|
+
"application/x-tar",
|
|
10
|
+
"application/pdf",
|
|
11
|
+
"application/wasm",
|
|
12
|
+
"application/protobuf",
|
|
13
|
+
"application/x-protobuf",
|
|
14
|
+
"application/grpc",
|
|
15
|
+
];
|
|
16
|
+
const MIME_TO_LANGUAGE = {
|
|
17
|
+
"application/json": "json",
|
|
18
|
+
"text/html": "html",
|
|
19
|
+
"application/xhtml+xml": "html",
|
|
20
|
+
"text/xml": "xml",
|
|
21
|
+
"application/xml": "xml",
|
|
22
|
+
"application/rss+xml": "xml",
|
|
23
|
+
"application/atom+xml": "xml",
|
|
24
|
+
"image/svg+xml": "xml",
|
|
25
|
+
};
|
|
26
|
+
export function parseContentType(headers) {
|
|
27
|
+
const raw = headers["content-type"];
|
|
28
|
+
if (!raw)
|
|
29
|
+
return "";
|
|
30
|
+
const value = Array.isArray(raw) ? (raw[0] ?? "") : raw;
|
|
31
|
+
return value.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
32
|
+
}
|
|
33
|
+
export function isBinaryContentType(mimeType) {
|
|
34
|
+
return BINARY_PREFIXES.some((prefix) => mimeType === prefix || mimeType.startsWith(prefix));
|
|
35
|
+
}
|
|
36
|
+
export function hasBinaryBytes(buffer) {
|
|
37
|
+
const limit = Math.min(buffer.length, 8192);
|
|
38
|
+
for (let i = 0; i < limit; i++) {
|
|
39
|
+
if (buffer[i] === 0)
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
export function getHighlightLanguage(mimeType) {
|
|
45
|
+
return MIME_TO_LANGUAGE[mimeType] ?? "plain";
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function copyToClipboard(text: string): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
export function copyToClipboard(text) {
|
|
4
|
+
const os = platform();
|
|
5
|
+
let command;
|
|
6
|
+
let args;
|
|
7
|
+
if (os === "darwin") {
|
|
8
|
+
command = "pbcopy";
|
|
9
|
+
args = [];
|
|
10
|
+
}
|
|
11
|
+
else if (os === "win32") {
|
|
12
|
+
command = "clip";
|
|
13
|
+
args = [];
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
command = "xclip";
|
|
17
|
+
args = ["-selection", "clipboard"];
|
|
18
|
+
}
|
|
19
|
+
const child = execFile(command, args);
|
|
20
|
+
child.stdin?.end(text);
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { brotliDecompressSync, gunzipSync, inflateSync } from "node:zlib";
|
|
2
|
+
export function decompressBody(body, headers) {
|
|
3
|
+
const encoding = headers["content-encoding"];
|
|
4
|
+
if (!encoding)
|
|
5
|
+
return body;
|
|
6
|
+
const enc = (Array.isArray(encoding) ? encoding[0] : encoding)
|
|
7
|
+
?.trim()
|
|
8
|
+
.toLowerCase();
|
|
9
|
+
try {
|
|
10
|
+
switch (enc) {
|
|
11
|
+
case "gzip":
|
|
12
|
+
case "x-gzip":
|
|
13
|
+
return gunzipSync(body);
|
|
14
|
+
case "deflate":
|
|
15
|
+
return inflateSync(body);
|
|
16
|
+
case "br":
|
|
17
|
+
return brotliDecompressSync(body);
|
|
18
|
+
default:
|
|
19
|
+
return body;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return body;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatBytes(bytes: number): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const UNITS = ["B", "KB", "MB", "GB"];
|
|
2
|
+
export function formatBytes(bytes) {
|
|
3
|
+
if (bytes === 0)
|
|
4
|
+
return "0 B";
|
|
5
|
+
let unit = 0;
|
|
6
|
+
let value = bytes;
|
|
7
|
+
while (value >= 1024 && unit < UNITS.length - 1) {
|
|
8
|
+
value /= 1024;
|
|
9
|
+
unit++;
|
|
10
|
+
}
|
|
11
|
+
if (unit === 0)
|
|
12
|
+
return `${bytes} B`;
|
|
13
|
+
return `${value.toFixed(1)} ${UNITS[unit]}`;
|
|
14
|
+
}
|