react-native-debug-toolkit 3.2.9 → 3.3.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.
@@ -5,11 +5,46 @@
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
+ NSString *urlStr = [NSString stringWithFormat:
25
+ @"http://%@/%@.bundle?platform=ios&dev=true&minify=false&lazy=true", metroHost, DebugToolkitBundleRoot];
26
+ NSURL *url = [NSURL URLWithString:urlStr];
27
+ NSLog(@"[DevConnect] swizzle: returning Metro URL %@ (host=%@)", url, metroHost);
28
+ return url;
29
+ }
30
+ if (original_sourceURLForBridge) {
31
+ return ((NSURL *(*)(id, SEL, RCTBridge *))original_sourceURLForBridge)(self, _cmd, bridge);
32
+ }
33
+ return nil;
34
+ }
35
+
36
+ static void swizzleSourceURLForBridge(Class targetClass)
37
+ {
38
+ if (!targetClass) return;
39
+ SEL selector = @selector(sourceURLForBridge:);
40
+ Method method = class_getInstanceMethod(targetClass, selector);
41
+ if (!method) return;
42
+ if (original_sourceURLForBridge) return; // Already swizzled
43
+ original_sourceURLForBridge = method_setImplementation(method, (IMP)devconnect_sourceURLForBridge);
44
+ NSLog(@"[DevConnect] swizzled sourceURLForBridge: on %@", NSStringFromClass(targetClass));
45
+ }
46
+
47
+ #pragma mark - Module
13
48
 
14
49
  @interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
15
50
  @end
@@ -21,6 +56,25 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
21
56
  @synthesize bridge = _bridge;
22
57
  @synthesize bundleManager = _bundleManager;
23
58
 
59
+ __attribute__((constructor))
60
+ static void devconnect_swizzle_init(void)
61
+ {
62
+ // Runs after all +load methods, before main() — same timing as +load but no conflict with RCT_EXPORT_MODULE
63
+ swizzleSourceURLForBridge(NSClassFromString(@"AppDelegate"));
64
+ }
65
+
66
+ - (instancetype)init
67
+ {
68
+ if ((self = [super init])) {
69
+ // Fallback — use actual delegate class at runtime (covers custom class names / Swift)
70
+ if (!original_sourceURLForBridge) {
71
+ Class delegateClass = object_getClass([UIApplication sharedApplication].delegate);
72
+ swizzleSourceURLForBridge(delegateClass);
73
+ }
74
+ }
75
+ return self;
76
+ }
77
+
24
78
  + (BOOL)requiresMainQueueSetup
25
79
  {
26
80
  return NO;
@@ -44,42 +98,6 @@ RCT_EXPORT_MODULE(DebugToolkitDevConnect)
44
98
  return nil;
45
99
  }
46
100
 
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
101
  #pragma mark - Exported Methods
84
102
 
85
103
  RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
@@ -98,8 +116,6 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
98
116
  rejecter:(RCTPromiseRejectBlock)reject)
