react-native-debug-toolkit 3.1.3 → 3.1.5

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.
Files changed (94) hide show
  1. package/README.md +83 -65
  2. package/README.zh-CN.md +82 -64
  3. package/bin/debug-toolkit.js +10 -2
  4. package/lib/commonjs/core/DebugToolkit.js +118 -97
  5. package/lib/commonjs/core/DebugToolkit.js.map +1 -1
  6. package/lib/commonjs/core/initialize.js +4 -4
  7. package/lib/commonjs/core/initialize.js.map +1 -1
  8. package/lib/commonjs/features/environment/index.js +22 -24
  9. package/lib/commonjs/features/environment/index.js.map +1 -1
  10. package/lib/commonjs/features/network/NetworkLogTab.js +7 -3
  11. package/lib/commonjs/features/network/NetworkLogTab.js.map +1 -1
  12. package/lib/commonjs/features/network/index.js +25 -47
  13. package/lib/commonjs/features/network/index.js.map +1 -1
  14. package/lib/commonjs/features/network/networkInterceptor.js +3 -3
  15. package/lib/commonjs/features/network/networkInterceptor.js.map +1 -1
  16. package/lib/commonjs/index.js +0 -30
  17. package/lib/commonjs/index.js.map +1 -1
  18. package/lib/commonjs/utils/DaemonClient.js +37 -51
  19. package/lib/commonjs/utils/DaemonClient.js.map +1 -1
  20. package/lib/commonjs/utils/createChannelFeature.js +8 -1
  21. package/lib/commonjs/utils/createChannelFeature.js.map +1 -1
  22. package/lib/commonjs/utils/deviceReport.js +3 -1
  23. package/lib/commonjs/utils/deviceReport.js.map +1 -1
  24. package/lib/commonjs/utils/urlRewriter.js +15 -0
  25. package/lib/commonjs/utils/urlRewriter.js.map +1 -0
  26. package/lib/module/core/DebugToolkit.js +117 -96
  27. package/lib/module/core/DebugToolkit.js.map +1 -1
  28. package/lib/module/core/initialize.js +6 -7
  29. package/lib/module/core/initialize.js.map +1 -1
  30. package/lib/module/features/environment/index.js +22 -24
  31. package/lib/module/features/environment/index.js.map +1 -1
  32. package/lib/module/features/network/NetworkLogTab.js +7 -3
  33. package/lib/module/features/network/NetworkLogTab.js.map +1 -1
  34. package/lib/module/features/network/index.js +25 -46
  35. package/lib/module/features/network/index.js.map +1 -1
  36. package/lib/module/features/network/networkInterceptor.js +3 -3
  37. package/lib/module/features/network/networkInterceptor.js.map +1 -1
  38. package/lib/module/index.js +1 -1
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/utils/DaemonClient.js +38 -42
  41. package/lib/module/utils/DaemonClient.js.map +1 -1
  42. package/lib/module/utils/createChannelFeature.js +8 -1
  43. package/lib/module/utils/createChannelFeature.js.map +1 -1
  44. package/lib/module/utils/deviceReport.js +4 -2
  45. package/lib/module/utils/deviceReport.js.map +1 -1
  46. package/lib/module/utils/urlRewriter.js +10 -0
  47. package/lib/module/utils/urlRewriter.js.map +1 -0
  48. package/lib/typescript/src/core/DebugToolkit.d.ts +23 -10
  49. package/lib/typescript/src/core/DebugToolkit.d.ts.map +1 -1
  50. package/lib/typescript/src/core/initialize.d.ts.map +1 -1
  51. package/lib/typescript/src/features/environment/index.d.ts.map +1 -1
  52. package/lib/typescript/src/features/network/NetworkLogTab.d.ts.map +1 -1
  53. package/lib/typescript/src/features/network/index.d.ts +3 -3
  54. package/lib/typescript/src/features/network/index.d.ts.map +1 -1
  55. package/lib/typescript/src/index.d.ts +2 -2
  56. package/lib/typescript/src/index.d.ts.map +1 -1
  57. package/lib/typescript/src/types/feature.d.ts +5 -0
  58. package/lib/typescript/src/types/feature.d.ts.map +1 -1
  59. package/lib/typescript/src/types/index.d.ts +1 -1
  60. package/lib/typescript/src/types/index.d.ts.map +1 -1
  61. package/lib/typescript/src/utils/DaemonClient.d.ts +5 -11
  62. package/lib/typescript/src/utils/DaemonClient.d.ts.map +1 -1
  63. package/lib/typescript/src/utils/createChannelFeature.d.ts +4 -0
  64. package/lib/typescript/src/utils/createChannelFeature.d.ts.map +1 -1
  65. package/lib/typescript/src/utils/deviceReport.d.ts +10 -1
  66. package/lib/typescript/src/utils/deviceReport.d.ts.map +1 -1
  67. package/lib/typescript/src/utils/urlRewriter.d.ts +5 -0
  68. package/lib/typescript/src/utils/urlRewriter.d.ts.map +1 -0
  69. package/node/daemon/src/console/console.html +197 -27
  70. package/node/daemon/src/server.js +32 -2
  71. package/node/daemon/src/store.js +45 -6
  72. package/node/mcp/src/logs.js +15 -4
  73. package/node/mcp/src/tools.js +4 -2
  74. package/package.json +6 -2
  75. package/src/core/DebugToolkit.tsx +119 -105
  76. package/src/core/initialize.ts +7 -8
  77. package/src/features/environment/index.ts +25 -27
  78. package/src/features/network/NetworkLogTab.tsx +6 -3
  79. package/src/features/network/index.ts +30 -52
  80. package/src/features/network/networkInterceptor.ts +3 -3
  81. package/src/index.ts +3 -8
  82. package/src/types/feature.ts +6 -0
  83. package/src/types/index.ts +1 -0
  84. package/src/utils/DaemonClient.ts +39 -56
  85. package/src/utils/createChannelFeature.ts +12 -1
  86. package/src/utils/deviceReport.ts +12 -3
  87. package/src/utils/urlRewriter.ts +11 -0
  88. package/lib/commonjs/utils/urlRewriterRegistry.js +0 -14
  89. package/lib/commonjs/utils/urlRewriterRegistry.js.map +0 -1
  90. package/lib/module/utils/urlRewriterRegistry.js +0 -10
  91. package/lib/module/utils/urlRewriterRegistry.js.map +0 -1
  92. package/lib/typescript/src/utils/urlRewriterRegistry.d.ts +0 -7
  93. package/lib/typescript/src/utils/urlRewriterRegistry.d.ts.map +0 -1
  94. package/src/utils/urlRewriterRegistry.ts +0 -10
