react-native-debug-toolkit 3.2.9 → 3.3.0

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.
@@ -5,11 +5,48 @@
5
5
  #import <React/RCTBundleURLProvider.h>
6
6
  #import <React/RCTDefines.h>
7
7
  #import <React/RCTReloadCommand.h>
8
+ #import <objc/runtime.h>
8
9
  #include <ifaddrs.h>
9
10
  #include <arpa/inet.h>
10
11
  #include <net/if.h>
11
12
 
12
13
  static NSString *const DebugToolkitBundleRoot = @"index";
14
+ static NSString *const kDevConnectMetroHost = @"_devconnect_metro_host";
15
+
16
+ #pragma mark - AppDelegate Swizzling
17
+
18
+ static IMP original_sourceURLForBridge = NULL;
19
+
20
+ static NSURL *devconnect_sourceURLForBridge(id self, SEL _cmd, RCTBridge *bridge)
21
+ {
22
+ NSString *metroHost = [[NSUserDefaults standardUserDefaults] stringForKey:kDevConnectMetroHost];
23
+ if (metroHost.length > 0) {
24
+ RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
25
+ NSString *saved = settings.jsLocation;
26
+ settings.jsLocation = metroHost;
27
+ NSURL *url = [settings jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
28
+ settings.jsLocation = saved;
29
+ NSLog(@"[DevConnect] swizzle: returning Metro URL %@ (host=%@)", url, metroHost);
30
+ return url;
31
+ }
32
+ if (original_sourceURLForBridge) {
33
+ return ((NSURL *(*)(id, SEL, RCTBridge *))original_sourceURLForBridge)(self, _cmd, bridge);
34
+ }
35
+ return nil;
36
+ }
37
+
38
+ static void swizzleSourceURLForBridge(Class targetClass)
39
+ {
40
+ if (!targetClass) return;
41
+ SEL selector = @selector(sourceURLForBridge:);
42
+ Method method = class_getInstanceMethod(targetClass, selector);
43
+ if (!method) return;
44
+ if (original_sourceURLForBridge) return; // Already swizzled
45
+ original_sourceURLForBridge = method_setImplementation(method, (IMP)devconnect_sourceURLForBridge);
46
+ NSLog(@"[DevConnect] swizzled sourceURLForBridge: on %@", NSStringFromClass(targetClass));
47
+ }
48
+
49
+ #pragma mark - Module
13
50
 
14
51
  @interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
15
52
  @end
@@ -21,6 +58,24 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
21
58
  @synthesize bridge = _bridge;
22
59
  @synthesize bundleManager = _bundleManager;
23
60
 
61
+ + (void)load
62
+ {
63
+ // Stage 1: Try standard AppDelegate class name (covers ObjC and most Swift apps)
64
+ swizzleSourceURLForBridge(NSClassFromString(@"AppDelegate"));
65
+ }
66
+
67
+ - (instancetype)init
68
+ {
69
+ if ((self = [super init])) {
70
+ // Stage 2: Fallback — use actual delegate class at runtime
71
+ if (!original_sourceURLForBridge) {
72
+ Class delegateClass = object_getClass([UIApplication sharedApplication].delegate);
73
+ swizzleSourceURLForBridge(delegateClass);
74
+ }
75
+ }
76
+ return self;
77
+ }
78
+
24
79
  + (BOOL)requiresMainQueueSetup
25
80
  {
26
81
  return NO;
@@ -44,42 +99,6 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
44
99
  return nil;
45
100
  }
46
101
 
47
- - (void)saveDiagnostic:(NSDictionary *)info
48
- {
49
- NSData *data = [NSJSONSerialization dataWithJSONObject:info options:0 error:nil];
50
- if (data) {
51
- NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
52
- [[NSUserDefaults standardUserDefaults] setObject:json forKey:@"_devconnect_last_diagnostic"];
53
- [[NSUserDefaults standardUserDefaults] synchronize];
54
- }
55
- }
56
-
57
- #pragma mark - Apply Bundle URL (multi-strategy)
58
-
59
- - (BOOL)applyBundleURL:(NSURL *)bundleURL
60
- {
61
- RCTBundleManager *bm = [self resolveBundleManager];
62
- if (bm) {
63
- if (bundleURL) {
64
- bm.bundleURL = bundleURL;
65
- NSLog(@"[DevConnect] applyBundleURL strategy 1: set bm.bundleURL = %@", bundleURL);
66
- } else {
67
- [bm resetBundleURL];
68
- NSLog(@"[DevConnect] applyBundleURL strategy 1: resetBundleURL");
69
- }
70
- return YES;
71
- }
72
-
73
- if (bundleURL) {
74
- RCTReloadCommandSetBundleURL(bundleURL);
75
- NSLog(@"[DevConnect] applyBundleURL strategy 2: RCTReloadCommandSetBundleURL = %@", bundleURL);
76
- return YES;
77
- }
78
-
79
- NSLog(@"[DevConnect] applyBundleURL strategy 3: no URL, relying on jsLocation persistence");
80
- return NO;
81
- }
82
-
83
102
  #pragma mark - Exported Methods
84
103
 
85
104
  RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
@@ -98,8 +117,6 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
98
117
  rejecter:(RCTPromiseRejectBlock)reject)
