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 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,10 @@
1
+ /**
2
+ * React Native Debug Toolkit
3
+ * A comprehensive debugging toolkit for React Native
4
+ */
5
+
6
+ import DebugToolKit from './lib/DebugToolKit';
7
+ import BuildType from './lib/utils/BuildTypeBridge';
8
+
9
+ export { BuildType };
10
+ export default DebugToolKit;
@@ -0,0 +1,4 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface BuildTypeModule : NSObject <RCTBridgeModule>
4
+ @end
@@ -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
+ }