@rozenite/network-activity-plugin 1.0.0-alpha.1 → 1.0.0-alpha.10
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 +3 -5
- package/dist/{panel.html → App.html} +3 -3
- package/dist/assets/App-CA1Fbh0I.js +25364 -0
- package/dist/assets/App-DoHQsY5s.css +1276 -0
- package/dist/event-source.cjs +22 -0
- package/dist/event-source.js +23 -0
- package/dist/react-native.cjs +8 -1
- package/dist/react-native.d.ts +1 -5
- package/dist/react-native.js +6 -171
- package/dist/rozenite.config.d.ts +7 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/http/network-inspector.d.ts +8 -0
- package/dist/src/react-native/http/network-requests-registry.d.ts +6 -0
- package/dist/src/react-native/http/xhr-interceptor.d.ts +38 -0
- package/dist/src/react-native/sse/event-source.d.ts +2 -0
- package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
- package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
- package/dist/src/react-native/sse/types.d.ts +6 -0
- package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -0
- package/dist/src/react-native/utils.d.ts +6 -0
- package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
- package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
- package/dist/src/shared/client.d.ts +68 -0
- package/dist/src/shared/sse-events.d.ts +35 -0
- package/dist/src/shared/websocket-events.d.ts +60 -0
- package/dist/src/ui/App.d.ts +1 -0
- package/dist/src/ui/components/Badge.d.ts +9 -0
- package/dist/src/ui/components/Button.d.ts +11 -0
- package/dist/src/ui/components/Input.d.ts +3 -0
- package/dist/src/ui/components/JsonTree.d.ts +5 -0
- package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
- package/dist/src/ui/components/RequestList.d.ts +25 -0
- package/dist/src/ui/components/ScrollArea.d.ts +4 -0
- package/dist/src/ui/components/Separator.d.ts +3 -0
- package/dist/src/ui/components/SidePanel.d.ts +1 -0
- package/dist/src/ui/components/Toolbar.d.ts +1 -0
- package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
- package/dist/src/ui/state/derived.d.ts +5 -0
- package/dist/src/ui/state/hooks.d.ts +17 -0
- package/dist/src/ui/state/model.d.ts +98 -0
- package/dist/src/ui/state/store.d.ts +24 -0
- package/dist/src/ui/tabs/CookiesTab.d.ts +5 -0
- package/dist/src/ui/tabs/HeadersTab.d.ts +5 -0
- package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
- package/dist/src/ui/tabs/RequestTab.d.ts +5 -0
- package/dist/src/ui/tabs/ResponseTab.d.ts +6 -0
- package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
- package/dist/src/ui/tabs/TimingTab.d.ts +5 -0
- package/dist/src/ui/types.d.ts +26 -0
- package/dist/src/ui/utils/assert.d.ts +1 -0
- package/dist/src/ui/utils/cn.d.ts +2 -0
- package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
- package/dist/src/ui/utils/getHttpHeaderValue.d.ts +2 -0
- package/dist/src/ui/utils/getId.d.ts +1 -0
- package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
- package/dist/src/ui/views/InspectorView.d.ts +5 -0
- package/dist/src/ui/views/LoadingView.d.ts +1 -0
- package/dist/useNetworkActivityDevTools.cjs +759 -0
- package/dist/useNetworkActivityDevTools.js +757 -0
- package/package.json +31 -10
- package/postcss.config.js +6 -0
- package/project.json +12 -0
- package/react-native.ts +2 -1
- package/rozenite.config.ts +2 -2
- package/src/css-modules.d.ts +1 -1
- package/src/react-native/http/network-inspector.ts +226 -0
- package/src/react-native/http/network-requests-registry.ts +52 -0
- package/src/react-native/http/xhr-interceptor.ts +211 -0
- package/src/react-native/http/xml-request.d.ts +34 -0
- package/src/react-native/sse/event-source.ts +25 -0
- package/src/react-native/sse/sse-inspector.ts +117 -0
- package/src/react-native/sse/sse-interceptor.ts +162 -0
- package/src/react-native/sse/types.ts +9 -0
- package/src/react-native/useNetworkActivityDevTools.ts +73 -210
- package/src/react-native/utils.ts +43 -0
- package/src/react-native/websocket/websocket-inspector.ts +180 -0
- package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
- package/src/react-native/websocket/websocket-interceptor.ts +166 -0
- package/src/shared/client.ts +86 -0
- package/src/shared/sse-events.ts +44 -0
- package/src/shared/websocket-events.ts +79 -0
- package/src/ui/App.tsx +19 -0
- package/src/ui/components/Badge.tsx +36 -0
- package/src/ui/components/Button.tsx +56 -0
- package/src/ui/components/Input.tsx +22 -0
- package/src/ui/components/JsonTree.tsx +50 -0
- package/src/ui/components/JsonTreeCopyableItem.tsx +33 -0
- package/src/ui/components/RequestList.tsx +295 -0
- package/src/ui/components/ScrollArea.tsx +48 -0
- package/src/ui/components/Separator.tsx +31 -0
- package/src/ui/components/SidePanel.tsx +323 -0
- package/src/ui/components/Tabs.tsx +55 -0
- package/src/ui/components/Toolbar.tsx +45 -0
- package/src/ui/globals.css +90 -0
- package/src/ui/hooks/useCopyToClipboard.ts +28 -0
- package/src/ui/state/derived.ts +112 -0
- package/src/ui/state/hooks.ts +44 -0
- package/src/ui/state/model.ts +129 -0
- package/src/ui/state/store.ts +559 -0
- package/src/ui/tabs/CookiesTab.tsx +279 -0
- package/src/ui/tabs/HeadersTab.tsx +110 -0
- package/src/ui/tabs/MessagesTab.tsx +276 -0
- package/src/ui/tabs/RequestTab.tsx +69 -0
- package/src/ui/tabs/ResponseTab.tsx +138 -0
- package/src/ui/tabs/SSEMessagesTab.tsx +213 -0
- package/src/ui/tabs/TimingTab.tsx +60 -0
- package/src/ui/types.ts +34 -0
- package/src/ui/utils/assert.ts +5 -0
- package/src/ui/utils/cn.ts +6 -0
- package/src/ui/utils/copyToClipboard.ts +3 -0
- package/src/ui/utils/getHttpHeaderValue.ts +14 -0
- package/src/ui/utils/getId.ts +10 -0
- package/src/ui/utils/getStatusColor.ts +15 -0
- package/src/ui/views/InspectorView.tsx +53 -0
- package/src/ui/views/LoadingView.tsx +19 -0
- package/tailwind.config.ts +96 -0
- package/tsconfig.json +13 -6
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +13 -1
- package/dist/assets/panel-C5YgUUj5.js +0 -54
- package/dist/assets/panel-NCVczPb1.css +0 -1
- package/src/types/network.ts +0 -153
- package/src/ui/components.module.css +0 -158
- package/src/ui/components.tsx +0 -219
- package/src/ui/network-details.module.css +0 -57
- package/src/ui/network-details.tsx +0 -134
- package/src/ui/network-list.module.css +0 -122
- package/src/ui/network-list.tsx +0 -145
- package/src/ui/network-toolbar.module.css +0 -9
- package/src/ui/network-toolbar.tsx +0 -40
- package/src/ui/panel.module.css +0 -61
- package/src/ui/panel.tsx +0 -201
- package/src/ui/tanstack-query.tsx +0 -197
- package/src/ui/utils.ts +0 -89
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createNanoEvents } from 'nanoevents';
|
|
2
|
+
import { getWebSocketInterceptor } from './websocket-interceptor';
|
|
3
|
+
import {
|
|
4
|
+
WebSocketEvent,
|
|
5
|
+
WebSocketEventMap,
|
|
6
|
+
} from '../../shared/websocket-events';
|
|
7
|
+
|
|
8
|
+
type NanoEventsMap = {
|
|
9
|
+
[K in keyof WebSocketEventMap]: (data: WebSocketEventMap[K]) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type WebSocketInspector = {
|
|
13
|
+
enable: () => void;
|
|
14
|
+
disable: () => void;
|
|
15
|
+
isEnabled: () => boolean;
|
|
16
|
+
dispose: () => void;
|
|
17
|
+
on: <TEventType extends keyof WebSocketEventMap>(
|
|
18
|
+
event: TEventType,
|
|
19
|
+
callback: (data: WebSocketEventMap[TEventType]) => void
|
|
20
|
+
) => () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const getWebSocketInspector = (): WebSocketInspector => {
|
|
24
|
+
const eventEmitter = createNanoEvents<NanoEventsMap>();
|
|
25
|
+
const socketUrlMap = new Map<number, string>();
|
|
26
|
+
const webSocketInterceptor = getWebSocketInterceptor();
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
enable: () => {
|
|
30
|
+
webSocketInterceptor.setConnectCallback(
|
|
31
|
+
(
|
|
32
|
+
url: string,
|
|
33
|
+
protocols: string[] | null,
|
|
34
|
+
options: string[],
|
|
35
|
+
socketId: number
|
|
36
|
+
) => {
|
|
37
|
+
socketUrlMap.set(socketId, url);
|
|
38
|
+
const event: WebSocketEvent = {
|
|
39
|
+
type: 'websocket-connect',
|
|
40
|
+
url,
|
|
41
|
+
socketId,
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
protocols,
|
|
44
|
+
options,
|
|
45
|
+
};
|
|
46
|
+
eventEmitter.emit('websocket-connect', event);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
webSocketInterceptor.setCloseCallback(
|
|
51
|
+
(code: number | null, reason: string | null, socketId: number) => {
|
|
52
|
+
const url = socketUrlMap.get(socketId);
|
|
53
|
+
|
|
54
|
+
if (!url) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const event: WebSocketEvent = {
|
|
59
|
+
type: 'websocket-close',
|
|
60
|
+
url,
|
|
61
|
+
socketId,
|
|
62
|
+
timestamp: Date.now(),
|
|
63
|
+
code: code || 0,
|
|
64
|
+
reason: reason || undefined,
|
|
65
|
+
};
|
|
66
|
+
eventEmitter.emit('websocket-close', event);
|
|
67
|
+
socketUrlMap.delete(socketId);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
webSocketInterceptor.setOnMessageCallback(
|
|
72
|
+
(data: string, socketId: number) => {
|
|
73
|
+
const url = socketUrlMap.get(socketId);
|
|
74
|
+
|
|
75
|
+
if (!url) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const event: WebSocketEvent = {
|
|
80
|
+
type: 'websocket-message-received',
|
|
81
|
+
url,
|
|
82
|
+
socketId,
|
|
83
|
+
timestamp: Date.now(),
|
|
84
|
+
data,
|
|
85
|
+
messageType: typeof data === 'string' ? 'text' : 'binary',
|
|
86
|
+
};
|
|
87
|
+
eventEmitter.emit('websocket-message-received', event);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
webSocketInterceptor.setOnErrorCallback(
|
|
92
|
+
(error: string, socketId: number) => {
|
|
93
|
+
const url = socketUrlMap.get(socketId);
|
|
94
|
+
|
|
95
|
+
if (!url) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const event: WebSocketEvent = {
|
|
100
|
+
type: 'websocket-error',
|
|
101
|
+
url,
|
|
102
|
+
socketId,
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
error,
|
|
105
|
+
};
|
|
106
|
+
eventEmitter.emit('websocket-error', event);
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
webSocketInterceptor.setSendCallback((data: string, socketId: number) => {
|
|
111
|
+
const url = socketUrlMap.get(socketId);
|
|
112
|
+
|
|
113
|
+
if (!url) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const event: WebSocketEvent = {
|
|
118
|
+
type: 'websocket-message-sent',
|
|
119
|
+
url,
|
|
120
|
+
socketId,
|
|
121
|
+
timestamp: Date.now(),
|
|
122
|
+
data,
|
|
123
|
+
messageType: typeof data === 'string' ? 'text' : 'binary',
|
|
124
|
+
};
|
|
125
|
+
eventEmitter.emit('websocket-message-sent', event);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
webSocketInterceptor.setOnOpenCallback((socketId: number) => {
|
|
129
|
+
const url = socketUrlMap.get(socketId);
|
|
130
|
+
|
|
131
|
+
if (!url) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const event: WebSocketEvent = {
|
|
136
|
+
type: 'websocket-open',
|
|
137
|
+
url,
|
|
138
|
+
socketId,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
};
|
|
141
|
+
eventEmitter.emit('websocket-open', event);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
webSocketInterceptor.setOnCloseCallback(
|
|
145
|
+
(error: { code: number; reason?: string }, socketId: number) => {
|
|
146
|
+
const url = socketUrlMap.get(socketId);
|
|
147
|
+
|
|
148
|
+
if (!url) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const event: WebSocketEvent = {
|
|
153
|
+
type: 'websocket-close',
|
|
154
|
+
url,
|
|
155
|
+
socketId,
|
|
156
|
+
timestamp: Date.now(),
|
|
157
|
+
code: error.code,
|
|
158
|
+
reason: error.reason,
|
|
159
|
+
};
|
|
160
|
+
eventEmitter.emit('websocket-close', event);
|
|
161
|
+
socketUrlMap.delete(socketId);
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
webSocketInterceptor.enableInterception();
|
|
166
|
+
},
|
|
167
|
+
disable: () => {
|
|
168
|
+
webSocketInterceptor.disableInterception();
|
|
169
|
+
},
|
|
170
|
+
isEnabled: () => webSocketInterceptor.isInterceptorEnabled(),
|
|
171
|
+
dispose: () => {
|
|
172
|
+
eventEmitter.events = {};
|
|
173
|
+
socketUrlMap.clear();
|
|
174
|
+
},
|
|
175
|
+
on: <TEventType extends keyof WebSocketEventMap>(
|
|
176
|
+
event: TEventType,
|
|
177
|
+
callback: (data: WebSocketEventMap[TEventType]) => void
|
|
178
|
+
) => eventEmitter.on(event, callback as NanoEventsMap[TEventType]),
|
|
179
|
+
};
|
|
180
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import WebSocketInterceptor from 'react-native/Libraries/WebSocket/WebSocketInterceptor';
|
|
3
|
+
|
|
4
|
+
export interface WebSocketInterceptor {
|
|
5
|
+
/**
|
|
6
|
+
* Invoked when RCTWebSocketModule.close(...) is called.
|
|
7
|
+
*/
|
|
8
|
+
setCloseCallback(
|
|
9
|
+
callback: (
|
|
10
|
+
code: number | null,
|
|
11
|
+
reason: string | null,
|
|
12
|
+
socketId: number
|
|
13
|
+
) => void
|
|
14
|
+
): void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Invoked when RCTWebSocketModule.send(...) or sendBinary(...) is called.
|
|
18
|
+
*/
|
|
19
|
+
setSendCallback(callback: (data: string, socketId: number) => void): void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Invoked when RCTWebSocketModule.connect(...) is called.
|
|
23
|
+
*/
|
|
24
|
+
setConnectCallback(
|
|
25
|
+
callback: (
|
|
26
|
+
url: string,
|
|
27
|
+
protocols: string[] | null,
|
|
28
|
+
options: string[],
|
|
29
|
+
socketId: number
|
|
30
|
+
) => void
|
|
31
|
+
): void;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Invoked when event "websocketOpen" happens.
|
|
35
|
+
*/
|
|
36
|
+
setOnOpenCallback(callback: (socketId: number) => void): void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Invoked when event "websocketMessage" happens.
|
|
40
|
+
*/
|
|
41
|
+
setOnMessageCallback(
|
|
42
|
+
callback: (data: string, socketId: number) => void
|
|
43
|
+
): void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Invoked when event "websocketFailed" happens.
|
|
47
|
+
*/
|
|
48
|
+
setOnErrorCallback(callback: (error: string, socketId: number) => void): void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Invoked when event "websocketClosed" happens.
|
|
52
|
+
*/
|
|
53
|
+
setOnCloseCallback(
|
|
54
|
+
callback: (
|
|
55
|
+
error: { code: number; reason?: string },
|
|
56
|
+
socketId: number
|
|
57
|
+
) => void
|
|
58
|
+
): void;
|
|
59
|
+
|
|
60
|
+
isInterceptorEnabled(): boolean;
|
|
61
|
+
enableInterception(): void;
|
|
62
|
+
disableInterception(): void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface WebSocketInterceptorPreRN079 {
|
|
66
|
+
/**
|
|
67
|
+
* Invoked when RCTWebSocketModule.close(...) is called.
|
|
68
|
+
*/
|
|
69
|
+
setCloseCallback(
|
|
70
|
+
callback: (
|
|
71
|
+
code: number | null,
|
|
72
|
+
reason: string | null,
|
|
73
|
+
socketId: number
|
|
74
|
+
) => void
|
|
75
|
+
): void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Invoked when RCTWebSocketModule.send(...) or sendBinary(...) is called.
|
|
79
|
+
*/
|
|
80
|
+
setSendCallback(callback: (data: string, socketId: number) => void): void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Invoked when RCTWebSocketModule.connect(...) is called.
|
|
84
|
+
*/
|
|
85
|
+
setConnectCallback(
|
|
86
|
+
callback: (
|
|
87
|
+
url: string,
|
|
88
|
+
protocols: string[] | null,
|
|
89
|
+
options: string[],
|
|
90
|
+
socketId: number
|
|
91
|
+
) => void
|
|
92
|
+
): void;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Invoked when event "websocketOpen" happens.
|
|
96
|
+
*/
|
|
97
|
+
setOnOpenCallback(callback: (socketId: number) => void): void;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Invoked when event "websocketMessage" happens.
|
|
101
|
+
*/
|
|
102
|
+
setOnMessageCallback(
|
|
103
|
+
callback: (socketId: number, data: string) => void
|
|
104
|
+
): void;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Invoked when event "websocketFailed" happens.
|
|
108
|
+
*/
|
|
109
|
+
setOnErrorCallback(callback: (socketId: number, error: string) => void): void;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Invoked when event "websocketClosed" happens.
|
|
113
|
+
*/
|
|
114
|
+
setOnCloseCallback(
|
|
115
|
+
callback: (
|
|
116
|
+
socketId: number,
|
|
117
|
+
error: { code: number; reason?: string }
|
|
118
|
+
) => void
|
|
119
|
+
): void;
|
|
120
|
+
|
|
121
|
+
isInterceptorEnabled(): boolean;
|
|
122
|
+
enableInterception(): void;
|
|
123
|
+
disableInterception(): void;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const getWebSocketInterceptor = (): WebSocketInterceptor => {
|
|
127
|
+
/**
|
|
128
|
+
* Note: RN 0.79 changed the order of the arguments.
|
|
129
|
+
* @see https://github.com/facebook/react-native/commit/d2adb976abebcb0f38750903d98fbb5a3f50924b
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
if (Platform.constants.reactNativeVersion.minor >= 79) {
|
|
133
|
+
return WebSocketInterceptor as WebSocketInterceptor;
|
|
134
|
+
} else {
|
|
135
|
+
const WebSocketInterceptorPreRN079 =
|
|
136
|
+
WebSocketInterceptor as WebSocketInterceptorPreRN079;
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
...WebSocketInterceptorPreRN079,
|
|
140
|
+
setOnMessageCallback: (
|
|
141
|
+
callback: (data: string, socketId: number) => void
|
|
142
|
+
) => {
|
|
143
|
+
WebSocketInterceptorPreRN079.setOnMessageCallback((socketId, data) => {
|
|
144
|
+
callback(data, socketId);
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
setOnCloseCallback: (
|
|
148
|
+
callback: (
|
|
149
|
+
error: { code: number; reason?: string },
|
|
150
|
+
socketId: number
|
|
151
|
+
) => void
|
|
152
|
+
) => {
|
|
153
|
+
WebSocketInterceptorPreRN079.setOnCloseCallback((error, socketId) => {
|
|
154
|
+
callback(socketId, error);
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
setOnErrorCallback: (
|
|
158
|
+
callback: (error: string, socketId: number) => void
|
|
159
|
+
) => {
|
|
160
|
+
WebSocketInterceptorPreRN079.setOnErrorCallback((error, socketId) => {
|
|
161
|
+
callback(socketId, error);
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
} as WebSocketInterceptor;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { RozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
2
|
+
import { WebSocketEventMap } from './websocket-events';
|
|
3
|
+
import { SSEEventMap } from './sse-events';
|
|
4
|
+
|
|
5
|
+
export type HttpHeaders = Record<string, string>;
|
|
6
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
|
|
7
|
+
|
|
8
|
+
export type RequestId = string;
|
|
9
|
+
export type Timestamp = number;
|
|
10
|
+
|
|
11
|
+
export type Request = {
|
|
12
|
+
url: string;
|
|
13
|
+
method: HttpMethod;
|
|
14
|
+
headers: HttpHeaders;
|
|
15
|
+
postData?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type Response = {
|
|
19
|
+
url: string;
|
|
20
|
+
status: number;
|
|
21
|
+
statusText: string;
|
|
22
|
+
headers: HttpHeaders;
|
|
23
|
+
contentType: string;
|
|
24
|
+
size: number;
|
|
25
|
+
responseTime: Timestamp;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type Initiator = {
|
|
29
|
+
type: string;
|
|
30
|
+
url?: string;
|
|
31
|
+
lineNumber?: number;
|
|
32
|
+
columnNumber?: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type ResourceType = 'XHR' | 'Fetch' | 'Other';
|
|
36
|
+
|
|
37
|
+
export type NetworkActivityEventMap = {
|
|
38
|
+
// Control events
|
|
39
|
+
'network-enable': unknown;
|
|
40
|
+
'network-disable': unknown;
|
|
41
|
+
|
|
42
|
+
// Network request events
|
|
43
|
+
'request-sent': {
|
|
44
|
+
requestId: RequestId;
|
|
45
|
+
request: Request;
|
|
46
|
+
timestamp: Timestamp;
|
|
47
|
+
initiator: Initiator;
|
|
48
|
+
type: ResourceType;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
'response-received': {
|
|
52
|
+
requestId: RequestId;
|
|
53
|
+
timestamp: Timestamp;
|
|
54
|
+
type: ResourceType;
|
|
55
|
+
response: Response;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
'request-completed': {
|
|
59
|
+
requestId: RequestId;
|
|
60
|
+
timestamp: Timestamp;
|
|
61
|
+
duration: number;
|
|
62
|
+
size: number;
|
|
63
|
+
ttfb: number;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
'request-failed': {
|
|
67
|
+
requestId: RequestId;
|
|
68
|
+
timestamp: Timestamp;
|
|
69
|
+
type: ResourceType;
|
|
70
|
+
error: string;
|
|
71
|
+
canceled: boolean;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
'get-response-body': {
|
|
75
|
+
requestId: RequestId;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
'response-body': {
|
|
79
|
+
requestId: RequestId;
|
|
80
|
+
body: string | null;
|
|
81
|
+
};
|
|
82
|
+
} & WebSocketEventMap &
|
|
83
|
+
SSEEventMap;
|
|
84
|
+
|
|
85
|
+
export type NetworkActivityDevToolsClient =
|
|
86
|
+
RozeniteDevToolsClient<NetworkActivityEventMap>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Response } from './client';
|
|
2
|
+
|
|
3
|
+
export type SSEConnectionStatus = 'connecting' | 'open' | 'closed';
|
|
4
|
+
export type SSERequestId = string;
|
|
5
|
+
|
|
6
|
+
export type SSEOpenEvent = {
|
|
7
|
+
type: 'sse-open';
|
|
8
|
+
requestId: SSERequestId;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
response: Response;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type SSEMessageEvent = {
|
|
14
|
+
type: 'sse-message';
|
|
15
|
+
requestId: SSERequestId;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
data: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type SSEErrorEvent = {
|
|
21
|
+
type: 'sse-error';
|
|
22
|
+
requestId: SSERequestId;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
error: {
|
|
25
|
+
type: 'error' | 'timeout' | 'exception';
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type SSECloseEvent = {
|
|
31
|
+
type: 'sse-close';
|
|
32
|
+
requestId: SSERequestId;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type SSEEvent =
|
|
37
|
+
| SSEOpenEvent
|
|
38
|
+
| SSEMessageEvent
|
|
39
|
+
| SSEErrorEvent
|
|
40
|
+
| SSECloseEvent;
|
|
41
|
+
|
|
42
|
+
export type SSEEventMap = {
|
|
43
|
+
[K in SSEEvent['type']]: Extract<SSEEvent, { type: K }>;
|
|
44
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type WebSocketMessageType = 'text' | 'binary';
|
|
2
|
+
|
|
3
|
+
export type WebSocketConnectionStatus =
|
|
4
|
+
| 'connecting'
|
|
5
|
+
| 'open'
|
|
6
|
+
| 'closing'
|
|
7
|
+
| 'closed';
|
|
8
|
+
|
|
9
|
+
export type WebSocketConnectEvent = {
|
|
10
|
+
type: 'websocket-connect';
|
|
11
|
+
url: string;
|
|
12
|
+
socketId: number;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
protocols: string[] | null;
|
|
15
|
+
options: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type WebSocketOpenEvent = {
|
|
19
|
+
type: 'websocket-open';
|
|
20
|
+
url: string;
|
|
21
|
+
socketId: number;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type WebSocketCloseEvent = {
|
|
26
|
+
type: 'websocket-close';
|
|
27
|
+
url: string;
|
|
28
|
+
socketId: number;
|
|
29
|
+
timestamp: number;
|
|
30
|
+
code: number;
|
|
31
|
+
reason?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type WebSocketMessageSentEvent = {
|
|
35
|
+
type: 'websocket-message-sent';
|
|
36
|
+
url: string;
|
|
37
|
+
socketId: number;
|
|
38
|
+
timestamp: number;
|
|
39
|
+
data: string;
|
|
40
|
+
messageType: WebSocketMessageType;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type WebSocketMessageReceivedEvent = {
|
|
44
|
+
type: 'websocket-message-received';
|
|
45
|
+
url: string;
|
|
46
|
+
socketId: number;
|
|
47
|
+
timestamp: number;
|
|
48
|
+
data: string;
|
|
49
|
+
messageType: WebSocketMessageType;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type WebSocketErrorEvent = {
|
|
53
|
+
type: 'websocket-error';
|
|
54
|
+
url: string;
|
|
55
|
+
socketId: number;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
error: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type WebSocketConnectionStatusChangedEvent = {
|
|
61
|
+
type: 'websocket-connection-status-changed';
|
|
62
|
+
url: string;
|
|
63
|
+
socketId: number;
|
|
64
|
+
timestamp: number;
|
|
65
|
+
status: WebSocketConnectionStatus;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type WebSocketEvent =
|
|
69
|
+
| WebSocketConnectEvent
|
|
70
|
+
| WebSocketOpenEvent
|
|
71
|
+
| WebSocketCloseEvent
|
|
72
|
+
| WebSocketMessageSentEvent
|
|
73
|
+
| WebSocketMessageReceivedEvent
|
|
74
|
+
| WebSocketErrorEvent
|
|
75
|
+
| WebSocketConnectionStatusChangedEvent;
|
|
76
|
+
|
|
77
|
+
export type WebSocketEventMap = {
|
|
78
|
+
[K in WebSocketEvent['type']]: Extract<WebSocketEvent, { type: K }>;
|
|
79
|
+
};
|
package/src/ui/App.tsx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
2
|
+
import { NetworkActivityEventMap } from '../shared/client';
|
|
3
|
+
|
|
4
|
+
import { InspectorView } from './views/InspectorView';
|
|
5
|
+
import { LoadingView } from './views/LoadingView';
|
|
6
|
+
|
|
7
|
+
import './globals.css';
|
|
8
|
+
|
|
9
|
+
export default function NetworkActivityPanel() {
|
|
10
|
+
const client = useRozeniteDevToolsClient<NetworkActivityEventMap>({
|
|
11
|
+
pluginId: '@rozenite/network-activity-plugin',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (!client) {
|
|
15
|
+
return <LoadingView />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <InspectorView client={client} />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
|
|
4
|
+
import { cn } from '../utils/cn';
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default:
|
|
12
|
+
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
|
13
|
+
secondary:
|
|
14
|
+
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
15
|
+
destructive:
|
|
16
|
+
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
|
17
|
+
outline: 'text-foreground',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: 'default',
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export interface BadgeProps
|
|
27
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
28
|
+
VariantProps<typeof badgeVariants> {}
|
|
29
|
+
|
|
30
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
31
|
+
return (
|
|
32
|
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../utils/cn';
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
13
|
+
destructive:
|
|
14
|
+
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
15
|
+
outline:
|
|
16
|
+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
17
|
+
secondary:
|
|
18
|
+
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
19
|
+
ghost: 'hover:bg-gray-700 hover:text-gray-100',
|
|
20
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default: 'h-10 px-4 py-2',
|
|
24
|
+
sm: 'h-9 rounded-md px-3',
|
|
25
|
+
lg: 'h-11 rounded-md px-8',
|
|
26
|
+
icon: 'h-10 w-10',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: 'default',
|
|
31
|
+
size: 'default',
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export interface ButtonProps
|
|
37
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
38
|
+
VariantProps<typeof buttonVariants> {
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
43
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
44
|
+
const Comp = asChild ? Slot : 'button';
|
|
45
|
+
return (
|
|
46
|
+
<Comp
|
|
47
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
48
|
+
ref={ref}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
Button.displayName = 'Button';
|
|
55
|
+
|
|
56
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../utils/cn';
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
|
6
|
+
({ className, type, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
className={cn(
|
|
11
|
+
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
ref={ref}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
Input.displayName = 'Input';
|
|
21
|
+
|
|
22
|
+
export { Input };
|