99
118
  {
100
119
  @try {
101
- NSLog(@"[DevConnect] applyMetroHost called with: %@", hostPort);
102
-
103
120
  if (hostPort.length == 0) {
104
121
  reject(@"invalid_host", @"Metro host cannot be empty.", nil);
105
122
  return;
@@ -107,6 +124,7 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
107
124
 
108
125
  RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
109
126
 
127
+ // Parse host and port
110
128
  NSRange separator = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
111
129
  NSString *host = separator.location == NSNotFound
112
130
  ? hostPort
@@ -128,57 +146,38 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
128
146
  }
129
147
 
130
148
  NSString *normalizedHostPort = [NSString stringWithFormat:@"%@:%d", host, portNumber.intValue];
131
- NSLog(@"[DevConnect] normalizedHostPort: %@", normalizedHostPort);
132
149
 
150
+ // Persist for AppDelegate swizzle (Release mode + restart)
151
+ [[NSUserDefaults standardUserDefaults] setObject:normalizedHostPort forKey:kDevConnectMetroHost];
152
+ [[NSUserDefaults standardUserDefaults] synchronize];
153
+
154
+ // Also set jsLocation (Debug mode + hot reload)
133
155
  settings.jsLocation = normalizedHostPort;
134
- NSLog(@"[DevConnect] jsLocation set, verify: %@", settings.jsLocation);
135
156
 
157
+ // Try hot reload via bundleManager (works in Debug)
136
158
  NSURL *bundleURL = nil;
137
159
  if (DebugToolkitBundleRoot.length > 0) {
138
160
  bundleURL = [settings jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
139
161
  }
140
- NSLog(@"[DevConnect] generated bundleURL: %@", bundleURL);
141
-
142
162
  RCTBundleManager *bm = [self resolveBundleManager];
143
- BOOL applied = [self applyBundleURL:bundleURL];
144
- NSLog(@"[DevConnect] resolveBundleManager: %@", bm ? @"found" : @"nil");
145
- NSLog(@"[DevConnect] applyBundleURL result: %@", applied ? @"YES" : @"NO");
146
- NSLog(@"[DevConnect] _bundleManager injected: %@", _bundleManager ? @"YES" : @"nil");
147
- NSLog(@"[DevConnect] _bridge available: %@", _bridge ? @"YES" : @"nil");
148
-
149
163
  if (bm) {
150
- NSLog(@"[DevConnect] bm.bundleURL after apply: %@", bm.bundleURL);
164
+ bm.bundleURL = bundleURL;
165
+ } else if (bundleURL) {
166
+ RCTReloadCommandSetBundleURL(bundleURL);
151
167
  }
152
168
 
153
- // Save diagnostic before reload (survives restart)
154
- NSMutableDictionary *diagnostic = [@{
155
- @"hostPort" : normalizedHostPort ?: @"",
156
- @"bundleManagerInjected" : _bundleManager ? @"YES" : @"nil",
157
- @"bridgeAvailable" : _bridge ? @"YES" : @"nil",
158
- @"resolvedBM" : bm ? @"YES" : @"nil",
159
- @"bundleURL" : bundleURL.absoluteString ?: @"nil",
160
- @"applied" : applied ? @"YES" : @"NO",
161
- } mutableCopy];
162
- if (bm) {
163
- diagnostic[@"bmBundleURL"] = bm.bundleURL.absoluteString ?: @"nil";
164
- }
165
- [self saveDiagnostic:diagnostic];
169
+ NSLog(@"[DevConnect] applyMetroHost: %@ | bm=%@ | bridge=%@ | url=%@",
170
+ normalizedHostPort, bm ? @"YES" : @"nil", _bridge ? @"YES" : @"nil", bundleURL);
171
+
172
+ RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
166
173
 
167
174
  NSMutableDictionary *result = [@{@"hostPort" : normalizedHostPort} mutableCopy];
168
175
  if (bm && bm.bundleURL.absoluteString) {
169
176
  result[@"bundleURL"] = bm.bundleURL.absoluteString;
170
177
  }
171
-
172
- NSLog(@"[DevConnect] triggering reload...");
173
- RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
174
178
  resolve(result);
175
179
  } @catch (NSException *exception) {
176
- NSLog(@"[DevConnect] EXCEPTION: %@ - %@", exception.name, exception.reason);
177
- // Save exception diagnostic
178
- [self saveDiagnostic:@{
179
- @"error" : exception.reason ?: @"unknown",
180
- @"exception" : exception.name ?: @"unknown",
181
- }];
180
+ NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", exception.reason);
182
181
  reject(@"native_error", exception.reason, nil);
183
182
  }
184
183
  }
@@ -187,13 +186,21 @@ RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
187
186
  rejecter:(__unused RCTPromiseRejectBlock)reject)
188
187
  {
189
188
  @try {
190
- NSLog(@"[DevConnect] resetMetroHost called");
189
+ // Clear stored Metro host
190
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:kDevConnectMetroHost];
191
+ [[NSUserDefaults standardUserDefaults] synchronize];
192
+
193
+ // Reset RN settings
191
194
  RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
192
195
  [settings resetToDefaults];
193
196
  NSURL *bundleURL = [settings jsBundleURLForFallbackExtension:nil];
194
- NSLog(@"[DevConnect] reset bundleURL: %@", bundleURL);
195
197
 
196
- [self applyBundleURL:bundleURL];
198
+ RCTBundleManager *bm = [self resolveBundleManager];
199
+ if (bm) {
200
+ bm.bundleURL = bundleURL;
201
+ }
202
+
203
+ NSLog(@"[DevConnect] resetMetroHost | bm=%@ | url=%@", bm ? @"YES" : @"nil", bundleURL);
197
204
  RCTTriggerReloadCommandListeners(@"Dev menu - reset to default");
198
205
  resolve([NSNull null]);
199
206
  } @catch (NSException *exception) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-debug-toolkit",
3
- "version": "3.2.9",
3
+ "version": "3.3.0",
4
4
  "description": "A local-first React Native debug toolkit with Web Console, HTTP API, and MCP support for AI-readable app logs",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",