react-native-debug-toolkit 0.1.1
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 +108 -0
- package/android/src/main/java/com/debugtoolkit/BuildTypeModule.java +68 -0
- package/android/src/main/java/com/debugtoolkit/BuildTypePackage.java +24 -0
- package/index.d.ts +155 -0
- package/index.js +10 -0
- package/ios/BuildTypeModule.h +4 -0
- package/ios/BuildTypeModule.m +71 -0
- package/lib/DebugToolKit.js +83 -0
- package/lib/EnvironmentManager.ts +80 -0
- package/lib/features/NetworkFeature.js +330 -0
- package/lib/index.js +6 -0
- package/lib/utils/DebugConst.js +22 -0
- package/lib/views/FloatPanelView.js +491 -0
- package/lib/views/HttpLogDetails.js +666 -0
- package/lib/views/RestartModal.js +75 -0
- package/lib/views/SubViewEnvironment.js +73 -0
- package/lib/views/SubViewHTTPLogs.js +267 -0
- package/lib/views/TabView.js +66 -0
- package/package.json +39 -0
- package/react-native-debug-toolkit-0.1.1.tgz +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# React Native Debug Toolkit
|
|
2
|
+
|
|
3
|
+
A simple yet powerful debugging toolkit for React Native with a convenient floating UI for development.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install react-native-debug-toolkit
|
|
9
|
+
# or
|
|
10
|
+
yarn add react-native-debug-toolkit
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Basic Usage
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
// In your App.js
|
|
17
|
+
import DebugToolKit from 'react-native-debug-toolkit';
|
|
18
|
+
|
|
19
|
+
function App() {
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (__DEV__) {
|
|
22
|
+
const debugToolKit = new DebugToolKit();
|
|
23
|
+
debugToolKit.addFeature({
|
|
24
|
+
name: 'network',
|
|
25
|
+
label: 'Network Logs',
|
|
26
|
+
setup: () => console.log('Feature ready'),
|
|
27
|
+
getData: () => ({ message: 'Hello debugging world!' })
|
|
28
|
+
});
|
|
29
|
+
debugToolKit.showDebugPanel();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return () => {
|
|
33
|
+
if (__DEV__) {
|
|
34
|
+
DebugToolKit.instance.destroy();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
return <YourApp />;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- Lightweight floating debug panel
|
|
46
|
+
- Customizable debugging features
|
|
47
|
+
- Development-only functionality (automatically disabled in production)
|
|
48
|
+
- Easy to extend with your own debug tools
|
|
49
|
+
|
|
50
|
+
## API Reference
|
|
51
|
+
|
|
52
|
+
### DebugToolKit
|
|
53
|
+
|
|
54
|
+
#### `new DebugToolKit()`
|
|
55
|
+
Initializes a new instance of the debug toolkit. It's a singleton, so multiple calls will return the same instance.
|
|
56
|
+
|
|
57
|
+
#### `addFeature(feature: Feature): DebugToolKit`
|
|
58
|
+
Adds a new debugging feature to the toolkit. Returns the toolkit instance for chaining.
|
|
59
|
+
|
|
60
|
+
Feature object must have:
|
|
61
|
+
- `name`: Unique identifier for the feature
|
|
62
|
+
- `label`: Display name in the debug panel
|
|
63
|
+
- `setup()`: Function to run when the feature is added
|
|
64
|
+
- `getData()`: Function that returns data to display in the panel
|
|
65
|
+
- `cleanup()`: (Optional) Function to run when the feature is removed
|
|
66
|
+
|
|
67
|
+
#### `showDebugPanel(): void`
|
|
68
|
+
Shows the floating debug panel.
|
|
69
|
+
|
|
70
|
+
#### `hideDebugPanel(): void`
|
|
71
|
+
Hides the floating debug panel.
|
|
72
|
+
|
|
73
|
+
#### `updateDebugPanel(): void`
|
|
74
|
+
Updates the debug panel with the latest feature data.
|
|
75
|
+
|
|
76
|
+
#### `destroy(): void`
|
|
77
|
+
Cleans up the debug toolkit, removing all features and event listeners.
|
|
78
|
+
|
|
79
|
+
## Example: Network Feature
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// In debugFeatures.js
|
|
83
|
+
import { NetworkFeature } from 'react-native-debug-toolkit/lib/features/NetworkFeature';
|
|
84
|
+
|
|
85
|
+
export const createNetworkFeature = () => {
|
|
86
|
+
const feature = new NetworkFeature();
|
|
87
|
+
return {
|
|
88
|
+
name: 'network',
|
|
89
|
+
label: 'Network Logs',
|
|
90
|
+
setup: () => feature.setup(),
|
|
91
|
+
getData: () => feature.getData(),
|
|
92
|
+
cleanup: () => feature.cleanup()
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// In App.js
|
|
97
|
+
const debugToolKit = new DebugToolKit();
|
|
98
|
+
debugToolKit.addFeature(createNetworkFeature());
|
|
99
|
+
debugToolKit.showDebugPanel();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Contributing
|
|
103
|
+
|
|
104
|
+
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
package com.debugtoolkit;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
4
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
5
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
6
|
+
import com.facebook.react.bridge.Promise;
|
|
7
|
+
import com.facebook.react.bridge.Callback;
|
|
8
|
+
|
|
9
|
+
import java.util.HashMap;
|
|
10
|
+
import java.util.Map;
|
|
11
|
+
|
|
12
|
+
public class BuildTypeModule extends ReactContextBaseJavaModule {
|
|
13
|
+
private final ReactApplicationContext reactContext;
|
|
14
|
+
|
|
15
|
+
public BuildTypeModule(ReactApplicationContext reactContext) {
|
|
16
|
+
super(reactContext);
|
|
17
|
+
this.reactContext = reactContext;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Override
|
|
21
|
+
public String getName() {
|
|
22
|
+
return "BuildTypeModule";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Override
|
|
26
|
+
public Map<String, Object> getConstants() {
|
|
27
|
+
final Map<String, Object> constants = new HashMap<>();
|
|
28
|
+
constants.put("isDebug", BuildConfig.DEBUG);
|
|
29
|
+
return constants;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@ReactMethod
|
|
33
|
+
public void isDebugMode(Promise promise) {
|
|
34
|
+
try {
|
|
35
|
+
promise.resolve(BuildConfig.DEBUG);
|
|
36
|
+
} catch (Exception e) {
|
|
37
|
+
promise.reject("ERR_UNEXPECTED", e.getMessage(), e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@ReactMethod
|
|
42
|
+
public void isReleaseMode(Promise promise) {
|
|
43
|
+
try {
|
|
44
|
+
promise.resolve(!BuildConfig.DEBUG);
|
|
45
|
+
} catch (Exception e) {
|
|
46
|
+
promise.reject("ERR_UNEXPECTED", e.getMessage(), e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
51
|
+
public Boolean isDebugModeSync() {
|
|
52
|
+
return BuildConfig.DEBUG;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
56
|
+
public Boolean isReleaseModeSync() {
|
|
57
|
+
return !BuildConfig.DEBUG;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@ReactMethod
|
|
61
|
+
public void getBuildType(Promise promise) {
|
|
62
|
+
try {
|
|
63
|
+
promise.resolve(BuildConfig.DEBUG ? "debug" : "release");
|
|
64
|
+
} catch (Exception e) {
|
|
65
|
+
promise.reject("ERR_UNEXPECTED", e.getMessage(), e);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package com.debugtoolkit;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage;
|
|
4
|
+
import com.facebook.react.bridge.NativeModule;
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
7
|
+
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.Collections;
|
|
10
|
+
import java.util.List;
|
|
11
|
+
|
|
12
|
+
public class BuildTypePackage implements ReactPackage {
|
|
13
|
+
@Override
|
|
14
|
+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
15
|
+
List<NativeModule> modules = new ArrayList<>();
|
|
16
|
+
modules.add(new BuildTypeModule(reactContext));
|
|
17
|
+
return modules;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Override
|
|
21
|
+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
22
|
+
return Collections.emptyList();
|
|
23
|
+
}
|
|
24
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
declare module 'react-native-debug-toolkit' {
|
|
2
|
+
export interface Environment {
|
|
3
|
+
[key: string]: any;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface LogEntry {
|
|
7
|
+
message: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface HttpRequestInfo {
|
|
12
|
+
url: string;
|
|
13
|
+
method?: string;
|
|
14
|
+
headers?: any;
|
|
15
|
+
body?: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface HttpResponseInfo {
|
|
19
|
+
status: number;
|
|
20
|
+
statusText?: string;
|
|
21
|
+
headers?: any;
|
|
22
|
+
data?: any;
|
|
23
|
+
success?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface HttpLogEntry {
|
|
27
|
+
request: HttpRequestInfo;
|
|
28
|
+
response: HttpResponseInfo;
|
|
29
|
+
timestamp: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
success?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface WebViewLogEntry {
|
|
35
|
+
url: string;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface EnvironmentOption {
|
|
40
|
+
label: string;
|
|
41
|
+
value: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface EnvironmentConfig {
|
|
45
|
+
options: Array<EnvironmentOption>;
|
|
46
|
+
currentValue: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface BuildType {
|
|
50
|
+
/**
|
|
51
|
+
* Determines if the app is running in debug mode (async)
|
|
52
|
+
* @returns Promise that resolves to true if in debug mode
|
|
53
|
+
*/
|
|
54
|
+
isDebugMode(): Promise<boolean>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Determines if the app is running in release mode (async)
|
|
58
|
+
* @returns Promise that resolves to true if in release mode
|
|
59
|
+
*/
|
|
60
|
+
isReleaseMode(): Promise<boolean>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Determines if the app is running in debug mode (sync)
|
|
64
|
+
* @returns true if in debug mode
|
|
65
|
+
*/
|
|
66
|
+
isDebugModeSync(): boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Determines if the app is running in release mode (sync)
|
|
70
|
+
* @returns true if in release mode
|
|
71
|
+
*/
|
|
72
|
+
isReleaseModeSync(): boolean;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets the current build type (async)
|
|
76
|
+
* @returns Promise that resolves to 'debug' or 'release'
|
|
77
|
+
*/
|
|
78
|
+
getBuildType(): Promise<'debug' | 'release'>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Executes the appropriate callback based on the current environment
|
|
82
|
+
* @param debugCallback - Function to execute in debug mode
|
|
83
|
+
* @param releaseCallback - Function to execute in release mode
|
|
84
|
+
* @returns Result of the executed callback
|
|
85
|
+
*/
|
|
86
|
+
runInEnvironment<T>(debugCallback: () => T, releaseCallback: () => T): T;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface DebugToolKit {
|
|
90
|
+
/**
|
|
91
|
+
* Initialize the debug toolkit with your environment configurations
|
|
92
|
+
* @param environments - Object with environment configurations
|
|
93
|
+
* @param defaultEnv - Default environment key (e.g., 'dev', 'prod')
|
|
94
|
+
* @returns Promise with the current environment key
|
|
95
|
+
*/
|
|
96
|
+
initialize(environments: { [key: string]: Environment }, defaultEnv?: string): Promise<string>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Subscribe to environment changes
|
|
100
|
+
* @param callback - Function to call when environment changes
|
|
101
|
+
* @returns Unsubscribe function
|
|
102
|
+
*/
|
|
103
|
+
onEnvironmentChange(callback: (newEnv: Environment) => void): () => void;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the current environment key
|
|
107
|
+
* @returns Current environment key (e.g., 'dev', 'prod')
|
|
108
|
+
*/
|
|
109
|
+
getCurrentEnvironment(): string;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get the complete environment configuration
|
|
113
|
+
* @returns Environment configuration object
|
|
114
|
+
*/
|
|
115
|
+
getEnvironmentConfig(): EnvironmentConfig;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Show the floating debug panel
|
|
119
|
+
*/
|
|
120
|
+
showDebugPanel(): void;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Hide the floating debug panel
|
|
124
|
+
*/
|
|
125
|
+
hideDebugPanel(): void;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Log a debug message
|
|
129
|
+
* @param message - Debug message to log
|
|
130
|
+
*/
|
|
131
|
+
log(message: string): void;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Manually log HTTP request and response
|
|
135
|
+
* @param request - HTTP request info
|
|
136
|
+
* @param response - HTTP response info
|
|
137
|
+
*/
|
|
138
|
+
logHttpRequest(request: HttpRequestInfo, response: HttpResponseInfo): void;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Log WebView navigation
|
|
142
|
+
* @param url - URL that the WebView navigated to
|
|
143
|
+
*/
|
|
144
|
+
logWebViewNavigation(url: string): void;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Clean up the debug toolkit and remove all listeners
|
|
148
|
+
*/
|
|
149
|
+
destroy(): void;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const BuildType: BuildType;
|
|
153
|
+
const debugToolKit: DebugToolKit;
|
|
154
|
+
export default debugToolKit;
|
|
155
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#import "BuildTypeModule.h"
|
|
2
|
+
|
|
3
|
+
@implementation BuildTypeModule
|
|
4
|
+
|
|
5
|
+
RCT_EXPORT_MODULE();
|
|
6
|
+
|
|
7
|
+
+ (BOOL)requiresMainQueueSetup
|
|
8
|
+
{
|
|
9
|
+
return NO;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
- (NSDictionary *)constantsToExport
|
|
13
|
+
{
|
|
14
|
+
#ifdef DEBUG
|
|
15
|
+
BOOL isDebug = YES;
|
|
16
|
+
#else
|
|
17
|
+
BOOL isDebug = NO;
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
return @{ @"isDebug": @(isDebug) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isDebugModeSync)
|
|
24
|
+
{
|
|
25
|
+
#ifdef DEBUG
|
|
26
|
+
return @(YES);
|
|
27
|
+
#else
|
|
28
|
+
return @(NO);
|
|
29
|
+
#endif
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isReleaseModeSync)
|
|
33
|
+
{
|
|
34
|
+
#ifdef DEBUG
|
|
35
|
+
return @(NO);
|
|
36
|
+
#else
|
|
37
|
+
return @(YES);
|
|
38
|
+
#endif
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
RCT_EXPORT_METHOD(isDebugMode:(RCTPromiseResolveBlock)resolve
|
|
42
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
43
|
+
{
|
|
44
|
+
#ifdef DEBUG
|
|
45
|
+
resolve(@(YES));
|
|
46
|
+
#else
|
|
47
|
+
resolve(@(NO));
|
|
48
|
+
#endif
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
RCT_EXPORT_METHOD(isReleaseMode:(RCTPromiseResolveBlock)resolve
|
|
52
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
53
|
+
{
|
|
54
|
+
#ifdef DEBUG
|
|
55
|
+
resolve(@(NO));
|
|
56
|
+
#else
|
|
57
|
+
resolve(@(YES));
|
|
58
|
+
#endif
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
RCT_EXPORT_METHOD(getBuildType:(RCTPromiseResolveBlock)resolve
|
|
62
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
63
|
+
{
|
|
64
|
+
#ifdef DEBUG
|
|
65
|
+
resolve(@"debug");
|
|
66
|
+
#else
|
|
67
|
+
resolve(@"release");
|
|
68
|
+
#endif
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { BackHandler, Pressable, Text } from 'react-native'
|
|
3
|
+
import RootSiblings from 'react-native-root-siblings'
|
|
4
|
+
import FloatPanelView from './views/FloatPanelView'
|
|
5
|
+
|
|
6
|
+
class DebugToolKit {
|
|
7
|
+
static instance = null
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
if (DebugToolKit.instance) {
|
|
11
|
+
return DebugToolKit.instance
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.floatPanel = null
|
|
15
|
+
this.features = []
|
|
16
|
+
|
|
17
|
+
BackHandler.addEventListener('hardwareBackPress', () => true)
|
|
18
|
+
DebugToolKit.instance = this
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addFeature(feature) {
|
|
22
|
+
if (!__DEV__) {
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!feature.name || !feature.label || !feature.setup || !feature.getData) {
|
|
27
|
+
console.error('Invalid feature format', feature)
|
|
28
|
+
return this
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
feature.setup()
|
|
32
|
+
this.features.push(feature)
|
|
33
|
+
this.updateDebugPanel()
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
showDebugPanel = () => {
|
|
38
|
+
if (!__DEV__) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!this.floatPanel) {
|
|
43
|
+
this.floatPanel = new RootSiblings(
|
|
44
|
+
<FloatPanelView features={this.features} close={this.hideDebugPanel} />,
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
hideDebugPanel = () => {
|
|
50
|
+
if (this.floatPanel) {
|
|
51
|
+
this.floatPanel.destroy()
|
|
52
|
+
this.floatPanel = null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateDebugPanel() {
|
|
57
|
+
if (this.floatPanel) {
|
|
58
|
+
this.floatPanel.update(
|
|
59
|
+
<FloatPanelView features={this.features} close={this.hideDebugPanel} />,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
destroy() {
|
|
65
|
+
this.hideDebugPanel()
|
|
66
|
+
|
|
67
|
+
if (this.restartModal) {
|
|
68
|
+
this.restartModal.destroy()
|
|
69
|
+
this.restartModal = null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.features.forEach((feature) => {
|
|
73
|
+
if (typeof feature.cleanup === 'function') {
|
|
74
|
+
feature.cleanup()
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
this.features = []
|
|
79
|
+
BackHandler.removeEventListener('hardwareBackPress')
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default DebugToolKit
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
|
+
|
|
3
|
+
export type BaseEnvironmentConfig = {
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class EnvironmentManager<T extends BaseEnvironmentConfig> {
|
|
8
|
+
private static instance: EnvironmentManager<any>;
|
|
9
|
+
private currentEnv: string;
|
|
10
|
+
private environments: Record<string, T>;
|
|
11
|
+
private listeners: ((env: T) => void)[] = [];
|
|
12
|
+
static ENV_STORAGE_KEY = '@debug_toolkit_env';
|
|
13
|
+
|
|
14
|
+
protected constructor() {
|
|
15
|
+
this.environments = {};
|
|
16
|
+
this.currentEnv = '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static getInstance<T extends BaseEnvironmentConfig>(): EnvironmentManager<T> {
|
|
20
|
+
if (!EnvironmentManager.instance) {
|
|
21
|
+
EnvironmentManager.instance = new EnvironmentManager<T>();
|
|
22
|
+
}
|
|
23
|
+
return EnvironmentManager.instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async initialize(environments: Record<string, T>, defaultEnv: string): Promise<string> {
|
|
27
|
+
if (!__DEV__) return defaultEnv;
|
|
28
|
+
|
|
29
|
+
this.environments = environments;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const savedEnv = await AsyncStorage.getItem(EnvironmentManager.ENV_STORAGE_KEY);
|
|
33
|
+
this.currentEnv = savedEnv || defaultEnv;
|
|
34
|
+
await AsyncStorage.setItem(EnvironmentManager.ENV_STORAGE_KEY, this.currentEnv);
|
|
35
|
+
return this.currentEnv;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('EnvironmentManager initialization error:', error);
|
|
38
|
+
this.currentEnv = defaultEnv;
|
|
39
|
+
return defaultEnv;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async changeEnvironment(newValue: string): Promise<void> {
|
|
44
|
+
if (newValue === this.currentEnv) return;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await AsyncStorage.setItem(EnvironmentManager.ENV_STORAGE_KEY, newValue);
|
|
48
|
+
this.currentEnv = newValue;
|
|
49
|
+
|
|
50
|
+
const envConfig = this.environments[newValue];
|
|
51
|
+
this.listeners.forEach(listener => listener(envConfig));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Failed to change environment:', error);
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public getCurrentEnvironment(): string {
|
|
59
|
+
return this.currentEnv;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public getEnvironmentConfig(): T {
|
|
63
|
+
return this.environments[this.currentEnv];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public getEnvironments(): Record<string, T> {
|
|
67
|
+
return this.environments;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public onEnvironmentChange(listener: (env: T) => void): () => void {
|
|
71
|
+
this.listeners.push(listener);
|
|
72
|
+
return () => {
|
|
73
|
+
this.listeners = this.listeners.filter(l => l !== listener);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public destroy(): void {
|
|
78
|
+
this.listeners = [];
|
|
79
|
+
}
|
|
80
|
+
}
|