@rozenite/network-activity-plugin 1.0.0-alpha.7 → 1.0.0-alpha.9
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.html +2 -2
- package/dist/assets/{App-CIflVb88.js → App-CA1Fbh0I.js} +12009 -10809
- package/dist/assets/{App-Czu6Vt2P.css → App-DoHQsY5s.css} +43 -0
- package/dist/event-source.cjs +22 -0
- package/dist/event-source.js +23 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/{network-inspector.d.ts → http/network-inspector.d.ts} +1 -1
- 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/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 +8 -4
- package/dist/src/shared/sse-events.d.ts +35 -0
- package/dist/src/shared/websocket-events.d.ts +60 -0
- package/dist/src/ui/components/Badge.d.ts +1 -1
- package/dist/src/ui/components/Button.d.ts +1 -1
- package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
- package/dist/src/ui/components/RequestList.d.ts +6 -26
- 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 +3 -6
- package/dist/src/ui/tabs/HeadersTab.d.ts +3 -15
- package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
- package/dist/src/ui/tabs/RequestTab.d.ts +2 -7
- package/dist/src/ui/tabs/ResponseTab.d.ts +2 -8
- package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
- package/dist/src/ui/tabs/TimingTab.d.ts +3 -5
- package/dist/src/ui/types.d.ts +6 -3
- package/dist/src/ui/utils/assert.d.ts +1 -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/useNetworkActivityDevTools.cjs +433 -34
- package/dist/useNetworkActivityDevTools.js +431 -34
- package/package.json +19 -8
- package/src/react-native/{network-inspector.ts → http/network-inspector.ts} +14 -32
- package/src/react-native/{xml-request.d.ts → http/xml-request.d.ts} +1 -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 +75 -1
- 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 +10 -4
- package/src/shared/sse-events.ts +44 -0
- package/src/shared/websocket-events.ts +79 -0
- package/src/ui/components/Badge.tsx +1 -1
- package/src/ui/components/Button.tsx +1 -1
- package/src/ui/components/Input.tsx +1 -1
- package/src/ui/components/JsonTree.tsx +13 -0
- package/src/ui/components/JsonTreeCopyableItem.tsx +33 -0
- package/src/ui/components/RequestList.tsx +42 -123
- package/src/ui/components/ScrollArea.tsx +1 -1
- package/src/ui/components/Separator.tsx +1 -1
- package/src/ui/components/SidePanel.tsx +323 -0
- package/src/ui/components/Tabs.tsx +2 -2
- package/src/ui/components/Toolbar.tsx +45 -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 +168 -179
- package/src/ui/tabs/HeadersTab.tsx +24 -31
- package/src/ui/tabs/MessagesTab.tsx +276 -0
- package/src/ui/tabs/RequestTab.tsx +28 -31
- package/src/ui/tabs/ResponseTab.tsx +10 -12
- package/src/ui/tabs/SSEMessagesTab.tsx +213 -0
- package/src/ui/tabs/TimingTab.tsx +33 -44
- package/src/ui/types.ts +6 -2
- package/src/ui/utils/assert.ts +5 -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 +24 -320
- package/tailwind.config.ts +3 -0
- package/vite.config.ts +12 -0
- /package/dist/src/react-native/{network-requests-registry.d.ts → http/network-requests-registry.d.ts} +0 -0
- /package/dist/src/react-native/{xhr-interceptor.d.ts → http/xhr-interceptor.d.ts} +0 -0
- /package/dist/src/ui/{utils.d.ts → utils/cn.d.ts} +0 -0
- /package/src/react-native/{network-requests-registry.ts → http/network-requests-registry.ts} +0 -0
- /package/src/react-native/{xhr-interceptor.ts → http/xhr-interceptor.ts} +0 -0
- /package/src/ui/{utils.ts → utils/cn.ts} +0 -0
|
@@ -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
|
+
};
|
package/src/shared/client.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
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';
|
|
2
7
|
|
|
3
8
|
export type RequestId = string;
|
|
4
9
|
export type Timestamp = number;
|
|
5
10
|
|
|
6
11
|
export type Request = {
|
|
7
12
|
url: string;
|
|
8
|
-
method:
|
|
9
|
-
headers:
|
|
13
|
+
method: HttpMethod;
|
|
14
|
+
headers: HttpHeaders;
|
|
10
15
|
postData?: string;
|
|
11
16
|
};
|
|
12
17
|
|
|
@@ -14,7 +19,7 @@ export type Response = {
|
|
|
14
19
|
url: string;
|
|
15
20
|
status: number;
|
|
16
21
|
statusText: string;
|
|
17
|
-
headers:
|
|
22
|
+
headers: HttpHeaders;
|
|
18
23
|
contentType: string;
|
|
19
24
|
size: number;
|
|
20
25
|
responseTime: Timestamp;
|
|
@@ -74,7 +79,8 @@ export type NetworkActivityEventMap = {
|
|
|
74
79
|
requestId: RequestId;
|
|
75
80
|
body: string | null;
|
|
76
81
|
};
|
|
77
|
-
}
|
|
82
|
+
} & WebSocketEventMap &
|
|
83
|
+
SSEEventMap;
|
|
78
84
|
|
|
79
85
|
export type NetworkActivityDevToolsClient =
|
|
80
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
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
3
|
|
|
4
|
-
import { cn } from '../utils';
|
|
4
|
+
import { cn } from '../utils/cn';
|
|
5
5
|
|
|
6
6
|
const badgeVariants = cva(
|
|
7
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',
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { Slot } from '@radix-ui/react-slot';
|
|
3
3
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
|
-
import { cn } from '../utils';
|
|
5
|
+
import { cn } from '../utils/cn';
|
|
6
6
|
|
|
7
7
|
const buttonVariants = cva(
|
|
8
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',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JSONTree } from 'react-json-tree';
|
|
2
|
+
import { JsonTreeCopyableItem } from './JsonTreeCopyableItem';
|
|
2
3
|
|
|
3
4
|
export type JsonTreeProps = {
|
|
4
5
|
data: unknown;
|
|
@@ -32,6 +33,18 @@ export const JsonTree = ({
|
|
|
32
33
|
}}
|
|
33
34
|
invertTheme={false}
|
|
34
35
|
shouldExpandNodeInitially={shouldExpandNodeInitially}
|
|
36
|
+
// For objects and arrays
|
|
37
|
+
getItemString={(_type, data, itemType, itemString) => (
|
|
38
|
+
<JsonTreeCopyableItem getCopyableValue={() => JSON.stringify(data, null, 2)}>
|
|
39
|
+
<>{itemType} {itemString}</>
|
|
40
|
+
</JsonTreeCopyableItem>
|
|
41
|
+
)}
|
|
42
|
+
// For primitives
|
|
43
|
+
valueRenderer={(valueAsString, value) => (
|
|
44
|
+
<JsonTreeCopyableItem getCopyableValue={() => String(value)} className="ml-2">
|
|
45
|
+
{String(valueAsString)}
|
|
46
|
+
</JsonTreeCopyableItem>
|
|
47
|
+
)}
|
|
35
48
|
/>
|
|
36
49
|
);
|
|
37
50
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Check, Copy } from "lucide-react";
|
|
2
|
+
import { MouseEvent, PropsWithChildren } from "react";
|
|
3
|
+
import { useCopyToClipboard } from "../hooks/useCopyToClipboard";
|
|
4
|
+
import { cn } from "../utils/cn";
|
|
5
|
+
|
|
6
|
+
type JsonTreeCopyableItemProps = PropsWithChildren<{
|
|
7
|
+
getCopyableValue: () => string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}>;
|
|
10
|
+
|
|
11
|
+
export const JsonTreeCopyableItem = ({ children, getCopyableValue, className }: JsonTreeCopyableItemProps) => {
|
|
12
|
+
const { isCopied, copy } = useCopyToClipboard();
|
|
13
|
+
|
|
14
|
+
const handleCopy = (event: MouseEvent) => {
|
|
15
|
+
event.stopPropagation();
|
|
16
|
+
|
|
17
|
+
copy(getCopyableValue());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Icon = isCopied ? Check : Copy;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<span className={cn('inline-block group', className)}>
|
|
24
|
+
{children}
|
|
25
|
+
<div
|
|
26
|
+
className="inline-block cursor-pointer opacity-0 group-hover:opacity-100 text-gray-500 hover:text-gray-300 transition-all p-2 -m-2 ml-0 translate-y-0.75"
|
|
27
|
+
onClick={handleCopy}
|
|
28
|
+
>
|
|
29
|
+
<Icon className='h-4 w-4' />
|
|
30
|
+
</div>
|
|
31
|
+
</span>
|
|
32
|
+
);
|
|
33
|
+
}
|