@teardown/react-native 1.0.12 → 1.0.13
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/package.json +25 -26
- package/src/components/debugger-ui.tsx +0 -237
- package/src/components/index.ts +0 -1
- package/src/components/teardown-logo.tsx +0 -16
- package/src/containers/index.ts +0 -1
- package/src/containers/teardown.container.tsx +0 -40
- package/src/debugger.ts +0 -72
- package/src/index.ts +0 -5
- package/src/plugins/http.plugin.ts +0 -171
- package/src/plugins/index.ts +0 -3
- package/src/plugins/logging.plugin.ts +0 -52
- package/src/plugins/websocket.plugin.ts +0 -130
- package/src/services/index.ts +0 -1
- package/src/services/teardown.service.ts +0 -27
- package/src/teardown.client.ts +0 -74
package/package.json
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
2
|
+
"name": "@teardown/react-native",
|
|
3
|
+
"version": "1.0.13",
|
|
4
|
+
"description": "",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
7
|
+
"build": "tsc --declaration",
|
|
8
|
+
"dev": "tsc --declaration --watch"
|
|
9
|
+
},
|
|
10
|
+
"main": "./src/index.ts",
|
|
11
|
+
"types": "./src/index.ts",
|
|
12
|
+
"files": ["README.md", "./src/**/*"],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@teardown/logger": "link:@teardown/logger",
|
|
15
|
+
"@teardown/util": "link:@teardown/util",
|
|
16
|
+
"@teardown/websocket": "link:@teardown/websocket",
|
|
17
|
+
"react": "^18.3.1",
|
|
18
|
+
"react-native": "^0.74.3"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "^18.3.1",
|
|
22
|
+
"react-native-gesture-handler": "^2.17.1",
|
|
23
|
+
"react-native-safe-area-context": "^4.10.8",
|
|
24
|
+
"react-native-svg": "^15.7.1",
|
|
25
|
+
"typescript": "^5.6.3"
|
|
26
|
+
}
|
|
28
27
|
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import {FunctionComponent, useEffect, useRef, useState} from 'react';
|
|
2
|
-
import {DevSettings, Pressable, StyleSheet, Text, View, ViewStyle,} from 'react-native';
|
|
3
|
-
import {EdgeInsets, useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
4
|
-
import {TeardownLogo} from './teardown-logo';
|
|
5
|
-
import {GestureHandlerRootView} from 'react-native-gesture-handler';
|
|
6
|
-
import {
|
|
7
|
-
BottomSheet,
|
|
8
|
-
BottomSheetCloseIcon,
|
|
9
|
-
BottomSheetContent,
|
|
10
|
-
BottomSheetHandleProps,
|
|
11
|
-
BottomSheetHeader,
|
|
12
|
-
BottomSheetModalProvider,
|
|
13
|
-
BottomSheetTitle,
|
|
14
|
-
BottomSheetView,
|
|
15
|
-
Button
|
|
16
|
-
} from '@teardown/react-native-ui';
|
|
17
|
-
import {TeardownService} from "../services/teardown.service";
|
|
18
|
-
import {DebuggerStatus} from "../debugger";
|
|
19
|
-
|
|
20
|
-
export type DebuggerUiPosition =
|
|
21
|
-
| 'top-left'
|
|
22
|
-
| 'top-right'
|
|
23
|
-
| 'bottom-left'
|
|
24
|
-
| 'bottom-right'
|
|
25
|
-
| 'center-left'
|
|
26
|
-
| 'center-right';
|
|
27
|
-
|
|
28
|
-
export type DebuggerUiOptions = {
|
|
29
|
-
enabled?: boolean;
|
|
30
|
-
position?: DebuggerUiPosition;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export const DebuggerUi: FunctionComponent<DebuggerUiOptions> = props => {
|
|
34
|
-
const {enabled = true} = props;
|
|
35
|
-
|
|
36
|
-
return null;
|
|
37
|
-
|
|
38
|
-
if (!__DEV__) {
|
|
39
|
-
// never render the debugger ui in any mode apart from __DEV__ == true "development"
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const isNotEnabled = !enabled;
|
|
44
|
-
|
|
45
|
-
if (isNotEnabled) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return <DebuggerUiEnabled {...props} />;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const DebuggerUiEnabled: FunctionComponent<DebuggerUiOptions> = props => {
|
|
53
|
-
const {position} = props;
|
|
54
|
-
|
|
55
|
-
const safeAreaInsets = useSafeAreaInsets();
|
|
56
|
-
const bottomSheetRef = useRef<BottomSheet>(null);
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<>
|
|
60
|
-
<View style={styles.container} pointerEvents={'box-none'}>
|
|
61
|
-
<GestureHandlerRootView>
|
|
62
|
-
<BottomSheetModalProvider>
|
|
63
|
-
<Pressable
|
|
64
|
-
onPress={() => {
|
|
65
|
-
// bottomSheetRef.current?.snapToIndex(0);
|
|
66
|
-
}}
|
|
67
|
-
style={[
|
|
68
|
-
styles.orb,
|
|
69
|
-
getOrbPosition(safeAreaInsets, position ?? 'center-right'),
|
|
70
|
-
]}>
|
|
71
|
-
<TeardownLogo height={20} width={20} />
|
|
72
|
-
</Pressable>
|
|
73
|
-
|
|
74
|
-
<BottomSheet
|
|
75
|
-
sheetRef={bottomSheetRef}
|
|
76
|
-
handleComponent={handleProps => (
|
|
77
|
-
<DebuggerStatusHandleComponent {...handleProps} />
|
|
78
|
-
)}>
|
|
79
|
-
<BottomSheetCloseIcon />
|
|
80
|
-
<BottomSheetView
|
|
81
|
-
className={''}
|
|
82
|
-
style={{
|
|
83
|
-
paddingBottom: safeAreaInsets.bottom + 16,
|
|
84
|
-
}}>
|
|
85
|
-
<BottomSheetHeader>
|
|
86
|
-
<BottomSheetTitle>Debugger</BottomSheetTitle>
|
|
87
|
-
</BottomSheetHeader>
|
|
88
|
-
<BottomSheetContent>
|
|
89
|
-
<Button
|
|
90
|
-
onPress={() => {
|
|
91
|
-
DevSettings.reload('Teardown reconnect');
|
|
92
|
-
}}>
|
|
93
|
-
Reconnect debugger
|
|
94
|
-
</Button>
|
|
95
|
-
</BottomSheetContent>
|
|
96
|
-
</BottomSheetView>
|
|
97
|
-
</BottomSheet>
|
|
98
|
-
</BottomSheetModalProvider>
|
|
99
|
-
</GestureHandlerRootView>
|
|
100
|
-
|
|
101
|
-
{/*<View style={styles.half_height}>*/}
|
|
102
|
-
{/* */}
|
|
103
|
-
{/*</View>*/}
|
|
104
|
-
</View>
|
|
105
|
-
</>
|
|
106
|
-
);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const DebuggerStatusHandleComponent: FunctionComponent<
|
|
110
|
-
BottomSheetHandleProps
|
|
111
|
-
> = () => {
|
|
112
|
-
const {client} = TeardownService.useState();
|
|
113
|
-
|
|
114
|
-
const [debuggerStatus, setDebuggerStatus] = useState<DebuggerStatus | null>(
|
|
115
|
-
client.debugger?.getStatus() ?? null,
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
const listener = client.debugger?.emitter.on("CONNECTION_STATUS_CHANGED", (event) => {
|
|
120
|
-
const { payload } = event;
|
|
121
|
-
setDebuggerStatus(event.payload.status);
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
return () => {
|
|
125
|
-
listener?.remove();
|
|
126
|
-
};
|
|
127
|
-
}, [client]);
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<View
|
|
131
|
-
style={[
|
|
132
|
-
{
|
|
133
|
-
backgroundColor: getColorForDebuggerStatus(
|
|
134
|
-
debuggerStatus,
|
|
135
|
-
),
|
|
136
|
-
},
|
|
137
|
-
styles.debugger_status,
|
|
138
|
-
]}>
|
|
139
|
-
<Text style={styles.debugger_status_text}>{debuggerStatus}</Text>
|
|
140
|
-
</View>
|
|
141
|
-
);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const styles = StyleSheet.create({
|
|
145
|
-
debugger_status: {
|
|
146
|
-
flex: 1,
|
|
147
|
-
borderTopLeftRadius: 15,
|
|
148
|
-
borderTopRightRadius: 15,
|
|
149
|
-
padding: 4,
|
|
150
|
-
},
|
|
151
|
-
debugger_status_text: {
|
|
152
|
-
color: 'white',
|
|
153
|
-
fontSize: 12,
|
|
154
|
-
textAlign: 'center',
|
|
155
|
-
fontWeight: 'bold',
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
container: {
|
|
159
|
-
...StyleSheet.absoluteFillObject,
|
|
160
|
-
backgroundColor: 'white',
|
|
161
|
-
},
|
|
162
|
-
orb: {
|
|
163
|
-
height: 40,
|
|
164
|
-
width: 40,
|
|
165
|
-
backgroundColor: 'hsl(240 5% 6%)',
|
|
166
|
-
borderRadius: 40,
|
|
167
|
-
position: 'absolute',
|
|
168
|
-
justifyContent: 'center',
|
|
169
|
-
alignItems: 'center',
|
|
170
|
-
},
|
|
171
|
-
half_height: {
|
|
172
|
-
height: '50%',
|
|
173
|
-
backgroundColor: '#e1e1e1',
|
|
174
|
-
position: 'absolute',
|
|
175
|
-
left: 0,
|
|
176
|
-
right: 0,
|
|
177
|
-
bottom: 0,
|
|
178
|
-
},
|
|
179
|
-
logo: {
|
|
180
|
-
height: 20,
|
|
181
|
-
width: 20,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
const getColorForDebuggerStatus = (status: DebuggerStatus | null) => {
|
|
186
|
-
switch (status) {
|
|
187
|
-
case 'CONNECTING':
|
|
188
|
-
return 'yellow';
|
|
189
|
-
case 'CONNECTED':
|
|
190
|
-
return 'green';
|
|
191
|
-
case 'DISCONNECTED':
|
|
192
|
-
return 'red';
|
|
193
|
-
case 'FAILED':
|
|
194
|
-
return 'red';
|
|
195
|
-
default:
|
|
196
|
-
return 'gray';
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const getOrbPosition = (
|
|
201
|
-
edgeInsets: EdgeInsets,
|
|
202
|
-
position: DebuggerUiPosition,
|
|
203
|
-
): ViewStyle => {
|
|
204
|
-
const DEFAULT_PADDING = 16;
|
|
205
|
-
switch (position) {
|
|
206
|
-
case 'top-left':
|
|
207
|
-
return {
|
|
208
|
-
top: DEFAULT_PADDING + edgeInsets.top,
|
|
209
|
-
left: DEFAULT_PADDING,
|
|
210
|
-
};
|
|
211
|
-
case 'top-right':
|
|
212
|
-
return {
|
|
213
|
-
top: DEFAULT_PADDING + edgeInsets.top,
|
|
214
|
-
right: DEFAULT_PADDING,
|
|
215
|
-
};
|
|
216
|
-
case 'bottom-left':
|
|
217
|
-
return {
|
|
218
|
-
bottom: DEFAULT_PADDING + edgeInsets.bottom,
|
|
219
|
-
left: DEFAULT_PADDING,
|
|
220
|
-
};
|
|
221
|
-
case 'bottom-right':
|
|
222
|
-
return {
|
|
223
|
-
bottom: DEFAULT_PADDING + edgeInsets.bottom,
|
|
224
|
-
right: DEFAULT_PADDING,
|
|
225
|
-
};
|
|
226
|
-
case 'center-left':
|
|
227
|
-
return {
|
|
228
|
-
top: '50%',
|
|
229
|
-
left: DEFAULT_PADDING,
|
|
230
|
-
};
|
|
231
|
-
case 'center-right':
|
|
232
|
-
return {
|
|
233
|
-
top: '50%',
|
|
234
|
-
right: DEFAULT_PADDING,
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
};
|
package/src/components/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './debugger-ui';
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// import type {FunctionComponent} from 'react';
|
|
2
|
-
// import Svg, {Path, SvgProps} from "react-native-svg";
|
|
3
|
-
//
|
|
4
|
-
// export type TeardownLogoProps = SvgProps
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// export const TeardownLogo: FunctionComponent<TeardownLogoProps> = (props) => {
|
|
8
|
-
// const {} = props;
|
|
9
|
-
// return (
|
|
10
|
-
// <Svg width="350" height="350" viewBox="0 0 350 350" fill="none" {...props}>
|
|
11
|
-
// <Path fillRule="evenodd" clipRule="evenodd"
|
|
12
|
-
// d="M175.09 345L300 218.574L256.859 174.909L299.821 131.426L174.91 5.00001L50 131.426L93.141 175.091L50.179 218.574L175.09 345ZM114.879 197.093L93.656 218.574L175.09 300.995L256.523 218.574L235.12 196.911L174.91 257.853L114.879 197.093ZM213.382 174.909L174.91 213.848L136.618 175.091L175.09 136.152L213.382 174.909ZM235.12 152.907L175.09 92.147L114.879 153.089L93.477 131.426L174.91 49.005L256.344 131.426L235.12 152.907Z"
|
|
13
|
-
// fill="white"/>
|
|
14
|
-
// </Svg>
|
|
15
|
-
// );
|
|
16
|
-
// }
|
package/src/containers/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './teardown.container';
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import {FunctionComponent, PropsWithChildren} from 'react';
|
|
2
|
-
import {PluginTuple, TeardownClient} from '../teardown.client';
|
|
3
|
-
import {DebuggerUiOptions} from '../components/debugger-ui';
|
|
4
|
-
import {SafeAreaProvider} from 'react-native-safe-area-context';
|
|
5
|
-
|
|
6
|
-
export type TeardownContainerOptions = {
|
|
7
|
-
debugger: DebuggerUiOptions;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type TeardownContainerProps<T extends readonly PluginTuple[]> = PropsWithChildren<{
|
|
11
|
-
client: TeardownClient<T>;
|
|
12
|
-
options?: TeardownContainerOptions;
|
|
13
|
-
}>;
|
|
14
|
-
|
|
15
|
-
export const TeardownContainer: FunctionComponent<
|
|
16
|
-
TeardownContainerProps<any>
|
|
17
|
-
> = props => {
|
|
18
|
-
const {children, client, options} = props;
|
|
19
|
-
|
|
20
|
-
// const providedState = TeardownService.useProvidedState(client);
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
{children}
|
|
25
|
-
</>
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<SafeAreaProvider className={"flex-1 bg-white"}>
|
|
30
|
-
{/*<GestureHandlerRootView>*/}
|
|
31
|
-
{/* <BottomSheetModalProvider> /!* TODO: Move this into a theme provider/package *!/*/}
|
|
32
|
-
{/* <TeardownService.Provider value={providedState}>*/}
|
|
33
|
-
{/* {children}*/}
|
|
34
|
-
{/* <DebuggerUi {...options?.debugger} />*/}
|
|
35
|
-
{/* </TeardownService.Provider>*/}
|
|
36
|
-
{/* </BottomSheetModalProvider>*/}
|
|
37
|
-
{/*</GestureHandlerRootView>*/}
|
|
38
|
-
</SafeAreaProvider>
|
|
39
|
-
);
|
|
40
|
-
};
|
package/src/debugger.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {NativeModules, Platform} from 'react-native';
|
|
2
|
-
import {
|
|
3
|
-
ClientWebsocketEvents,
|
|
4
|
-
ConnectionEstablishedWebsocketEvent,
|
|
5
|
-
WebsocketClient,
|
|
6
|
-
WebsocketClientOptions,
|
|
7
|
-
WebsocketConnectionStatus,
|
|
8
|
-
} from '@teardown/websocket';
|
|
9
|
-
|
|
10
|
-
export type DebuggerStatus = WebsocketConnectionStatus;
|
|
11
|
-
|
|
12
|
-
export type DebuggerOptions = WebsocketClientOptions;
|
|
13
|
-
|
|
14
|
-
export class Debugger extends WebsocketClient<ClientWebsocketEvents> {
|
|
15
|
-
|
|
16
|
-
constructor(options?: DebuggerOptions) {
|
|
17
|
-
super(options);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public onEvent(event: ClientWebsocketEvents[keyof ClientWebsocketEvents]) {
|
|
21
|
-
this.logger.log('onEvent', event);
|
|
22
|
-
return event;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public onConnectionEstablished(event: ConnectionEstablishedWebsocketEvent) {
|
|
26
|
-
this.logger.log('Connection established', event);
|
|
27
|
-
|
|
28
|
-
this.client_id = event.client_id;
|
|
29
|
-
|
|
30
|
-
this.send('CLIENT_CONNECTION_ESTABLISHED', {
|
|
31
|
-
deviceName: '~~~--- device name here ---~~~',
|
|
32
|
-
platform: Platform.OS,
|
|
33
|
-
platformVersion: Platform.Version,
|
|
34
|
-
reactNativeVersion: Platform.constants.reactNativeVersion,
|
|
35
|
-
isDisableAnimations: Platform.constants.isDisableAnimations ?? false,
|
|
36
|
-
isTesting: Platform.constants.isTesting,
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
getHostFromUrl(url: string) {
|
|
41
|
-
const host = url.match(
|
|
42
|
-
/^(?:https?:\/\/)?(\[[^\]]+\]|[^/:\s]+)(?::\d+)?(?:[/?#]|$)/,
|
|
43
|
-
)?.[1];
|
|
44
|
-
|
|
45
|
-
if (typeof host !== 'string') {
|
|
46
|
-
throw new Error('Invalid URL - host not found');
|
|
47
|
-
}
|
|
48
|
-
return host;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
getHost() {
|
|
52
|
-
try {
|
|
53
|
-
// https://github.com/facebook/react-native/blob/2a7f969500cef73b621269299619ee1f0ee9521a/packages/react-native/src/private/specs/modules/NativeSourceCode.js#L16
|
|
54
|
-
const scriptURL = NativeModules?.SourceCode?.getConstants().scriptURL
|
|
55
|
-
if (typeof scriptURL !== "string") throw new Error("Invalid non-string URL")
|
|
56
|
-
console.log('scriptURL', scriptURL);
|
|
57
|
-
|
|
58
|
-
return this.getHostFromUrl(scriptURL)
|
|
59
|
-
} catch (error) {
|
|
60
|
-
const superHost = super.getHost();
|
|
61
|
-
this.logger.warn(`Failed to get host: "${error.message}" - Falling back to ${superHost}`);
|
|
62
|
-
return superHost;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public send<
|
|
67
|
-
Type extends keyof ClientWebsocketEvents,
|
|
68
|
-
Payload extends ClientWebsocketEvents[Type]['payload'],
|
|
69
|
-
>(type: Type, payload: Payload) {
|
|
70
|
-
super.send(type, payload);
|
|
71
|
-
}
|
|
72
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
// @ts-ignore
|
|
2
|
-
import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor';
|
|
3
|
-
import type {Plugin, TeardownClient} from '../teardown.client';
|
|
4
|
-
import {Logger} from '@teardown/logger';
|
|
5
|
-
import {Util} from '@teardown/util';
|
|
6
|
-
import type {HTTPRequestInfo, RequestMethod} from '@teardown/websocket';
|
|
7
|
-
|
|
8
|
-
interface ExtendedXMLHttpRequest extends XMLHttpRequest {
|
|
9
|
-
_id: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type HTTPPluginOptions = {
|
|
13
|
-
ignoreURLs?: RegExp[];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export class HTTPPlugin implements Plugin {
|
|
17
|
-
private logger = new Logger('HTTPPlugin');
|
|
18
|
-
private client: TeardownClient<any> | null = null;
|
|
19
|
-
private requests: Map<number, HTTPRequestInfo> = new Map();
|
|
20
|
-
private ignoreURLs: RegExp[];
|
|
21
|
-
|
|
22
|
-
constructor(options: HTTPPluginOptions = {}) {
|
|
23
|
-
this.ignoreURLs = options.ignoreURLs || [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
install(client: TeardownClient<any>): void {
|
|
27
|
-
this.client = client;
|
|
28
|
-
this.setupXHRInterceptor();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private setupXHRInterceptor(): void {
|
|
32
|
-
if (XHRInterceptor.isInterceptorEnabled()) {
|
|
33
|
-
this.logger.warn(
|
|
34
|
-
'XHRInterceptor is already enabled by another library. Disable it or run this plugin first when your app loads.',
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
XHRInterceptor.setOpenCallback(this.xhrOpenCallback);
|
|
39
|
-
XHRInterceptor.setRequestHeaderCallback(this.xhrRequestHeaderCallback);
|
|
40
|
-
XHRInterceptor.setSendCallback(this.xhrSendCallback);
|
|
41
|
-
XHRInterceptor.setResponseCallback(this.xhrResponseCallback);
|
|
42
|
-
|
|
43
|
-
XHRInterceptor.enableInterception();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
private shouldIgnoreURL(url: string): boolean {
|
|
47
|
-
return this.ignoreURLs.some(ignoreRegex => ignoreRegex.test(url));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private xhrOpenCallback = (
|
|
51
|
-
method: string,
|
|
52
|
-
url: string,
|
|
53
|
-
xhr: ExtendedXMLHttpRequest,
|
|
54
|
-
): void => {
|
|
55
|
-
if (this.shouldIgnoreURL(url)) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const requestId = Util.generateUUID();
|
|
60
|
-
|
|
61
|
-
const HTTPRequestInfo: HTTPRequestInfo = {
|
|
62
|
-
id: requestId,
|
|
63
|
-
type: 'XMLHttpRequest',
|
|
64
|
-
url,
|
|
65
|
-
method: method as RequestMethod,
|
|
66
|
-
requestHeaders: {'TD-Request-ID': requestId},
|
|
67
|
-
startTime: Date.now(),
|
|
68
|
-
updatedAt: Date.now(),
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
this.requests.set(xhr._id, HTTPRequestInfo);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
private xhrRequestHeaderCallback = (
|
|
75
|
-
header: string,
|
|
76
|
-
value: string,
|
|
77
|
-
xhr: ExtendedXMLHttpRequest,
|
|
78
|
-
): void => {
|
|
79
|
-
const request = this.requests.get(xhr._id);
|
|
80
|
-
if (request) {
|
|
81
|
-
request.requestHeaders[header] = value;
|
|
82
|
-
request.updatedAt = Date.now();
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
private xhrSendCallback = (data: any, xhr: ExtendedXMLHttpRequest): void => {
|
|
87
|
-
const request = this.requests.get(xhr._id);
|
|
88
|
-
if (request) {
|
|
89
|
-
xhr.setRequestHeader('TD-Request-ID', request.id);
|
|
90
|
-
|
|
91
|
-
request.dataSent = this.serializeRequestBody(data);
|
|
92
|
-
request.updatedAt = Date.now();
|
|
93
|
-
this.sendHTTPEvent(request);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
private serializeRequestBody(data: any): string {
|
|
98
|
-
if (typeof data === 'string') {
|
|
99
|
-
return data;
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
return JSON.stringify(data);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
this.logger.warn('Failed to stringify request body', error);
|
|
105
|
-
return '[Unable to serialize request body]';
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private xhrResponseCallback = (
|
|
110
|
-
status: number,
|
|
111
|
-
timeout: number,
|
|
112
|
-
response: string,
|
|
113
|
-
responseURL: string,
|
|
114
|
-
responseType: XMLHttpRequestResponseType,
|
|
115
|
-
xhr: ExtendedXMLHttpRequest,
|
|
116
|
-
): void => {
|
|
117
|
-
const request = this.requests.get(xhr._id);
|
|
118
|
-
if (request) {
|
|
119
|
-
request.status = status;
|
|
120
|
-
request.responseHeaders = this.parseResponseHeaders(
|
|
121
|
-
xhr.getAllResponseHeaders(),
|
|
122
|
-
);
|
|
123
|
-
request.response = this.parseResponseBody(response, responseType);
|
|
124
|
-
request.responseURL = responseURL;
|
|
125
|
-
request.responseType = responseType;
|
|
126
|
-
request.timeout = timeout;
|
|
127
|
-
request.endTime = Date.now();
|
|
128
|
-
request.updatedAt = Date.now();
|
|
129
|
-
|
|
130
|
-
this.sendHTTPEvent(request);
|
|
131
|
-
this.requests.delete(xhr._id);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
private parseResponseHeaders(headersString: string): Record<string, string> {
|
|
136
|
-
const headersObject: Record<string, string> = {};
|
|
137
|
-
if (headersString) {
|
|
138
|
-
const headerPairs = headersString.trim().split(/[\r\n]+/);
|
|
139
|
-
headerPairs.forEach(headerPair => {
|
|
140
|
-
const [key, value] = headerPair.split(': ');
|
|
141
|
-
headersObject[key] = value;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
return headersObject;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private parseResponseBody(
|
|
148
|
-
response: string,
|
|
149
|
-
responseType: XMLHttpRequestResponseType,
|
|
150
|
-
): string {
|
|
151
|
-
if (responseType === 'json') {
|
|
152
|
-
try {
|
|
153
|
-
return JSON.stringify(JSON.parse(response), null, 2);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
this.logger.warn('Failed to parse JSON response', error);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return response;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private sendHTTPEvent(httpRequestInfo: HTTPRequestInfo): void {
|
|
162
|
-
if (this.client?.debugger) {
|
|
163
|
-
this.client.debugger.send('NETWORK_HTTP_REQUEST', httpRequestInfo);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
disableInterception(): void {
|
|
168
|
-
XHRInterceptor.disableInterception();
|
|
169
|
-
this.requests.clear();
|
|
170
|
-
}
|
|
171
|
-
}
|
package/src/plugins/index.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import {Plugin, TeardownClient} from '../teardown.client';
|
|
2
|
-
|
|
3
|
-
const originalConsoleLog = console.log;
|
|
4
|
-
const originalConsoleWarn = console.warn;
|
|
5
|
-
const originalConsoleDebug = console.debug;
|
|
6
|
-
const originalConsoleError = console.error;
|
|
7
|
-
|
|
8
|
-
export class LoggingPlugin implements Plugin {
|
|
9
|
-
private client: TeardownClient<any> | null = null;
|
|
10
|
-
|
|
11
|
-
install(client: TeardownClient<any>): void {
|
|
12
|
-
this.client = client;
|
|
13
|
-
|
|
14
|
-
console.log = (...args: Parameters<typeof console.log>) => {
|
|
15
|
-
originalConsoleLog(...args);
|
|
16
|
-
|
|
17
|
-
this.client?.debugger?.send('CONSOLE_LOG', {
|
|
18
|
-
type: 'log',
|
|
19
|
-
args,
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
console.warn = (...args: Parameters<typeof console.warn>) => {
|
|
24
|
-
originalConsoleWarn(...args);
|
|
25
|
-
|
|
26
|
-
this.client?.debugger?.send('CONSOLE_LOG', {
|
|
27
|
-
type: 'warn',
|
|
28
|
-
args,
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
console.debug = (...args: Parameters<typeof console.debug>) => {
|
|
33
|
-
originalConsoleDebug(...args);
|
|
34
|
-
|
|
35
|
-
this.client?.debugger?.send('CONSOLE_LOG', {
|
|
36
|
-
type: 'debug',
|
|
37
|
-
args,
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
console.error = (...args: Parameters<typeof console.error>) => {
|
|
42
|
-
originalConsoleError(...args);
|
|
43
|
-
|
|
44
|
-
this.client?.debugger?.send('CONSOLE_LOG', {
|
|
45
|
-
type: 'error',
|
|
46
|
-
args,
|
|
47
|
-
});
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
uninstall() {}
|
|
52
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
// @ts-ignore
|
|
2
|
-
import WebSocketInterceptor from 'react-native/Libraries/WebSocket/WebSocketInterceptor';
|
|
3
|
-
|
|
4
|
-
import type {Plugin, TeardownClient} from '../teardown.client';
|
|
5
|
-
import {Logger} from '@teardown/logger';
|
|
6
|
-
import {Util} from '@teardown/util';
|
|
7
|
-
import {
|
|
8
|
-
WebSocketInfo,
|
|
9
|
-
WebSocketMessageInfo,
|
|
10
|
-
WebSocketCloseInfo,
|
|
11
|
-
} from '@teardown/websocket';
|
|
12
|
-
|
|
13
|
-
export class WebSocketPlugin implements Plugin {
|
|
14
|
-
private logger = new Logger('WebSocketPlugin');
|
|
15
|
-
private client: TeardownClient<any> | null = null;
|
|
16
|
-
private sockets: Map<string, WebSocketInfo> = new Map();
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
this.setupWebSocketInterceptor();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
install(client: TeardownClient<any>): void {
|
|
23
|
-
this.client = client;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private setupWebSocketInterceptor(): void {
|
|
27
|
-
if (WebSocketInterceptor.isInterceptorEnabled()) {
|
|
28
|
-
this.logger.warn(
|
|
29
|
-
'WebSocketInterceptor is already enabled by another library, disable it or run this first when your app loads',
|
|
30
|
-
);
|
|
31
|
-
this.disableInterception();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
WebSocketInterceptor.setConnectCallback(this.wsConnectCallback);
|
|
35
|
-
WebSocketInterceptor.setSendCallback(this.wsSendCallback);
|
|
36
|
-
WebSocketInterceptor.setOnMessageCallback(this.wsOnMessageCallback);
|
|
37
|
-
WebSocketInterceptor.setOnCloseCallback(this.wsOnCloseCallback);
|
|
38
|
-
|
|
39
|
-
WebSocketInterceptor.enableInterception();
|
|
40
|
-
|
|
41
|
-
this.logger.log('WebSocketInterceptor enabled');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private wsConnectCallback = (
|
|
45
|
-
url: string,
|
|
46
|
-
protocols: string | string[] | undefined,
|
|
47
|
-
options: Object,
|
|
48
|
-
socketId: string,
|
|
49
|
-
): void => {
|
|
50
|
-
const requestId = Util.generateUUID();
|
|
51
|
-
const socketInfo: WebSocketInfo = {
|
|
52
|
-
id: requestId,
|
|
53
|
-
url,
|
|
54
|
-
protocols,
|
|
55
|
-
timestamp: Date.now(),
|
|
56
|
-
};
|
|
57
|
-
this.sockets.set(socketId, socketInfo);
|
|
58
|
-
this.sendOpenEvent(socketInfo);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
private wsSendCallback = (
|
|
62
|
-
data: string | ArrayBuffer | ArrayBufferView,
|
|
63
|
-
socketId: string,
|
|
64
|
-
): void => {
|
|
65
|
-
const socket = this.sockets.get(socketId);
|
|
66
|
-
if (socket) {
|
|
67
|
-
const messageInfo: WebSocketMessageInfo = {
|
|
68
|
-
id: socket.id,
|
|
69
|
-
data: '', // TODO: Currently getting array buffer length errors here when sending data
|
|
70
|
-
timestamp: Date.now(),
|
|
71
|
-
direction: 'sent',
|
|
72
|
-
};
|
|
73
|
-
console.log('messageInfo', messageInfo);
|
|
74
|
-
// this.sendMessageEvent(messageInfo);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
private wsOnMessageCallback = (
|
|
79
|
-
socketId: string,
|
|
80
|
-
message: WebSocketMessageEvent,
|
|
81
|
-
): void => {
|
|
82
|
-
const socket = this.sockets.get(socketId);
|
|
83
|
-
if (socket) {
|
|
84
|
-
const messageInfo: WebSocketMessageInfo = {
|
|
85
|
-
id: socket.id,
|
|
86
|
-
data: '', // TODO: Currently getting array buffer length errors here when sending data
|
|
87
|
-
timestamp: Date.now(),
|
|
88
|
-
direction: 'received',
|
|
89
|
-
};
|
|
90
|
-
this.sendMessageEvent(messageInfo);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
private wsOnCloseCallback = (socketId: string, event: any): void => {
|
|
95
|
-
const socket = this.sockets.get(socketId);
|
|
96
|
-
if (socket) {
|
|
97
|
-
const closeInfo: WebSocketCloseInfo = {
|
|
98
|
-
id: socket.id,
|
|
99
|
-
code: event.code,
|
|
100
|
-
reason: event.reason,
|
|
101
|
-
timestamp: Date.now(),
|
|
102
|
-
};
|
|
103
|
-
this.sendCloseEvent(closeInfo);
|
|
104
|
-
this.sockets.delete(socketId);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
private sendOpenEvent(info: WebSocketInfo): void {
|
|
109
|
-
if (this.client && this.client.debugger) {
|
|
110
|
-
this.client.debugger.send('NETWORK_WEBSOCKET_OPEN', info);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
private sendMessageEvent(info: WebSocketMessageInfo): void {
|
|
115
|
-
if (this.client && this.client.debugger) {
|
|
116
|
-
this.client.debugger.send('NETWORK_WEBSOCKET_MESSAGE', info);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private sendCloseEvent(info: WebSocketCloseInfo): void {
|
|
121
|
-
if (this.client && this.client.debugger) {
|
|
122
|
-
this.client.debugger.send('NETWORK_WEBSOCKET_CLOSE', info);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
disableInterception(): void {
|
|
127
|
-
WebSocketInterceptor.disableInterception();
|
|
128
|
-
this.sockets.clear();
|
|
129
|
-
}
|
|
130
|
-
}
|
package/src/services/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './teardown.service';
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {useContext, createContext} from "react";
|
|
2
|
-
import type {PluginTuple, TeardownClient} from "../teardown.client";
|
|
3
|
-
|
|
4
|
-
export type TeardownServiceContextType<T extends readonly PluginTuple[]> = {
|
|
5
|
-
client: TeardownClient<T>
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const Context = createContext<TeardownServiceContextType<any> | null>(null);
|
|
9
|
-
|
|
10
|
-
export const TeardownService = {
|
|
11
|
-
Context,
|
|
12
|
-
Provider: Context.Provider,
|
|
13
|
-
|
|
14
|
-
useState<T extends readonly PluginTuple[]>() {
|
|
15
|
-
const state = useContext(Context);
|
|
16
|
-
if (state == null) {
|
|
17
|
-
throw new Error("TeardownService not found");
|
|
18
|
-
}
|
|
19
|
-
return state as TeardownServiceContextType<T>
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
useProvidedState<T extends readonly PluginTuple[]>(client: TeardownClient<T>): TeardownServiceContextType<T> {
|
|
23
|
-
return {
|
|
24
|
-
client
|
|
25
|
-
};
|
|
26
|
-
},
|
|
27
|
-
};
|
package/src/teardown.client.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import {Logger} from '@teardown/logger';
|
|
2
|
-
import {Debugger, DebuggerOptions} from './debugger';
|
|
3
|
-
|
|
4
|
-
export interface Plugin {
|
|
5
|
-
install?(client: TeardownClient<any>): void;
|
|
6
|
-
uninstall?(): void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type PluginTuple = readonly [string, Plugin];
|
|
10
|
-
|
|
11
|
-
type InferPluginFromTuple<T extends PluginTuple> = {
|
|
12
|
-
[K in T[0]]: Omit<T[1], 'install' | 'uninstall'>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type InferPluginsFromArray<T extends readonly PluginTuple[]> =
|
|
16
|
-
UnionToIntersection<InferPluginFromTuple<T[number]>>;
|
|
17
|
-
|
|
18
|
-
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
19
|
-
k: infer I,
|
|
20
|
-
) => void
|
|
21
|
-
? I
|
|
22
|
-
: never;
|
|
23
|
-
|
|
24
|
-
export type TeardownClientOptions<T extends readonly PluginTuple[]> = {
|
|
25
|
-
plugins?: T;
|
|
26
|
-
debug?: boolean;
|
|
27
|
-
} & DebuggerOptions;
|
|
28
|
-
|
|
29
|
-
export class TeardownClient<T extends readonly PluginTuple[]> {
|
|
30
|
-
private logger = new Logger('TeardownClient');
|
|
31
|
-
|
|
32
|
-
private readonly plugins: Map<string, Plugin> = new Map();
|
|
33
|
-
|
|
34
|
-
public debugger: Debugger | null;
|
|
35
|
-
|
|
36
|
-
public api: InferPluginsFromArray<T> = {} as InferPluginsFromArray<T>;
|
|
37
|
-
|
|
38
|
-
constructor(options?: TeardownClientOptions<T>) {
|
|
39
|
-
this.debugger = __DEV__ ? new Debugger(options) : null;
|
|
40
|
-
|
|
41
|
-
options?.plugins?.forEach(([key, plugin]) => {
|
|
42
|
-
this.plugins.set(key, plugin);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
this.installPlugins();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private installPlugins() {
|
|
49
|
-
this.logger.log('Installing plugins');
|
|
50
|
-
this.plugins.forEach((plugin, key) => {
|
|
51
|
-
plugin.install?.(this);
|
|
52
|
-
(this.api as any)[key] = plugin;
|
|
53
|
-
});
|
|
54
|
-
this.logger.log('Plugins installed');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
uninstallPlugin(key: string) {
|
|
58
|
-
const plugin = this.plugins.get(key);
|
|
59
|
-
if (plugin) {
|
|
60
|
-
plugin.uninstall?.();
|
|
61
|
-
this.plugins.delete(key);
|
|
62
|
-
delete (this.api as any)[key];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
uninstallAllPlugins() {
|
|
67
|
-
this.plugins.forEach((_, key) => this.uninstallPlugin(key));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
reinstallPlugins() {
|
|
71
|
-
this.uninstallAllPlugins();
|
|
72
|
-
this.installPlugins();
|
|
73
|
-
}
|
|
74
|
-
}
|