react-native-debug-toolkit 2.0.0 → 2.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/README.md +72 -72
- package/README.zh-CN.md +209 -0
- package/lib/commonjs/components/ClipboardTab.js +15 -16
- package/lib/commonjs/components/ClipboardTab.js.map +1 -1
- package/lib/commonjs/components/ConsoleLogTab.js +69 -202
- package/lib/commonjs/components/ConsoleLogTab.js.map +1 -1
- package/lib/commonjs/components/DebugPanel.js +230 -0
- package/lib/commonjs/components/DebugPanel.js.map +1 -0
- package/lib/commonjs/components/DebugView.js +66 -0
- package/lib/commonjs/components/DebugView.js.map +1 -0
- package/lib/commonjs/components/EnvironmentTab.js +22 -23
- package/lib/commonjs/components/EnvironmentTab.js.map +1 -1
- package/lib/commonjs/components/FeatureTabBar.js +182 -0
- package/lib/commonjs/components/FeatureTabBar.js.map +1 -0
- package/lib/commonjs/components/FloatIcon.js +187 -0
- package/lib/commonjs/components/FloatIcon.js.map +1 -0
- package/lib/commonjs/components/FloatPanelView.js +140 -723
- package/lib/commonjs/components/FloatPanelView.js.map +1 -1
- package/lib/commonjs/components/NavigationLogTab.js +8 -8
- package/lib/commonjs/components/NavigationLogTab.js.map +1 -1
- package/lib/commonjs/components/NetworkLogTab.js +179 -350
- package/lib/commonjs/components/NetworkLogTab.js.map +1 -1
- package/lib/commonjs/components/ThirdPartyLibsTab.js +8 -8
- package/lib/commonjs/components/ThirdPartyLibsTab.js.map +1 -1
- package/lib/commonjs/components/TrackLogTab.js +106 -248
- package/lib/commonjs/components/TrackLogTab.js.map +1 -1
- package/lib/commonjs/components/ZustandLogTab.js +148 -288
- package/lib/commonjs/components/ZustandLogTab.js.map +1 -1
- package/lib/commonjs/components/shared/CollapsibleSection.js +4 -4
- package/lib/commonjs/components/shared/CollapsibleSection.js.map +1 -1
- package/lib/commonjs/components/shared/CopyButton.js +3 -3
- package/lib/commonjs/components/shared/CopyButton.js.map +1 -1
- package/lib/commonjs/components/shared/LogListScreen.js +174 -0
- package/lib/commonjs/components/shared/LogListScreen.js.map +1 -0
- package/lib/commonjs/core/DebugToolkit.js +92 -112
- package/lib/commonjs/core/DebugToolkit.js.map +1 -1
- package/lib/commonjs/core/DebugToolkitProvider.js +30 -28
- package/lib/commonjs/core/DebugToolkitProvider.js.map +1 -1
- package/lib/commonjs/features/ClipboardFeature.js +6 -2
- package/lib/commonjs/features/ClipboardFeature.js.map +1 -1
- package/lib/commonjs/features/ConsoleLogFeature.js +66 -49
- package/lib/commonjs/features/ConsoleLogFeature.js.map +1 -1
- package/lib/commonjs/features/EnvironmentFeature.js +5 -13
- package/lib/commonjs/features/EnvironmentFeature.js.map +1 -1
- package/lib/commonjs/features/NavigationLogFeature.js +17 -38
- package/lib/commonjs/features/NavigationLogFeature.js.map +1 -1
- package/lib/commonjs/features/NetworkFeature.js +35 -256
- package/lib/commonjs/features/NetworkFeature.js.map +1 -1
- package/lib/commonjs/features/TrackFeature.js +17 -38
- package/lib/commonjs/features/TrackFeature.js.map +1 -1
- package/lib/commonjs/features/ZustandLogFeature.js +21 -38
- package/lib/commonjs/features/ZustandLogFeature.js.map +1 -1
- package/lib/commonjs/hooks/useNavigationLogger.js.map +1 -1
- package/lib/commonjs/index.js +7 -20
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/initialize.js +19 -96
- package/lib/commonjs/initialize.js.map +1 -1
- package/lib/commonjs/interceptors/networkInterceptor.js +466 -0
- package/lib/commonjs/interceptors/networkInterceptor.js.map +1 -0
- package/lib/commonjs/utils/colors.js +48 -0
- package/lib/commonjs/utils/colors.js.map +1 -0
- package/lib/commonjs/utils/createChannelFeature.js +48 -0
- package/lib/commonjs/utils/createChannelFeature.js.map +1 -0
- package/lib/commonjs/utils/layout.js +8 -0
- package/lib/commonjs/utils/layout.js.map +1 -0
- package/lib/commonjs/utils/urlRewriterRegistry.js +14 -0
- package/lib/commonjs/utils/urlRewriterRegistry.js.map +1 -0
- package/lib/module/components/ClipboardTab.js +8 -8
- package/lib/module/components/ClipboardTab.js.map +1 -1
- package/lib/module/components/ConsoleLogTab.js +66 -199
- package/lib/module/components/ConsoleLogTab.js.map +1 -1
- package/lib/module/components/DebugPanel.js +225 -0
- package/lib/module/components/DebugPanel.js.map +1 -0
- package/lib/module/components/DebugView.js +61 -0
- package/lib/module/components/DebugView.js.map +1 -0
- package/lib/module/components/EnvironmentTab.js +3 -3
- package/lib/module/components/EnvironmentTab.js.map +1 -1
- package/lib/module/components/FeatureTabBar.js +177 -0
- package/lib/module/components/FeatureTabBar.js.map +1 -0
- package/lib/module/components/FloatIcon.js +182 -0
- package/lib/module/components/FloatIcon.js.map +1 -0
- package/lib/module/components/FloatPanelView.js +141 -723
- package/lib/module/components/FloatPanelView.js.map +1 -1
- package/lib/module/components/NavigationLogTab.js +1 -1
- package/lib/module/components/NavigationLogTab.js.map +1 -1
- package/lib/module/components/NetworkLogTab.js +167 -338
- package/lib/module/components/NetworkLogTab.js.map +1 -1
- package/lib/module/components/ThirdPartyLibsTab.js +1 -1
- package/lib/module/components/ThirdPartyLibsTab.js.map +1 -1
- package/lib/module/components/TrackLogTab.js +94 -236
- package/lib/module/components/TrackLogTab.js.map +1 -1
- package/lib/module/components/ZustandLogTab.js +136 -276
- package/lib/module/components/ZustandLogTab.js.map +1 -1
- package/lib/module/components/shared/CollapsibleSection.js +1 -1
- package/lib/module/components/shared/CollapsibleSection.js.map +1 -1
- package/lib/module/components/shared/CopyButton.js +1 -1
- package/lib/module/components/shared/CopyButton.js.map +1 -1
- package/lib/module/components/shared/LogListScreen.js +169 -0
- package/lib/module/components/shared/LogListScreen.js.map +1 -0
- package/lib/module/core/DebugToolkit.js +92 -111
- package/lib/module/core/DebugToolkit.js.map +1 -1
- package/lib/module/core/DebugToolkitProvider.js +31 -29
- package/lib/module/core/DebugToolkitProvider.js.map +1 -1
- package/lib/module/features/ClipboardFeature.js +6 -2
- package/lib/module/features/ClipboardFeature.js.map +1 -1
- package/lib/module/features/ConsoleLogFeature.js +65 -49
- package/lib/module/features/ConsoleLogFeature.js.map +1 -1
- package/lib/module/features/EnvironmentFeature.js +5 -13
- package/lib/module/features/EnvironmentFeature.js.map +1 -1
- package/lib/module/features/NavigationLogFeature.js +16 -38
- package/lib/module/features/NavigationLogFeature.js.map +1 -1
- package/lib/module/features/NetworkFeature.js +34 -255
- package/lib/module/features/NetworkFeature.js.map +1 -1
- package/lib/module/features/TrackFeature.js +16 -38
- package/lib/module/features/TrackFeature.js.map +1 -1
- package/lib/module/features/ZustandLogFeature.js +22 -38
- package/lib/module/features/ZustandLogFeature.js.map +1 -1
- package/lib/module/hooks/useNavigationLogger.js.map +1 -1
- package/lib/module/index.js +2 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/initialize.js +19 -95
- package/lib/module/initialize.js.map +1 -1
- package/lib/module/interceptors/networkInterceptor.js +460 -0
- package/lib/module/interceptors/networkInterceptor.js.map +1 -0
- package/lib/module/utils/colors.js +43 -0
- package/lib/module/utils/colors.js.map +1 -0
- package/lib/module/utils/createChannelFeature.js +44 -0
- package/lib/module/utils/createChannelFeature.js.map +1 -0
- package/lib/module/utils/layout.js +4 -0
- package/lib/module/utils/layout.js.map +1 -0
- package/lib/module/utils/urlRewriterRegistry.js +10 -0
- package/lib/module/utils/urlRewriterRegistry.js.map +1 -0
- package/lib/typescript/src/components/ClipboardTab.d.ts.map +1 -1
- package/lib/typescript/src/components/ConsoleLogTab.d.ts.map +1 -1
- package/lib/typescript/src/components/DebugPanel.d.ts +9 -0
- package/lib/typescript/src/components/DebugPanel.d.ts.map +1 -0
- package/lib/typescript/src/components/DebugView.d.ts +19 -0
- package/lib/typescript/src/components/DebugView.d.ts.map +1 -0
- package/lib/typescript/src/components/EnvironmentTab.d.ts.map +1 -1
- package/lib/typescript/src/components/FeatureTabBar.d.ts +13 -0
- package/lib/typescript/src/components/FeatureTabBar.d.ts.map +1 -0
- package/lib/typescript/src/components/FloatIcon.d.ts +12 -0
- package/lib/typescript/src/components/FloatIcon.d.ts.map +1 -0
- package/lib/typescript/src/components/FloatPanelView.d.ts +4 -59
- package/lib/typescript/src/components/FloatPanelView.d.ts.map +1 -1
- package/lib/typescript/src/components/NetworkLogTab.d.ts.map +1 -1
- package/lib/typescript/src/components/ThirdPartyLibsTab.d.ts.map +1 -1
- package/lib/typescript/src/components/TrackLogTab.d.ts.map +1 -1
- package/lib/typescript/src/components/ZustandLogTab.d.ts.map +1 -1
- package/lib/typescript/src/components/shared/LogListScreen.d.ts +21 -0
- package/lib/typescript/src/components/shared/LogListScreen.d.ts.map +1 -0
- package/lib/typescript/src/core/DebugToolkit.d.ts +11 -18
- package/lib/typescript/src/core/DebugToolkit.d.ts.map +1 -1
- package/lib/typescript/src/core/DebugToolkitProvider.d.ts +2 -5
- package/lib/typescript/src/core/DebugToolkitProvider.d.ts.map +1 -1
- package/lib/typescript/src/features/ClipboardFeature.d.ts +4 -0
- package/lib/typescript/src/features/ClipboardFeature.d.ts.map +1 -1
- package/lib/typescript/src/features/ConsoleLogFeature.d.ts +2 -0
- package/lib/typescript/src/features/ConsoleLogFeature.d.ts.map +1 -1
- package/lib/typescript/src/features/EnvironmentFeature.d.ts.map +1 -1
- package/lib/typescript/src/features/NavigationLogFeature.d.ts +2 -0
- package/lib/typescript/src/features/NavigationLogFeature.d.ts.map +1 -1
- package/lib/typescript/src/features/NetworkFeature.d.ts +7 -20
- package/lib/typescript/src/features/NetworkFeature.d.ts.map +1 -1
- package/lib/typescript/src/features/TrackFeature.d.ts +2 -0
- package/lib/typescript/src/features/TrackFeature.d.ts.map +1 -1
- package/lib/typescript/src/features/ZustandLogFeature.d.ts +2 -0
- package/lib/typescript/src/features/ZustandLogFeature.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useNavigationLogger.d.ts +1 -8
- package/lib/typescript/src/hooks/useNavigationLogger.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -5
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/initialize.d.ts +3 -22
- package/lib/typescript/src/initialize.d.ts.map +1 -1
- package/lib/typescript/src/interceptors/networkInterceptor.d.ts +18 -0
- package/lib/typescript/src/interceptors/networkInterceptor.d.ts.map +1 -0
- package/lib/typescript/src/types/index.d.ts +26 -29
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/colors.d.ts +21 -0
- package/lib/typescript/src/utils/colors.d.ts.map +1 -0
- package/lib/typescript/src/utils/createChannelFeature.d.ts +18 -0
- package/lib/typescript/src/utils/createChannelFeature.d.ts.map +1 -0
- package/lib/typescript/src/utils/layout.d.ts +2 -0
- package/lib/typescript/src/utils/layout.d.ts.map +1 -0
- package/lib/typescript/src/utils/urlRewriterRegistry.d.ts +7 -0
- package/lib/typescript/src/utils/urlRewriterRegistry.d.ts.map +1 -0
- package/package.json +5 -1
- package/src/components/ClipboardTab.tsx +8 -8
- package/src/components/ConsoleLogTab.tsx +49 -163
- package/src/components/DebugPanel.tsx +215 -0
- package/src/components/DebugView.tsx +80 -0
- package/src/components/EnvironmentTab.tsx +3 -3
- package/src/components/FeatureTabBar.tsx +204 -0
- package/src/components/FloatIcon.tsx +171 -0
- package/src/components/FloatPanelView.tsx +135 -647
- package/src/components/NavigationLogTab.tsx +1 -1
- package/src/components/NetworkLogTab.tsx +128 -269
- package/src/components/ThirdPartyLibsTab.tsx +3 -3
- package/src/components/TrackLogTab.tsx +53 -188
- package/src/components/ZustandLogTab.tsx +79 -181
- package/src/components/shared/CollapsibleSection.tsx +1 -1
- package/src/components/shared/CopyButton.tsx +1 -1
- package/src/components/shared/LogListScreen.tsx +164 -0
- package/src/core/DebugToolkit.tsx +114 -138
- package/src/core/DebugToolkitProvider.tsx +32 -38
- package/src/features/ClipboardFeature.ts +6 -2
- package/src/features/ConsoleLogFeature.ts +66 -68
- package/src/features/EnvironmentFeature.ts +5 -13
- package/src/features/NavigationLogFeature.ts +12 -42
- package/src/features/NetworkFeature.ts +43 -405
- package/src/features/TrackFeature.ts +14 -49
- package/src/features/ZustandLogFeature.ts +16 -42
- package/src/hooks/useNavigationLogger.ts +1 -6
- package/src/index.ts +5 -9
- package/src/initialize.ts +28 -120
- package/src/interceptors/networkInterceptor.ts +646 -0
- package/src/types/index.ts +25 -36
- package/src/utils/colors.ts +38 -0
- package/src/utils/createChannelFeature.ts +55 -0
- package/src/utils/layout.ts +1 -0
- package/src/utils/urlRewriterRegistry.ts +10 -0
- package/lib/commonjs/utils/constants.js +0 -135
- package/lib/commonjs/utils/constants.js.map +0 -1
- package/lib/module/utils/constants.js +0 -130
- package/lib/module/utils/constants.js.map +0 -1
- package/lib/typescript/src/utils/constants.d.ts +0 -96
- package/lib/typescript/src/utils/constants.d.ts.map +0 -1
- package/src/utils/constants.ts +0 -91
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import type { NetworkLogEntry } from '../types';
|
|
2
|
+
import { urlRewriter } from '../utils/urlRewriterRegistry';
|
|
3
|
+
|
|
4
|
+
type NetworkLogPayload = Omit<NetworkLogEntry, 'id'>;
|
|
5
|
+
|
|
6
|
+
// ─── Axios types ───────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
interface AxiosInterceptorManager {
|
|
9
|
+
use: (onFulfilled: unknown, onRejected?: unknown) => number | void;
|
|
10
|
+
eject?: (id: number) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface AxiosRequestConfigLike {
|
|
14
|
+
baseURL?: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
method?: string;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
data?: unknown;
|
|
19
|
+
params?: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface AxiosResponseLike {
|
|
23
|
+
status: number;
|
|
24
|
+
statusText: string;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
data?: unknown;
|
|
27
|
+
config: AxiosRequestConfigLike;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface AxiosErrorLike {
|
|
31
|
+
message: string;
|
|
32
|
+
config?: AxiosRequestConfigLike;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AxiosInstanceLike {
|
|
36
|
+
interceptors: {
|
|
37
|
+
request: AxiosInterceptorManager;
|
|
38
|
+
response: AxiosInterceptorManager;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface AxiosRegistration {
|
|
43
|
+
refCount: number;
|
|
44
|
+
requestId?: number;
|
|
45
|
+
responseId?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── XMLHttpRequest types ─────────────────────────────
|
|
49
|
+
|
|
50
|
+
type XhrOpen = (
|
|
51
|
+
this: XMLHttpRequestLike,
|
|
52
|
+
method: string,
|
|
53
|
+
url: string,
|
|
54
|
+
async?: boolean,
|
|
55
|
+
username?: string | null,
|
|
56
|
+
password?: string | null,
|
|
57
|
+
) => void;
|
|
58
|
+
|
|
59
|
+
type XhrSend = (this: XMLHttpRequestLike, body?: unknown) => void;
|
|
60
|
+
type XhrSetRequestHeader = (
|
|
61
|
+
this: XMLHttpRequestLike,
|
|
62
|
+
name: string,
|
|
63
|
+
value: string,
|
|
64
|
+
) => void;
|
|
65
|
+
type XhrEventListener = (event?: unknown) => void;
|
|
66
|
+
type XhrHandler = ((this: XMLHttpRequestLike, event?: unknown) => void) | null;
|
|
67
|
+
|
|
68
|
+
interface XMLHttpRequestLike {
|
|
69
|
+
status?: number;
|
|
70
|
+
statusText?: string;
|
|
71
|
+
response?: unknown;
|
|
72
|
+
responseText?: string;
|
|
73
|
+
responseType?: string;
|
|
74
|
+
readyState?: number;
|
|
75
|
+
DONE?: number;
|
|
76
|
+
open: XhrOpen;
|
|
77
|
+
send: XhrSend;
|
|
78
|
+
setRequestHeader?: XhrSetRequestHeader;
|
|
79
|
+
getAllResponseHeaders?: () => string;
|
|
80
|
+
addEventListener?: (event: string, listener: XhrEventListener) => void;
|
|
81
|
+
removeEventListener?: (event: string, listener: XhrEventListener) => void;
|
|
82
|
+
onreadystatechange?: XhrHandler;
|
|
83
|
+
onloadend?: XhrHandler;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface XMLHttpRequestConstructorLike {
|
|
87
|
+
new (): XMLHttpRequestLike;
|
|
88
|
+
prototype: XMLHttpRequestLike;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface XMLHttpRequestRegistration {
|
|
92
|
+
constructor: XMLHttpRequestConstructorLike;
|
|
93
|
+
open: XhrOpen;
|
|
94
|
+
send: XhrSend;
|
|
95
|
+
setRequestHeader?: XhrSetRequestHeader;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface XhrRequestState {
|
|
99
|
+
method: string;
|
|
100
|
+
url: string;
|
|
101
|
+
headers: Record<string, string>;
|
|
102
|
+
body?: unknown;
|
|
103
|
+
startTime: number;
|
|
104
|
+
timestamp: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Shared helpers ────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
function rewriteUrl(url: string): string {
|
|
110
|
+
const rewriter = urlRewriter.get();
|
|
111
|
+
if (!rewriter) {
|
|
112
|
+
return url;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
return rewriter(url);
|
|
116
|
+
} catch {
|
|
117
|
+
return url;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function headersToObject(
|
|
122
|
+
headers: Record<string, string> | Headers | undefined,
|
|
123
|
+
): Record<string, string> {
|
|
124
|
+
const result: Record<string, string> = {};
|
|
125
|
+
if (!headers) {
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
if (typeof (headers as Headers).forEach === 'function') {
|
|
129
|
+
(headers as Headers).forEach((value: string, key: string) => {
|
|
130
|
+
result[key] = value;
|
|
131
|
+
});
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
Object.keys(headers).forEach((key) => {
|
|
135
|
+
result[key] = (headers as Record<string, string>)[key]!;
|
|
136
|
+
});
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getRequestSnapshot(
|
|
141
|
+
input: unknown,
|
|
142
|
+
init?: RequestInit,
|
|
143
|
+
): NetworkLogEntry['request'] {
|
|
144
|
+
const request = input instanceof Request ? input : null;
|
|
145
|
+
return {
|
|
146
|
+
url: typeof input === 'string' ? input : request?.url ?? String(input),
|
|
147
|
+
method: (init?.method || request?.method || 'GET').toUpperCase(),
|
|
148
|
+
headers: init?.headers
|
|
149
|
+
? headersToObject(init.headers as Record<string, string> | Headers)
|
|
150
|
+
: request?.headers
|
|
151
|
+
? headersToObject(request.headers)
|
|
152
|
+
: undefined,
|
|
153
|
+
body: init?.body,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function parseResponseBody(response: Response): Promise<unknown> {
|
|
158
|
+
const raw = await response.clone().text();
|
|
159
|
+
if (!raw) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
return JSON.parse(raw);
|
|
164
|
+
} catch {
|
|
165
|
+
return raw;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function parseMaybeJson(raw: string): unknown {
|
|
170
|
+
if (!raw) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(raw);
|
|
175
|
+
} catch {
|
|
176
|
+
return raw;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function parseRawHeaders(rawHeaders: string | undefined): Record<string, string> | undefined {
|
|
181
|
+
if (!rawHeaders) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const headers: Record<string, string> = {};
|
|
185
|
+
rawHeaders.split(/\r?\n/).forEach((line) => {
|
|
186
|
+
const separatorIndex = line.indexOf(':');
|
|
187
|
+
if (separatorIndex <= 0) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
191
|
+
const value = line.slice(separatorIndex + 1).trim();
|
|
192
|
+
if (!key) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
headers[key] = value;
|
|
196
|
+
});
|
|
197
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getXMLHttpRequestConstructor(): XMLHttpRequestConstructorLike | undefined {
|
|
201
|
+
const globalWithXhr = globalThis as unknown as { XMLHttpRequest?: unknown };
|
|
202
|
+
const candidate = globalWithXhr.XMLHttpRequest;
|
|
203
|
+
if (typeof candidate !== 'function') {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const prototype = (candidate as { prototype?: Partial<XMLHttpRequestLike> }).prototype;
|
|
208
|
+
if (
|
|
209
|
+
!prototype ||
|
|
210
|
+
typeof prototype.open !== 'function' ||
|
|
211
|
+
typeof prototype.send !== 'function'
|
|
212
|
+
) {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return candidate as XMLHttpRequestConstructorLike;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function getXhrResponseHeaders(xhr: XMLHttpRequestLike): Record<string, string> | undefined {
|
|
220
|
+
if (typeof xhr.getAllResponseHeaders !== 'function') {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
return parseRawHeaders(xhr.getAllResponseHeaders());
|
|
225
|
+
} catch {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function getXhrResponseData(xhr: XMLHttpRequestLike): unknown {
|
|
231
|
+
const responseType = typeof xhr.responseType === 'string' ? xhr.responseType : '';
|
|
232
|
+
if (responseType === 'json') {
|
|
233
|
+
return xhr.response;
|
|
234
|
+
}
|
|
235
|
+
if (responseType && responseType !== 'text') {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
if (typeof xhr.responseText === 'string') {
|
|
241
|
+
return parseMaybeJson(xhr.responseText);
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return typeof xhr.response === 'string' ? parseMaybeJson(xhr.response) : xhr.response;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ─── Interceptor manager (encapsulated mutable state) ──
|
|
251
|
+
|
|
252
|
+
let originalFetch: typeof globalThis.fetch | null = null;
|
|
253
|
+
let fetchRefCount = 0;
|
|
254
|
+
const pendingAxiosRequests = new WeakMap<AxiosRequestConfigLike, { startTime: number; timestamp: number }>();
|
|
255
|
+
const axiosRegistrations = new WeakMap<AxiosInstanceLike, AxiosRegistration>();
|
|
256
|
+
let originalXMLHttpRequest: XMLHttpRequestRegistration | null = null;
|
|
257
|
+
let xhrRefCount = 0;
|
|
258
|
+
const pendingXhrRequests = new WeakMap<XMLHttpRequestLike, XhrRequestState>();
|
|
259
|
+
|
|
260
|
+
function stopFetch(): void {
|
|
261
|
+
fetchRefCount = Math.max(0, fetchRefCount - 1);
|
|
262
|
+
if (fetchRefCount === 0 && originalFetch) {
|
|
263
|
+
globalThis.fetch = originalFetch;
|
|
264
|
+
originalFetch = null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function startFetch(emit: (entry: NetworkLogPayload) => void): () => void {
|
|
269
|
+
fetchRefCount += 1;
|
|
270
|
+
if (originalFetch) {
|
|
271
|
+
return () => {
|
|
272
|
+
stopFetch();
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
originalFetch = globalThis.fetch;
|
|
277
|
+
|
|
278
|
+
globalThis.fetch = async function interceptedFetch(
|
|
279
|
+
input: Parameters<typeof fetch>[0],
|
|
280
|
+
init?: Parameters<typeof fetch>[1],
|
|
281
|
+
) {
|
|
282
|
+
const startTime = Date.now();
|
|
283
|
+
|
|
284
|
+
let rewrittenInput: typeof input = input;
|
|
285
|
+
if (urlRewriter.get()) {
|
|
286
|
+
if (typeof input === 'string') {
|
|
287
|
+
rewrittenInput = rewriteUrl(input);
|
|
288
|
+
} else if (input instanceof Request) {
|
|
289
|
+
rewrittenInput = new Request(rewriteUrl(input.url), input as RequestInit);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const request = getRequestSnapshot(rewrittenInput, init);
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const response = await originalFetch!.call(globalThis, rewrittenInput, init);
|
|
297
|
+
const duration = Date.now() - startTime;
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const data = await parseResponseBody(response);
|
|
301
|
+
emit({
|
|
302
|
+
timestamp: startTime,
|
|
303
|
+
duration,
|
|
304
|
+
request,
|
|
305
|
+
response: { status: response.status, statusText: response.statusText, data },
|
|
306
|
+
});
|
|
307
|
+
} catch {
|
|
308
|
+
emit({
|
|
309
|
+
timestamp: startTime,
|
|
310
|
+
duration,
|
|
311
|
+
request,
|
|
312
|
+
response: { status: response.status, statusText: response.statusText },
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return response;
|
|
317
|
+
} catch (error) {
|
|
318
|
+
emit({
|
|
319
|
+
timestamp: startTime,
|
|
320
|
+
duration: Date.now() - startTime,
|
|
321
|
+
request,
|
|
322
|
+
error: error instanceof Error ? error.message : String(error),
|
|
323
|
+
});
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
return () => {
|
|
329
|
+
stopFetch();
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function emitXhrLog(
|
|
334
|
+
xhr: XMLHttpRequestLike,
|
|
335
|
+
emit: (entry: NetworkLogPayload) => void,
|
|
336
|
+
error?: string,
|
|
337
|
+
): void {
|
|
338
|
+
const pending = pendingXhrRequests.get(xhr);
|
|
339
|
+
if (!pending) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const status = typeof xhr.status === 'number' ? xhr.status : 0;
|
|
344
|
+
const statusText = typeof xhr.statusText === 'string' ? xhr.statusText : undefined;
|
|
345
|
+
|
|
346
|
+
emit({
|
|
347
|
+
timestamp: pending.timestamp,
|
|
348
|
+
duration: Date.now() - pending.startTime,
|
|
349
|
+
request: {
|
|
350
|
+
url: pending.url,
|
|
351
|
+
method: pending.method,
|
|
352
|
+
headers: Object.keys(pending.headers).length > 0 ? pending.headers : undefined,
|
|
353
|
+
body: pending.body,
|
|
354
|
+
},
|
|
355
|
+
response: error
|
|
356
|
+
? undefined
|
|
357
|
+
: {
|
|
358
|
+
status,
|
|
359
|
+
statusText,
|
|
360
|
+
headers: getXhrResponseHeaders(xhr),
|
|
361
|
+
data: getXhrResponseData(xhr),
|
|
362
|
+
success: status >= 200 && status < 300,
|
|
363
|
+
},
|
|
364
|
+
error,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
pendingXhrRequests.delete(xhr);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function isXhrDone(xhr: XMLHttpRequestLike): boolean {
|
|
371
|
+
return typeof xhr.readyState === 'number' && xhr.readyState === (xhr.DONE ?? 4);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function attachXhrCompletion(
|
|
375
|
+
xhr: XMLHttpRequestLike,
|
|
376
|
+
emit: (entry: NetworkLogPayload) => void,
|
|
377
|
+
): void {
|
|
378
|
+
let completed = false;
|
|
379
|
+
const finish = (error?: string) => {
|
|
380
|
+
if (completed) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
completed = true;
|
|
384
|
+
if (typeof xhr.removeEventListener === 'function') {
|
|
385
|
+
xhr.removeEventListener('loadend', onLoadEnd);
|
|
386
|
+
xhr.removeEventListener('error', onError);
|
|
387
|
+
xhr.removeEventListener('abort', onAbort);
|
|
388
|
+
xhr.removeEventListener('timeout', onTimeout);
|
|
389
|
+
}
|
|
390
|
+
emitXhrLog(xhr, emit, error);
|
|
391
|
+
};
|
|
392
|
+
const onLoadEnd = () => finish();
|
|
393
|
+
const onError = () => finish('XMLHttpRequest error');
|
|
394
|
+
const onAbort = () => finish('XMLHttpRequest aborted');
|
|
395
|
+
const onTimeout = () => finish('XMLHttpRequest timeout');
|
|
396
|
+
|
|
397
|
+
if (typeof xhr.addEventListener === 'function') {
|
|
398
|
+
xhr.addEventListener('loadend', onLoadEnd);
|
|
399
|
+
xhr.addEventListener('error', onError);
|
|
400
|
+
xhr.addEventListener('abort', onAbort);
|
|
401
|
+
xhr.addEventListener('timeout', onTimeout);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const originalOnReadyStateChange = xhr.onreadystatechange;
|
|
406
|
+
const originalOnLoadEnd = xhr.onloadend;
|
|
407
|
+
xhr.onreadystatechange = function interceptedReadyStateChange(event?: unknown) {
|
|
408
|
+
originalOnReadyStateChange?.call(this, event);
|
|
409
|
+
if (isXhrDone(this)) {
|
|
410
|
+
finish();
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
xhr.onloadend = function interceptedLoadEnd(event?: unknown) {
|
|
414
|
+
originalOnLoadEnd?.call(this, event);
|
|
415
|
+
finish();
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function stopXMLHttpRequest(): void {
|
|
420
|
+
xhrRefCount = Math.max(0, xhrRefCount - 1);
|
|
421
|
+
if (xhrRefCount === 0 && originalXMLHttpRequest) {
|
|
422
|
+
const prototype = originalXMLHttpRequest.constructor.prototype;
|
|
423
|
+
prototype.open = originalXMLHttpRequest.open;
|
|
424
|
+
prototype.send = originalXMLHttpRequest.send;
|
|
425
|
+
if (originalXMLHttpRequest.setRequestHeader) {
|
|
426
|
+
prototype.setRequestHeader = originalXMLHttpRequest.setRequestHeader;
|
|
427
|
+
}
|
|
428
|
+
originalXMLHttpRequest = null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function startXMLHttpRequest(
|
|
433
|
+
emit: (entry: NetworkLogPayload) => void,
|
|
434
|
+
): () => void {
|
|
435
|
+
xhrRefCount += 1;
|
|
436
|
+
if (originalXMLHttpRequest) {
|
|
437
|
+
return () => {
|
|
438
|
+
stopXMLHttpRequest();
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const XMLHttpRequestConstructor = getXMLHttpRequestConstructor();
|
|
443
|
+
if (!XMLHttpRequestConstructor) {
|
|
444
|
+
return () => {
|
|
445
|
+
stopXMLHttpRequest();
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const prototype = XMLHttpRequestConstructor.prototype;
|
|
450
|
+
const originalOpen = prototype.open;
|
|
451
|
+
const originalSend = prototype.send;
|
|
452
|
+
const originalSetRequestHeader = prototype.setRequestHeader;
|
|
453
|
+
originalXMLHttpRequest = {
|
|
454
|
+
constructor: XMLHttpRequestConstructor,
|
|
455
|
+
open: originalOpen,
|
|
456
|
+
send: originalSend,
|
|
457
|
+
setRequestHeader: originalSetRequestHeader,
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
prototype.open = function interceptedOpen(
|
|
461
|
+
method: string,
|
|
462
|
+
url: string,
|
|
463
|
+
async?: boolean,
|
|
464
|
+
username?: string | null,
|
|
465
|
+
password?: string | null,
|
|
466
|
+
): void {
|
|
467
|
+
const rewrittenUrl = urlRewriter.get() ? rewriteUrl(url) : url;
|
|
468
|
+
const now = Date.now();
|
|
469
|
+
pendingXhrRequests.set(this, {
|
|
470
|
+
method: method.toUpperCase(),
|
|
471
|
+
url: rewrittenUrl,
|
|
472
|
+
headers: {},
|
|
473
|
+
startTime: now,
|
|
474
|
+
timestamp: now,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
if (typeof password !== 'undefined') {
|
|
478
|
+
return originalOpen.call(this, method, rewrittenUrl, async, username, password);
|
|
479
|
+
}
|
|
480
|
+
if (typeof username !== 'undefined') {
|
|
481
|
+
return originalOpen.call(this, method, rewrittenUrl, async, username);
|
|
482
|
+
}
|
|
483
|
+
if (typeof async !== 'undefined') {
|
|
484
|
+
return originalOpen.call(this, method, rewrittenUrl, async);
|
|
485
|
+
}
|
|
486
|
+
return originalOpen.call(this, method, rewrittenUrl);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
if (originalSetRequestHeader) {
|
|
490
|
+
prototype.setRequestHeader = function interceptedSetRequestHeader(
|
|
491
|
+
name: string,
|
|
492
|
+
value: string,
|
|
493
|
+
): void {
|
|
494
|
+
const pending = pendingXhrRequests.get(this);
|
|
495
|
+
if (pending) {
|
|
496
|
+
pending.headers[name] = value;
|
|
497
|
+
}
|
|
498
|
+
return originalSetRequestHeader.call(this, name, value);
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
prototype.send = function interceptedSend(body?: unknown): void {
|
|
503
|
+
const pending = pendingXhrRequests.get(this);
|
|
504
|
+
if (pending) {
|
|
505
|
+
const now = Date.now();
|
|
506
|
+
pending.body = body;
|
|
507
|
+
pending.startTime = now;
|
|
508
|
+
pending.timestamp = now;
|
|
509
|
+
attachXhrCompletion(this, emit);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
return originalSend.call(this, body);
|
|
514
|
+
} catch (error) {
|
|
515
|
+
emitXhrLog(
|
|
516
|
+
this,
|
|
517
|
+
emit,
|
|
518
|
+
error instanceof Error ? error.message : String(error),
|
|
519
|
+
);
|
|
520
|
+
throw error;
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
return () => {
|
|
525
|
+
stopXMLHttpRequest();
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function teardownAxios(axiosInstance: AxiosInstanceLike): void {
|
|
530
|
+
const registration = axiosRegistrations.get(axiosInstance);
|
|
531
|
+
if (!registration) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
registration.refCount -= 1;
|
|
535
|
+
if (registration.refCount > 0) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (typeof registration.requestId === 'number') {
|
|
539
|
+
axiosInstance.interceptors.request.eject?.(registration.requestId);
|
|
540
|
+
}
|
|
541
|
+
if (typeof registration.responseId === 'number') {
|
|
542
|
+
axiosInstance.interceptors.response.eject?.(registration.responseId);
|
|
543
|
+
}
|
|
544
|
+
axiosRegistrations.delete(axiosInstance);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
export function setupAxios(
|
|
548
|
+
axiosInstance: AxiosInstanceLike,
|
|
549
|
+
emit: (entry: NetworkLogPayload) => void,
|
|
550
|
+
): () => void {
|
|
551
|
+
const existing = axiosRegistrations.get(axiosInstance);
|
|
552
|
+
if (existing) {
|
|
553
|
+
existing.refCount += 1;
|
|
554
|
+
return () => {
|
|
555
|
+
teardownAxios(axiosInstance);
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const requestId = axiosInstance.interceptors.request.use(
|
|
560
|
+
(config: AxiosRequestConfigLike) => {
|
|
561
|
+
if (urlRewriter.get() && config.url) {
|
|
562
|
+
const fullUrl = config.baseURL ? `${config.baseURL}${config.url}` : config.url;
|
|
563
|
+
const rewritten = rewriteUrl(fullUrl);
|
|
564
|
+
if (rewritten !== fullUrl) {
|
|
565
|
+
if (config.baseURL && rewritten.startsWith(config.baseURL)) {
|
|
566
|
+
config.url = rewritten.slice(config.baseURL.length);
|
|
567
|
+
} else {
|
|
568
|
+
config.baseURL = '';
|
|
569
|
+
config.url = rewritten;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
pendingAxiosRequests.set(config, { startTime: Date.now(), timestamp: Date.now() });
|
|
574
|
+
return config;
|
|
575
|
+
},
|
|
576
|
+
(error: unknown) => Promise.reject(error),
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
const responseId = axiosInstance.interceptors.response.use(
|
|
580
|
+
(response: AxiosResponseLike) => {
|
|
581
|
+
const pending = pendingAxiosRequests.get(response.config);
|
|
582
|
+
if (pending) {
|
|
583
|
+
emit({
|
|
584
|
+
timestamp: pending.timestamp,
|
|
585
|
+
duration: Date.now() - pending.startTime,
|
|
586
|
+
request: {
|
|
587
|
+
url: `${response.config.baseURL || ''}${response.config.url}`,
|
|
588
|
+
method: (response.config.method || 'GET').toUpperCase(),
|
|
589
|
+
headers: response.config.headers,
|
|
590
|
+
body: response.config.data || response.config.params,
|
|
591
|
+
},
|
|
592
|
+
response: {
|
|
593
|
+
status: response.status,
|
|
594
|
+
statusText: response.statusText,
|
|
595
|
+
headers: response.headers,
|
|
596
|
+
data: response.data,
|
|
597
|
+
success:
|
|
598
|
+
response.data && typeof response.data === 'object'
|
|
599
|
+
? (response.data as Record<string, unknown>).success !== false
|
|
600
|
+
: response.status >= 200 && response.status < 300,
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
pendingAxiosRequests.delete(response.config);
|
|
604
|
+
}
|
|
605
|
+
return response;
|
|
606
|
+
},
|
|
607
|
+
(error: AxiosErrorLike) => {
|
|
608
|
+
if (error.config) {
|
|
609
|
+
const pending = pendingAxiosRequests.get(error.config);
|
|
610
|
+
emit({
|
|
611
|
+
timestamp: pending ? pending.timestamp : Date.now(),
|
|
612
|
+
duration: pending ? Date.now() - pending.startTime : undefined,
|
|
613
|
+
request: {
|
|
614
|
+
url: `${error.config.baseURL || ''}${error.config.url}`,
|
|
615
|
+
method: (error.config.method || 'GET').toUpperCase(),
|
|
616
|
+
headers: error.config.headers,
|
|
617
|
+
body: error.config.data || error.config.params,
|
|
618
|
+
},
|
|
619
|
+
error: error.message,
|
|
620
|
+
});
|
|
621
|
+
pendingAxiosRequests.delete(error.config);
|
|
622
|
+
}
|
|
623
|
+
return Promise.reject(error);
|
|
624
|
+
},
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
axiosRegistrations.set(axiosInstance, {
|
|
628
|
+
refCount: 1,
|
|
629
|
+
requestId: typeof requestId === 'number' ? requestId : undefined,
|
|
630
|
+
responseId: typeof responseId === 'number' ? responseId : undefined,
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
return () => {
|
|
634
|
+
teardownAxios(axiosInstance);
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export function resetInterceptors(): void {
|
|
639
|
+
if (originalFetch) {
|
|
640
|
+
globalThis.fetch = originalFetch;
|
|
641
|
+
originalFetch = null;
|
|
642
|
+
}
|
|
643
|
+
fetchRefCount = 0;
|
|
644
|
+
xhrRefCount = 1;
|
|
645
|
+
stopXMLHttpRequest();
|
|
646
|
+
}
|