@@ -140,6 +140,25 @@ function toDevicePayload(deviceLog) {
140
140
  };
141
141
  }
142
142
 
143
+ function stripBodies(value, parentKey) {
144
+ if (Array.isArray(value)) {
145
+ return value.map((item) => stripBodies(item, parentKey));
146
+ }
147
+
148
+ if (!value || typeof value !== 'object') {
149
+ return value;
150
+ }
151
+
152
+ return Object.entries(value).reduce((acc, [key, child]) => {
153
+ const normalizedKey = key.toLowerCase();
154
+ if (normalizedKey === 'body' || (parentKey === 'response' && normalizedKey === 'data')) {
155
+ return acc;
156
+ }
157
+ acc[key] = stripBodies(child, normalizedKey);
158
+ return acc;
159
+ }, {});
160
+ }
161
+
143
162
  function selectLogs(deviceLog, searchParams) {
144
163
  if (!deviceLog) {
145
164
  return [];
@@ -150,6 +169,8 @@ function selectLogs(deviceLog, searchParams) {
150
169
  const limit = Number.isFinite(limitParam) && limitParam > 0
151
170
  ? Math.min(Math.floor(limitParam), 500)
152
171
  : 50;
172
+ const entryId = searchParams.get('entryId');
173
+ const includeBodies = entryId ? true : searchParams.get('includeBodies') === 'true';
153
174
  const failedOnly = searchParams.get('failedOnly') === 'true';
154
175
  const logs = deviceLog.report.logs || {};
155
176
 
@@ -160,7 +181,12 @@ function selectLogs(deviceLog, searchParams) {
160
181
  entries = Object.values(logs).flatMap((value) => Array.isArray(value) ? value : []);
161
182
  }
162
183
 
163
- if (failedOnly) {
184
+ if (entryId) {
185
+ entries = entries.filter((entry) => (
186
+ entry && typeof entry === 'object' &&
187
+ (entry.id === entryId || entry.id === Number(entryId))
188
+ ));
189
+ } else if (failedOnly) {
164
190
  entries = entries.filter((entry) => (
165
191
  entry &&
166
192
  typeof entry === 'object' &&
@@ -173,7 +199,11 @@ function selectLogs(deviceLog, searchParams) {
173
199
  ));
174
200
  }
175
201
 
176
- return entries.slice(-limit);
202
+ if (!entryId) {
203
+ entries = entries.slice(-limit);
204
+ }
205
+
206
+ return includeBodies ? entries : entries.map(stripBodies);
177
207
  }
178
208
 
179
209
  function broadcastSSE(clients, eventType, deviceLog, delta) {
@@ -28,15 +28,33 @@ function slugPart(value) {
28
28
  .slice(0, 80) || 'unknown';
29
29
  }
30
30
 
31
+ function ipTail(ip) {
32
+ if (!ip || typeof ip !== 'string') return '0';
33
+ const parts = ip.split('.');
34
+ return parts.length >= 2 ? parts[parts.length - 1] : slugPart(ip);
35
+ }
36
+
37
+ function isSimulatorIp(ip) {
38
+ return ip === '127.0.0.1' || ip === '::1' || ip === '10.0.2.2' || ip === 'localhost';
39
+ }
40
+
31
41
  function createDeviceId(report, source) {
32
42
  const device = report && typeof report === 'object' && report.device && typeof report.device === 'object'
33
43
  ? report.device
34
44
  : {};
35
- return [
36
- slugPart(device.platform),
37
- slugPart(device.model),
38
- slugPart(source && source.ip),
39
- ].join('_');
45
+ const platform = slugPart(device.platform);
46
+ const ip = source && source.ip ? String(source.ip) : '';
47
+ const sim = isSimulatorIp(ip);
48
+ let model = slugPart(device.model);
49
+ if (model === 'unknown' && platform !== 'unknown') {
50
+ model = sim ? 'sim' : 'device';
51
+ }
52
+ const ver = device.appVersion ? slugPart(device.appVersion) : '';
53
+ const tail = sim ? 'sim' : ipTail(ip);
54
+ const parts = [platform, model];
55
+ if (ver && ver !== 'unknown') parts.push(ver);
56
+ parts.push(tail);
57
+ return parts.join('_');
40
58
  }
41
59
 
42
60
  function readPersistedDevices(storagePath, maxDevices) {
@@ -92,6 +110,18 @@ function createMemoryStore(options = {}) {
92
110
  const deviceId = createDeviceId(report, source);
93
111
  const existingIndex = devices.findIndex((item) => item.deviceId === deviceId);
94
112
  const existing = existingIndex >= 0 ? devices[existingIndex] : null;
113
+ const reportSessionId = report.session ? report.session.id : null;
114
+ if (reportSessionId && report.logs) {
115
+ Object.entries(report.logs).forEach(function(pair) {
116
+ if (!Array.isArray(pair[1])) return;
117
+ report.logs[pair[0]] = pair[1].map(function(entry) {
118
+ if (entry && typeof entry === 'object' && !entry.sessionId) {
119
+ return Object.assign({}, entry, { sessionId: reportSessionId });
120
+ }
121
+ return entry;
122
+ });
123
+ });
124
+ }
95
125
  const deviceLog = {
96
126
  deviceId,
97
127
  firstSeenAt: existing ? existing.firstSeenAt : receivedAt,
@@ -99,6 +129,7 @@ function createMemoryStore(options = {}) {
99
129
  receivedAt,
100
130
  source,
101
131
  device: report.device || null,
132
+ session: report.session || null,
102
133
  report,
103
134
  logCount: createLogCount(report),
104
135
  };
@@ -124,6 +155,7 @@ function createMemoryStore(options = {}) {
124
155
  }
125
156
 
126
157
  const deltaLogs = (delta && delta.logs) || {};
158
+ const currentSessionId = deviceLog.session ? deviceLog.session.id : null;
127
159
  Object.entries(deltaLogs).forEach(([type, entries]) => {
128
160
  if (!Array.isArray(entries)) {
129
161
  return;
@@ -131,7 +163,13 @@ function createMemoryStore(options = {}) {
131
163
  if (!deviceLog.report.logs[type]) {
132
164
  deviceLog.report.logs[type] = [];
133
165
  }
134
- deviceLog.report.logs[type].push(...entries);
166
+ const tagged = entries.map(function(entry) {
167
+ if (entry && typeof entry === 'object' && currentSessionId && !entry.sessionId) {
168
+ return Object.assign({}, entry, { sessionId: currentSessionId });
169
+ }
170
+ return entry;
171
+ });
172
+ deviceLog.report.logs[type].push(...tagged);
135
173
  });
136
174
 
137
175
  deviceLog.lastSeenAt = new Date(Date.now()).toISOString();
@@ -153,6 +191,7 @@ function createMemoryStore(options = {}) {
153
191
  receivedAt: deviceLog.receivedAt,
154
192
  device: deviceLog.device || null,
155
193
  source: deviceLog.source || null,
194
+ session: deviceLog.session || null,
156
195
  logCount: deviceLog.logCount,
157
196
  }));
158
197
  }
@@ -40,7 +40,8 @@ function selectLogs(report, options = {}) {
40
40
  const limit = Number.isFinite(options.limit) && options.limit > 0
41
41
  ? Math.min(Math.floor(options.limit), 200)
42
42
  : 50;
43
- const includeBodies = options.includeBodies !== false;
43
+ const entryId = options.entryId;
44
+ const includeBodies = entryId ? true : options.includeBodies === true;
44
45
  const failedOnly = options.failedOnly === true;
45
46
 
46
47
  let entries;
@@ -54,11 +55,18 @@ function selectLogs(report, options = {}) {
54
55
  ));
55
56
  }
56
57
 
57
- if (failedOnly) {
58
+ if (entryId) {
59
+ entries = entries.filter((item) => {
60
+ const e = item.entry || item;
61
+ return e.id === entryId || e.id === Number(entryId);
62
+ });
63
+ } else if (failedOnly) {
58
64
  entries = entries.filter((item) => isFailedLog(item.entry || item));
59
65
  }
60
66
 
61
- entries = entries.slice(-limit);
67
+ if (!entryId) {
68
+ entries = entries.slice(-limit);
69
+ }
62
70
 
63
71
  if (logType) {
64
72
  return includeBodies ? entries : entries.map(stripBodies);
@@ -72,6 +80,8 @@ function selectLogs(report, options = {}) {
72
80
 
73
81
  function createToolPayload(device, options = {}) {
74
82
  const report = device.report || { version: 2, logs: {} };
83
+ const entryId = options.entryId;
84
+ const includeBodies = entryId ? true : options.includeBodies === true;
75
85
  const logs = selectLogs(report, options);
76
86
 
77
87
  return {
@@ -81,7 +91,8 @@ function createToolPayload(device, options = {}) {
81
91
  lastSeenAt: device.lastSeenAt,
82
92
  logType: options.logType || 'all',
83
93
  failedOnly: options.failedOnly === true,
84
- includeBodies: options.includeBodies !== false,
94
+ includeBodies,
95
+ entryId: entryId || undefined,
85
96
  count: logs.length,
86
97
  logs,
87
98
  };
@@ -5,7 +5,7 @@ const { KNOWN_LOG_TYPES, createToolPayload } = require('./logs');
5
5
 
6
6
  const getAppLogsTool = {
7
7
  name: 'get_app_logs',
8
- description: 'Read React Native Debug Toolkit logs from the local daemon. Tip: if you have shell access, curl http://127.0.0.1:3799/devices/latest is more efficient.',
8
+ description: 'Read React Native Debug Toolkit logs from the local daemon. Bodies are excluded by default to reduce token usage; set includeBodies=true or pass an entryId to fetch details. Tip: if you have shell access, curl http://127.0.0.1:3799/devices/latest/logs?includeBodies=true is more efficient.',
9
9
  inputSchema: {
10
10
  type: 'object',
11
11
  properties: {
@@ -16,7 +16,8 @@ const getAppLogsTool = {
16
16
  },
17
17
  limit: { type: 'number', default: 50 },
18
18
  failedOnly: { type: 'boolean', default: false },
19
- includeBodies: { type: 'boolean', default: true },
19
+ includeBodies: { type: 'boolean', default: false },
20
+ entryId: { type: 'string', description: 'Fetch a single entry by ID; forces includeBodies=true' },
20
21
  },
21
22
  },
22
23
  };
@@ -66,6 +67,7 @@ async function callTool(name, args = {}, context = {}) {
66
67
  limit: args.limit,
67
68
  failedOnly: args.failedOnly,
68
69
  includeBodies: args.includeBodies,
70
+ entryId: args.entryId,
69
71
  });
70
72
  } catch (error) {
71
73
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-debug-toolkit",
3
- "version": "3.1.3",
4
- "description": "A local-first React Native debugging bridge with in-app logs, desktop daemon, Web Console, HTTP API, and MCP support",
3
+ "version": "3.1.5",
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",
7
7
  "types": "lib/typescript/src/index.d.ts",
@@ -30,8 +30,12 @@
30
30
  "keywords": [
31
31
  "react-native",
32
32
  "debug",
33
+ "log-bridge",
34
+ "web-console",
35
+ "ai-debugging",
33
36
  "toolkit",
34
37
  "http-inspector",
38
+ "mcp",
35
39
  "development-tools",
36
40
  "floating-panel"
37
41
  ],
@@ -1,176 +1,190 @@
1
- import type { AnyDebugFeature } from '../types';
1
+ import type { AnyDebugFeature, DebugFeatureListener, FeatureDataProvider } from '../types';
2
2
 
3
- type Listener = () => void;
3
+ class DebugToolkitCore implements FeatureDataProvider {
4
+ private _features: AnyDebugFeature[] = [];
5
+ private _launcherVisible = false;
6
+ private _panelOpen = false;
7
+ private _enabled = true;
8
+ private _listeners = new Set<DebugFeatureListener>();
4
9
 
5
- const listeners = new Set<Listener>();
6
- let _features: AnyDebugFeature[] = [];
7
- let _launcherVisible = false;
8
- let _panelOpen = false;
9
- let _enabled = true;
10
+ private notify(): void {
11
+ this._listeners.forEach((l) => l());
12
+ }
10
13
 
11
- function notify(): void {
12
- listeners.forEach((l) => l());
13
- }
14
-
15
- function setupFeature(feature: AnyDebugFeature): void {
16
- feature.setup?.();
17
- }
14
+ // --- FeatureDataProvider ---
18
15
 
19
- function cleanupFeature(feature: AnyDebugFeature): void {
20
- feature.cleanup?.();
21
- }
16
+ get features(): AnyDebugFeature[] {
17
+ return [...this._features];
18
+ }
22
19
 
23
- export const DebugToolkit = {
24
- subscribe(listener: Listener): () => void {
25
- listeners.add(listener);
20
+ subscribe(listener: DebugFeatureListener): () => void {
21
+ this._listeners.add(listener);
26
22
  return () => {
27
- listeners.delete(listener);
23
+ this._listeners.delete(listener);
28
24
  };
29
- },
25
+ }
30
26
 
31
- get features(): AnyDebugFeature[] {
32
- return [..._features];
33
- },
27
+ // --- Enabled ---
34
28
 
35
29
  get enabled(): boolean {
36
- return _enabled;
37
- },
38
-
39
- get launcherVisible(): boolean {
40
- return _launcherVisible;
41
- },
30
+ return this._enabled;
31
+ }
42
32
 
43
33
  setEnabled(enabled: boolean): void {
44
- if (_enabled === enabled) return;
45
- _enabled = enabled;
34
+ if (this._enabled === enabled) return;
35
+ this._enabled = enabled;
46
36
  if (!enabled) {
47
- _launcherVisible = false;
48
- _features.forEach(cleanupFeature);
49
- _features = [];
37
+ this._launcherVisible = false;
38
+ this._features.forEach((f) => f.cleanup?.());
39
+ this._features = [];
50
40
  }
51
- notify();
52
- },
41
+ this.notify();
42
+ }
43
+
44
+ // --- Feature Management ---
53
45
 
54
46
  replaceFeatures(features: AnyDebugFeature[]): void {
55
- if (!_enabled) return;
47
+ if (!this._enabled) return;
56
48
  const next = features.filter(
57
49
  (f, i, arr) =>
58
50
  f && typeof f.name === 'string' && arr.findIndex((item) => item.name === f.name) === i,
59
51
  );
60
- const prevMap = new Map(_features.map((f) => [f.name, f]));
52
+ const prevMap = new Map(this._features.map((f) => [f.name, f]));
61
53
  const nextMap = new Map(next.map((f) => [f.name, f]));
62
54
 
63
- _features.forEach((f) => {
64
- if (!nextMap.get(f.name) || nextMap.get(f.name) !== f) cleanupFeature(f);
55
+ this._features.forEach((f) => {
56
+ if (!nextMap.get(f.name) || nextMap.get(f.name) !== f) f.cleanup?.();
65
57
  });
66
58
  next.forEach((f) => {
67
- if (!prevMap.get(f.name) || prevMap.get(f.name) !== f) setupFeature(f);
59
+ if (!prevMap.get(f.name) || prevMap.get(f.name) !== f) f.setup?.();
68
60
  });
69
61
 
70
- _features = next;
71
- notify();
72
- },
62
+ this._features = next;
63
+ this.notify();
64
+ }
73
65
 
74
66
  addFeature(feature: AnyDebugFeature): void {
75
- if (!_enabled || !feature || typeof feature.name !== 'string') {
67
+ if (!this._enabled || !feature || typeof feature.name !== 'string') {
76
68
  return;
77
69
  }
78
70
 
79
- const existingIndex = _features.findIndex((f) => f.name === feature.name);
71
+ const existingIndex = this._features.findIndex((f) => f.name === feature.name);
80
72
  if (existingIndex >= 0) {
81
- const existing = _features[existingIndex]!;
73
+ const existing = this._features[existingIndex]!;
82
74
  if (existing === feature) {
83
75
  return;
84
76
  }
85
77
 
86
- cleanupFeature(existing);
87
- setupFeature(feature);
88
- _features = [
89
- ..._features.slice(0, existingIndex),
78
+ existing.cleanup?.();
79
+ feature.setup?.();
80
+ this._features = [
81
+ ...this._features.slice(0, existingIndex),
90
82
  feature,
91
- ..._features.slice(existingIndex + 1),
83
+ ...this._features.slice(existingIndex + 1),
92
84
  ];
93
- notify();
85
+ this.notify();
94
86
  return;
95
87
  }
96
88
 
97
- setupFeature(feature);
98
- _features = [..._features, feature];
99
- notify();
100
- },
89
+ feature.setup?.();
90
+ this._features = [...this._features, feature];
91
+ this.notify();
92
+ }
101
93
 
102
94
  removeFeature(name: string): void {
103
- if (!_enabled) {
95
+ if (!this._enabled) {
104
96
  return;
105
97
  }
106
98
 
107
- const feature = _features.find((f) => f.name === name);
99
+ const feature = this._features.find((f) => f.name === name);
108
100
  if (!feature) {
109
101
  return;
110
102
  }
111
103
 
112
- cleanupFeature(feature);
113
- _features = _features.filter((f) => f.name !== name);
114
- if (_features.length === 0) {
115
- _launcherVisible = false;
104
+ feature.cleanup?.();
105
+ this._features = this._features.filter((f) => f.name !== name);
106
+ if (this._features.length === 0) {
107
+ this._launcherVisible = false;
116
108
  }
117
- notify();
118
- },
119
-
120
- reset(): void {
121
- _launcherVisible = false;
122
- _panelOpen = false;
123
- _features.forEach(cleanupFeature);
124
- _features = [];
125
- notify();
126
- },
109
+ this.notify();
110
+ }
127
111
 
128
112
  hasFeatures(): boolean {
129
- return _features.length > 0;
130
- },
113
+ return this._features.length > 0;
114
+ }
115
+
116
+ // --- Panel ---
131
117
 
132
118
  get panelOpen(): boolean {
133
- return _panelOpen;
134
- },
119
+ return this._panelOpen;
120
+ }
135
121
 
136
122
  openPanel(): void {
137
- if (!_enabled || _panelOpen) return;
138
- _panelOpen = true;
139
- notify();
140
- },
123
+ if (!this._enabled || this._panelOpen) return;
124
+ this._panelOpen = true;
125
+ this.notify();
126
+ }
141
127
 
142
128
  closePanel(): void {
143
- if (!_panelOpen) return;
144
- _panelOpen = false;
145
- notify();
146
- },
129
+ if (!this._panelOpen) return;
130
+ this._panelOpen = false;
131
+ this.notify();
132
+ }
147
133
 
148
134
  togglePanel(): void {
149
- if (_panelOpen) {
150
- DebugToolkit.closePanel();
135
+ if (this._panelOpen) {
136
+ this.closePanel();
151
137
  } else {
152
- DebugToolkit.openPanel();
138
+ this.openPanel();
153
139
  }
154
- },
140
+ }
141
+
142
+ // --- Launcher ---
143
+
144
+ get launcherVisible(): boolean {
145
+ return this._launcherVisible;
146
+ }
155
147
 
156
148
  showLauncher(): void {
157
- if (!_enabled) return;
158
- _launcherVisible = true;
159
- notify();
160
- },
149
+ if (!this._enabled) return;
150
+ this._launcherVisible = true;
151
+ this.notify();
152
+ }
161
153
 
162
154
  hideLauncher(): void {
163
- _launcherVisible = false;
164
- notify();
165
- },
155
+ if (!this._launcherVisible) return;
156
+ this._launcherVisible = false;
157
+ this.notify();
158
+ }
159
+
160
+ // --- Bulk Operations ---
166
161
 
167
162
  clearAll(): void {
168
- _features.forEach((f) => f.clear?.());
169
- notify();
170
- },
163
+ this._features.forEach((f) => f.clear?.());
164
+ this.notify();
165
+ }
166
+
167
+ reset(): void {
168
+ this._launcherVisible = false;
169
+ this._panelOpen = false;
170
+ this._features.forEach((f) => f.cleanup?.());
171
+ this._features = [];
172
+ this.notify();
173
+ }
171
174
 
172
175
  destroy(): void {
173
- DebugToolkit.reset();
174
- listeners.clear();
175
- },
176
- };
176
+ this.reset();
177
+ this._listeners.clear();
178
+ }
179
+ }
180
+
181
+ /** Module-level default instance for non-React scenarios and backward compatibility. */
182
+ export const debugToolkit = new DebugToolkitCore();
183
+
184
+ export type DebugToolkit = DebugToolkitCore;
185
+
186
+ /**
187
+ * @deprecated Use `debugToolkit` or get instance from `initializeDebugToolkit()`.
188
+ * This is the default module-level instance exported for backward compatibility.
189
+ */
190
+ export const DebugToolkit = debugToolkit;
@@ -1,5 +1,5 @@
1
1
  import { DebugToolkit } from './DebugToolkit';
2
- import { createNetworkFeature } from '../features/network';
2
+ import { createNetworkFeature, addToBlacklist } from '../features/network';
3
3
  import type { NetworkFeatureConfig } from '../features/network';
4
4
  import { createConsoleLogFeature } from '../features/console';
5
5
  import type { ConsoleFeatureConfig } from '../features/console';
@@ -11,16 +11,11 @@ import { createTrackFeature } from '../features/track';
11
11
  import type { TrackFeatureConfig } from '../features/track';
12
12
  import { createEnvironmentFeature } from '../features/environment';
13
13
  import { createClipboardFeature } from '../features/clipboard';
14
- import { daemonClient, restoreDaemonStreaming } from '../utils/DaemonClient';
15
- import { _addDaemonEndpointToNetworkBlacklist } from '../features/network';
14
+ import { daemonClient } from '../utils/DaemonClient';
16
15
  import type { AnyDebugFeature, BuiltInFeatureName } from '../types';
17
16
 
18
17
  const isDebugMode = __DEV__;
19
18
 
20
- daemonClient.setEndpointDetector((url) => {
21
- _addDaemonEndpointToNetworkBlacklist(url);
22
- });
23
-
24
19
  /** Feature-specific configuration map */
25
20
  export interface FeatureConfigs {
26
21
  network?: boolean | NetworkFeatureConfig;
@@ -114,13 +109,17 @@ export function initializeDebugToolkit(
114
109
 
115
110
  DebugToolkit.replaceFeatures(resolvedFeatures);
116
111
 
112
+ daemonClient.setEndpointDetector((url) => {
113
+ addToBlacklist(url);
114
+ });
115
+
117
116
  if (DebugToolkit.hasFeatures()) {
118
117
  DebugToolkit.showLauncher();
119
118
  } else {
120
119
  DebugToolkit.hideLauncher();
121
120
  }
122
121
 
123
- restoreDaemonStreaming().catch(() => {});
122
+ daemonClient.restore().catch(() => {});
124
123
 
125
124
  return DebugToolkit;
126
125
  } catch (error) {