99
117
  {
100
118
  @try {
101
- NSLog(@"[DevConnect] applyMetroHost called with: %@", hostPort);
102
-
103
119
  if (hostPort.length == 0) {
104
120
  reject(@"invalid_host", @"Metro host cannot be empty.", nil);
105
121
  return;
@@ -107,6 +123,7 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
107
123
 
108
124
  RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
109
125
 
126
+ // Parse host and port
110
127
  NSRange separator = [hostPort rangeOfString:@":" options:NSBackwardsSearch];
111
128
  NSString *host = separator.location == NSNotFound
112
129
  ? hostPort
@@ -124,61 +141,49 @@ RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
124
141
  formatter.numberStyle = NSNumberFormatterDecimalStyle;
125
142
  NSNumber *portNumber = [formatter numberFromString:port];
126
143
  if (portNumber == nil) {
144
+ #ifdef RCT_METRO_PORT
127
145
  portNumber = [NSNumber numberWithInt:RCT_METRO_PORT];
146
+ #else
147
+ portNumber = [NSNumber numberWithInt:8081];
148
+ #endif
128
149
  }
129
150
 
130
151
  NSString *normalizedHostPort = [NSString stringWithFormat:@"%@:%d", host, portNumber.intValue];
131
- NSLog(@"[DevConnect] normalizedHostPort: %@", normalizedHostPort);
132
152
 
153
+ // Persist for AppDelegate swizzle (Release mode + restart)
154
+ [[NSUserDefaults standardUserDefaults] setObject:normalizedHostPort forKey:kDevConnectMetroHost];
155
+ [[NSUserDefaults standardUserDefaults] synchronize];
156
+
157
+ // Also set jsLocation (Debug mode + hot reload)
133
158
  settings.jsLocation = normalizedHostPort;
134
- NSLog(@"[DevConnect] jsLocation set, verify: %@", settings.jsLocation);
135
159
 
160
+ // Try hot reload via bundleManager (works in Debug)
136
161
  NSURL *bundleURL = nil;
137
162
  if (DebugToolkitBundleRoot.length > 0) {
138
163
  bundleURL = [settings jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
139
164
  }
140
- NSLog(@"[DevConnect] generated bundleURL: %@", bundleURL);
141
-
142
165
  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
166
  if (bm) {
150
- NSLog(@"[DevConnect] bm.bundleURL after apply: %@", bm.bundleURL);
167
+ bm.bundleURL = bundleURL;
151
168
  }
152
-
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";
169
+ #ifdef RCTReloadCommandSetBundleURL
170
+ else if (bundleURL) {
171
+ RCTReloadCommandSetBundleURL(bundleURL);
164
172
  }
165
- [self saveDiagnostic:diagnostic];
173
+ #endif
174
+
175
+ NSLog(@"[DevConnect] applyMetroHost: %@ | bm=%@ | bridge=%@ | url=%@",
176
+ normalizedHostPort, bm ? @"YES" : @"nil", _bridge ? @"YES" : @"nil", bundleURL);
177
+
178
+ RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
166
179
 
167
180
  NSMutableDictionary *result = [@{@"hostPort" : normalizedHostPort} mutableCopy];
168
181
  if (bm && bm.bundleURL.absoluteString) {
169
182
  result[@"bundleURL"] = bm.bundleURL.absoluteString;
170
183
  }
171
-
172
- NSLog(@"[DevConnect] triggering reload...");
173
- RCTTriggerReloadCommandListeners(@"Dev menu - apply changes");
174
184
  resolve(result);
175
185
  } @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
- }];
186
+ NSLog(@"[DevConnect] applyMetroHost EXCEPTION: %@", exception.reason);
182
187
  reject(@"native_error", exception.reason, nil);
183
188
  }
184
189
  }
@@ -187,13 +192,21 @@ RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
187
192
  rejecter:(__unused RCTPromiseRejectBlock)reject)
188
193
  {
189
194
  @try {
190
- NSLog(@"[DevConnect] resetMetroHost called");
195
+ // Clear stored Metro host
196
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:kDevConnectMetroHost];
197
+ [[NSUserDefaults standardUserDefaults] synchronize];
198
+
199
+ // Reset RN settings
191
200
  RCTBundleURLProvider *settings = [RCTBundleURLProvider sharedSettings];
192
201
  [settings resetToDefaults];
193
202
  NSURL *bundleURL = [settings jsBundleURLForFallbackExtension:nil];
194
- NSLog(@"[DevConnect] reset bundleURL: %@", bundleURL);
195
203
 
196
- [self applyBundleURL:bundleURL];
204
+ RCTBundleManager *bm = [self resolveBundleManager];
205
+ if (bm) {
206
+ bm.bundleURL = bundleURL;
207
+ }
208
+
209
+ NSLog(@"[DevConnect] resetMetroHost | bm=%@ | url=%@", bm ? @"YES" : @"nil", bundleURL);
197
210
  RCTTriggerReloadCommandListeners(@"Dev menu - reset to default");
198
211
  resolve([NSNull null]);
199
212
  } @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.1",
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",