react-native-debug-toolkit 0.6.4 → 2.0.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.
Files changed (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -212
  3. package/lib/commonjs/components/ClipboardTab.js +92 -0
  4. package/lib/commonjs/components/ClipboardTab.js.map +1 -0
  5. package/lib/commonjs/components/ConsoleLogTab.js +295 -0
  6. package/lib/commonjs/components/ConsoleLogTab.js.map +1 -0
  7. package/lib/commonjs/components/EnvironmentTab.js +288 -0
  8. package/lib/commonjs/components/EnvironmentTab.js.map +1 -0
  9. package/lib/commonjs/components/FloatPanelView.js +797 -0
  10. package/lib/commonjs/components/FloatPanelView.js.map +1 -0
  11. package/lib/commonjs/components/NavigationLogTab.js +131 -0
  12. package/lib/commonjs/components/NavigationLogTab.js.map +1 -0
  13. package/lib/commonjs/components/NetworkLogTab.js +575 -0
  14. package/lib/commonjs/components/NetworkLogTab.js.map +1 -0
  15. package/lib/commonjs/components/ThirdPartyLibsTab.js +97 -0
  16. package/lib/commonjs/components/ThirdPartyLibsTab.js.map +1 -0
  17. package/lib/commonjs/components/TrackLogTab.js +372 -0
  18. package/lib/commonjs/components/TrackLogTab.js.map +1 -0
  19. package/lib/commonjs/components/ZustandLogTab.js +451 -0
  20. package/lib/commonjs/components/ZustandLogTab.js.map +1 -0
  21. package/lib/commonjs/components/shared/CollapsibleSection.js +84 -0
  22. package/lib/commonjs/components/shared/CollapsibleSection.js.map +1 -0
  23. package/lib/commonjs/components/shared/CopyButton.js +64 -0
  24. package/lib/commonjs/components/shared/CopyButton.js.map +1 -0
  25. package/lib/commonjs/components/shared/JsonView.js +157 -0
  26. package/lib/commonjs/components/shared/JsonView.js.map +1 -0
  27. package/lib/commonjs/core/DebugToolkit.js +142 -0
  28. package/lib/commonjs/core/DebugToolkit.js.map +1 -0
  29. package/lib/commonjs/core/DebugToolkitProvider.js +64 -0
  30. package/lib/commonjs/core/DebugToolkitProvider.js.map +1 -0
  31. package/lib/commonjs/features/ClipboardFeature.js +17 -0
  32. package/lib/commonjs/features/ClipboardFeature.js.map +1 -0
  33. package/lib/commonjs/features/ConsoleLogFeature.js +98 -0
  34. package/lib/commonjs/features/ConsoleLogFeature.js.map +1 -0
  35. package/lib/commonjs/features/EnvironmentFeature.js +168 -0
  36. package/lib/commonjs/features/EnvironmentFeature.js.map +1 -0
  37. package/lib/commonjs/features/NavigationLogFeature.js +60 -0
  38. package/lib/commonjs/features/NavigationLogFeature.js.map +1 -0
  39. package/lib/commonjs/features/NetworkFeature.js +328 -0
  40. package/lib/commonjs/features/NetworkFeature.js.map +1 -0
  41. package/lib/commonjs/features/ThirdPartyLibsFeature.js +51 -0
  42. package/lib/commonjs/features/ThirdPartyLibsFeature.js.map +1 -0
  43. package/lib/commonjs/features/TrackFeature.js +55 -0
  44. package/lib/commonjs/features/TrackFeature.js.map +1 -0
  45. package/lib/commonjs/features/ZustandLogFeature.js +76 -0
  46. package/lib/commonjs/features/ZustandLogFeature.js.map +1 -0
  47. package/lib/commonjs/hooks/useNavigationLogger.js +78 -0
  48. package/lib/commonjs/hooks/useNavigationLogger.js.map +1 -0
  49. package/lib/commonjs/hooks/useSlideDetailAnimation.js +53 -0
  50. package/lib/commonjs/hooks/useSlideDetailAnimation.js.map +1 -0
  51. package/lib/commonjs/index.js +165 -0
  52. package/lib/commonjs/index.js.map +1 -0
  53. package/lib/commonjs/initialize.js +161 -0
  54. package/lib/commonjs/initialize.js.map +1 -0
  55. package/lib/commonjs/native/NativeDebugLibs.js +58 -0
  56. package/lib/commonjs/native/NativeDebugLibs.js.map +1 -0
  57. package/lib/commonjs/package.json +1 -0
  58. package/lib/commonjs/types/index.js +6 -0
  59. package/lib/commonjs/types/index.js.map +1 -0
  60. package/lib/commonjs/utils/constants.js +135 -0
  61. package/lib/commonjs/utils/constants.js.map +1 -0
  62. package/lib/commonjs/utils/copyToComputer.js +97 -0
  63. package/lib/commonjs/utils/copyToComputer.js.map +1 -0
  64. package/lib/commonjs/utils/createEventChannel.js +21 -0
  65. package/lib/commonjs/utils/createEventChannel.js.map +1 -0
  66. package/lib/commonjs/utils/createObservableStore.js +38 -0
  67. package/lib/commonjs/utils/createObservableStore.js.map +1 -0
  68. package/lib/commonjs/utils/safeStringify.js +27 -0
  69. package/lib/commonjs/utils/safeStringify.js.map +1 -0
  70. package/lib/module/components/ClipboardTab.js +86 -0
  71. package/lib/module/components/ClipboardTab.js.map +1 -0
  72. package/lib/module/components/ConsoleLogTab.js +290 -0
  73. package/lib/module/components/ConsoleLogTab.js.map +1 -0
  74. package/lib/module/components/EnvironmentTab.js +282 -0
  75. package/lib/module/components/EnvironmentTab.js.map +1 -0
  76. package/lib/module/components/FloatPanelView.js +791 -0
  77. package/lib/module/components/FloatPanelView.js.map +1 -0
  78. package/lib/module/components/NavigationLogTab.js +126 -0
  79. package/lib/module/components/NavigationLogTab.js.map +1 -0
  80. package/lib/module/components/NetworkLogTab.js +570 -0
  81. package/lib/module/components/NetworkLogTab.js.map +1 -0
  82. package/lib/module/components/ThirdPartyLibsTab.js +91 -0
  83. package/lib/module/components/ThirdPartyLibsTab.js.map +1 -0
  84. package/lib/module/components/TrackLogTab.js +367 -0
  85. package/lib/module/components/TrackLogTab.js.map +1 -0
  86. package/lib/module/components/ZustandLogTab.js +446 -0
  87. package/lib/module/components/ZustandLogTab.js.map +1 -0
  88. package/lib/module/components/shared/CollapsibleSection.js +78 -0
  89. package/lib/module/components/shared/CollapsibleSection.js.map +1 -0
  90. package/lib/module/components/shared/CopyButton.js +58 -0
  91. package/lib/module/components/shared/CopyButton.js.map +1 -0
  92. package/lib/module/components/shared/JsonView.js +152 -0
  93. package/lib/module/components/shared/JsonView.js.map +1 -0
  94. package/lib/module/core/DebugToolkit.js +137 -0
  95. package/lib/module/core/DebugToolkit.js.map +1 -0
  96. package/lib/module/core/DebugToolkitProvider.js +58 -0
  97. package/lib/module/core/DebugToolkitProvider.js.map +1 -0
  98. package/lib/module/features/ClipboardFeature.js +12 -0
  99. package/lib/module/features/ClipboardFeature.js.map +1 -0
  100. package/lib/module/features/ConsoleLogFeature.js +93 -0
  101. package/lib/module/features/ConsoleLogFeature.js.map +1 -0
  102. package/lib/module/features/EnvironmentFeature.js +164 -0
  103. package/lib/module/features/EnvironmentFeature.js.map +1 -0
  104. package/lib/module/features/NavigationLogFeature.js +54 -0
  105. package/lib/module/features/NavigationLogFeature.js.map +1 -0
  106. package/lib/module/features/NetworkFeature.js +322 -0
  107. package/lib/module/features/NetworkFeature.js.map +1 -0
  108. package/lib/module/features/ThirdPartyLibsFeature.js +46 -0
  109. package/lib/module/features/ThirdPartyLibsFeature.js.map +1 -0
  110. package/lib/module/features/TrackFeature.js +49 -0
  111. package/lib/module/features/TrackFeature.js.map +1 -0
  112. package/lib/module/features/ZustandLogFeature.js +69 -0
  113. package/lib/module/features/ZustandLogFeature.js.map +1 -0
  114. package/lib/module/hooks/useNavigationLogger.js +74 -0
  115. package/lib/module/hooks/useNavigationLogger.js.map +1 -0
  116. package/lib/module/hooks/useSlideDetailAnimation.js +50 -0
  117. package/lib/module/hooks/useSlideDetailAnimation.js.map +1 -0
  118. package/lib/module/index.js +29 -0
  119. package/lib/module/index.js.map +1 -0
  120. package/lib/module/initialize.js +156 -0
  121. package/lib/module/initialize.js.map +1 -0
  122. package/lib/module/native/NativeDebugLibs.js +54 -0
  123. package/lib/module/native/NativeDebugLibs.js.map +1 -0
  124. package/lib/module/package.json +1 -0
  125. package/lib/module/types/index.js +4 -0
  126. package/lib/module/types/index.js.map +1 -0
  127. package/lib/module/utils/constants.js +130 -0
  128. package/lib/module/utils/constants.js.map +1 -0
  129. package/lib/module/utils/copyToComputer.js +91 -0
  130. package/lib/module/utils/copyToComputer.js.map +1 -0
  131. package/lib/module/utils/createEventChannel.js +17 -0
  132. package/lib/module/utils/createEventChannel.js.map +1 -0
  133. package/lib/module/utils/createObservableStore.js +34 -0
  134. package/lib/module/utils/createObservableStore.js.map +1 -0
  135. package/lib/module/utils/safeStringify.js +23 -0
  136. package/lib/module/utils/safeStringify.js.map +1 -0
  137. package/lib/typescript/src/components/ClipboardTab.d.ts +4 -0
  138. package/lib/typescript/src/components/ClipboardTab.d.ts.map +1 -0
  139. package/lib/typescript/src/components/ConsoleLogTab.d.ts +4 -0
  140. package/lib/typescript/src/components/ConsoleLogTab.d.ts.map +1 -0
  141. package/lib/typescript/src/components/EnvironmentTab.d.ts +4 -0
  142. package/lib/typescript/src/components/EnvironmentTab.d.ts.map +1 -0
  143. package/lib/typescript/src/components/FloatPanelView.d.ts +64 -0
  144. package/lib/typescript/src/components/FloatPanelView.d.ts.map +1 -0
  145. package/lib/typescript/src/components/NavigationLogTab.d.ts +4 -0
  146. package/lib/typescript/src/components/NavigationLogTab.d.ts.map +1 -0
  147. package/lib/typescript/src/components/NetworkLogTab.d.ts +4 -0
  148. package/lib/typescript/src/components/NetworkLogTab.d.ts.map +1 -0
  149. package/lib/typescript/src/components/ThirdPartyLibsTab.d.ts +4 -0
  150. package/lib/typescript/src/components/ThirdPartyLibsTab.d.ts.map +1 -0
  151. package/lib/typescript/src/components/TrackLogTab.d.ts +4 -0
  152. package/lib/typescript/src/components/TrackLogTab.d.ts.map +1 -0
  153. package/lib/typescript/src/components/ZustandLogTab.d.ts +4 -0
  154. package/lib/typescript/src/components/ZustandLogTab.d.ts.map +1 -0
  155. package/lib/typescript/src/components/shared/CollapsibleSection.d.ts +9 -0
  156. package/lib/typescript/src/components/shared/CollapsibleSection.d.ts.map +1 -0
  157. package/lib/typescript/src/components/shared/CopyButton.d.ts +12 -0
  158. package/lib/typescript/src/components/shared/CopyButton.d.ts.map +1 -0
  159. package/lib/typescript/src/components/shared/JsonView.d.ts +6 -0
  160. package/lib/typescript/src/components/shared/JsonView.d.ts.map +1 -0
  161. package/lib/typescript/src/core/DebugToolkit.d.ts +27 -0
  162. package/lib/typescript/src/core/DebugToolkit.d.ts.map +1 -0
  163. package/lib/typescript/src/core/DebugToolkitProvider.d.ts +18 -0
  164. package/lib/typescript/src/core/DebugToolkitProvider.d.ts.map +1 -0
  165. package/lib/typescript/src/features/ClipboardFeature.d.ts +3 -0
  166. package/lib/typescript/src/features/ClipboardFeature.d.ts.map +1 -0
  167. package/lib/typescript/src/features/ConsoleLogFeature.d.ts +7 -0
  168. package/lib/typescript/src/features/ConsoleLogFeature.d.ts.map +1 -0
  169. package/lib/typescript/src/features/EnvironmentFeature.d.ts +8 -0
  170. package/lib/typescript/src/features/EnvironmentFeature.d.ts.map +1 -0
  171. package/lib/typescript/src/features/NavigationLogFeature.d.ts +8 -0
  172. package/lib/typescript/src/features/NavigationLogFeature.d.ts.map +1 -0
  173. package/lib/typescript/src/features/NetworkFeature.d.ts +28 -0
  174. package/lib/typescript/src/features/NetworkFeature.d.ts.map +1 -0
  175. package/lib/typescript/src/features/ThirdPartyLibsFeature.d.ts +3 -0
  176. package/lib/typescript/src/features/ThirdPartyLibsFeature.d.ts.map +1 -0
  177. package/lib/typescript/src/features/TrackFeature.d.ts +12 -0
  178. package/lib/typescript/src/features/TrackFeature.d.ts.map +1 -0
  179. package/lib/typescript/src/features/ZustandLogFeature.d.ts +29 -0
  180. package/lib/typescript/src/features/ZustandLogFeature.d.ts.map +1 -0
  181. package/lib/typescript/src/hooks/useNavigationLogger.d.ts +20 -0
  182. package/lib/typescript/src/hooks/useNavigationLogger.d.ts.map +1 -0
  183. package/lib/typescript/src/hooks/useSlideDetailAnimation.d.ts +11 -0
  184. package/lib/typescript/src/hooks/useSlideDetailAnimation.d.ts.map +1 -0
  185. package/lib/typescript/src/index.d.ts +26 -0
  186. package/lib/typescript/src/index.d.ts.map +1 -0
  187. package/lib/typescript/src/initialize.d.ts +51 -0
  188. package/lib/typescript/src/initialize.d.ts.map +1 -0
  189. package/lib/typescript/src/native/NativeDebugLibs.d.ts +11 -0
  190. package/lib/typescript/src/native/NativeDebugLibs.d.ts.map +1 -0
  191. package/lib/typescript/src/types/index.d.ts +112 -0
  192. package/lib/typescript/src/types/index.d.ts.map +1 -0
  193. package/lib/typescript/src/utils/constants.d.ts +96 -0
  194. package/lib/typescript/src/utils/constants.d.ts.map +1 -0
  195. package/lib/typescript/src/utils/copyToComputer.d.ts +30 -0
  196. package/lib/typescript/src/utils/copyToComputer.d.ts.map +1 -0
  197. package/lib/typescript/src/utils/createEventChannel.d.ts +7 -0
  198. package/lib/typescript/src/utils/createEventChannel.d.ts.map +1 -0
  199. package/lib/typescript/src/utils/createObservableStore.d.ts +9 -0
  200. package/lib/typescript/src/utils/createObservableStore.d.ts.map +1 -0
  201. package/lib/typescript/src/utils/safeStringify.d.ts +6 -0
  202. package/lib/typescript/src/utils/safeStringify.d.ts.map +1 -0
  203. package/package.json +63 -24
  204. package/src/components/ClipboardTab.tsx +81 -0
  205. package/src/components/ConsoleLogTab.tsx +209 -0
  206. package/src/components/EnvironmentTab.tsx +276 -0
  207. package/src/components/FloatPanelView.tsx +714 -0
  208. package/src/components/NavigationLogTab.tsx +66 -0
  209. package/src/components/NetworkLogTab.tsx +411 -0
  210. package/src/components/ThirdPartyLibsTab.tsx +63 -0
  211. package/src/components/TrackLogTab.tsx +245 -0
  212. package/src/components/ZustandLogTab.tsx +305 -0
  213. package/src/components/shared/CollapsibleSection.tsx +78 -0
  214. package/src/components/shared/CopyButton.tsx +68 -0
  215. package/src/components/shared/JsonView.tsx +125 -0
  216. package/src/core/DebugToolkit.tsx +174 -0
  217. package/src/core/DebugToolkitProvider.tsx +89 -0
  218. package/src/features/ClipboardFeature.ts +11 -0
  219. package/src/features/ConsoleLogFeature.ts +118 -0
  220. package/src/features/EnvironmentFeature.ts +194 -0
  221. package/src/features/NavigationLogFeature.ts +74 -0
  222. package/src/features/NetworkFeature.ts +488 -0
  223. package/src/features/ThirdPartyLibsFeature.ts +42 -0
  224. package/src/features/TrackFeature.ts +69 -0
  225. package/src/features/ZustandLogFeature.ts +127 -0
  226. package/src/hooks/useNavigationLogger.ts +107 -0
  227. package/src/hooks/useSlideDetailAnimation.ts +45 -0
  228. package/src/index.ts +52 -0
  229. package/src/initialize.ts +214 -0
  230. package/src/native/NativeDebugLibs.ts +74 -0
  231. package/src/types/index.ts +138 -0
  232. package/src/utils/constants.ts +91 -0
  233. package/src/utils/copyToComputer.ts +104 -0
  234. package/src/utils/createEventChannel.ts +22 -0
  235. package/src/utils/createObservableStore.ts +42 -0
  236. package/src/utils/safeStringify.ts +25 -0
  237. package/.cursor/rules/react-native.mdc +0 -41
  238. package/README.zh-CN.md +0 -230
  239. package/index.js +0 -41
  240. package/ios/BuildTypeModule.h +0 -9
  241. package/ios/BuildTypeModule.m +0 -42
  242. package/ios/RNDebugLibs.h +0 -10
  243. package/ios/RNDebugLibs.m +0 -79
  244. package/lib/DebugToolKit.js +0 -126
  245. package/lib/EnvironmentManager.ts +0 -80
  246. package/lib/NativeDebugLibs.js +0 -67
  247. package/lib/features/ConsoleLogFeature.js +0 -70
  248. package/lib/features/NavigationLogFeature.js +0 -45
  249. package/lib/features/NetworkFeature.js +0 -389
  250. package/lib/features/PerformanceFeature.js +0 -390
  251. package/lib/features/ThirdPartyLibsFeature.js +0 -63
  252. package/lib/features/TrackFeature.js +0 -94
  253. package/lib/features/ZustandLogFeature.js +0 -44
  254. package/lib/hooks/useNavigationLogger.js +0 -92
  255. package/lib/index.js +0 -114
  256. package/lib/navigation/NavigationLogger.js +0 -1
  257. package/lib/types/TrackTypes.ts +0 -92
  258. package/lib/utils/DebugConst.js +0 -67
  259. package/lib/utils/StorageUtils.js +0 -80
  260. package/lib/views/ConsoleLogDetails.js +0 -314
  261. package/lib/views/FloatPanelView.js +0 -697
  262. package/lib/views/HttpLogDetails.js +0 -648
  263. package/lib/views/NavigationLogDetails.js +0 -302
  264. package/lib/views/RestartModal.js +0 -75
  265. package/lib/views/SubViewConsoleLogs.js +0 -209
  266. package/lib/views/SubViewEnvironment.js +0 -73
  267. package/lib/views/SubViewHTTPLogs.js +0 -235
  268. package/lib/views/SubViewNavigationLogs.js +0 -199
  269. package/lib/views/SubViewPerformance.js +0 -515
  270. package/lib/views/SubViewThirdPartyLibs.js +0 -239
  271. package/lib/views/SubViewTrackLogs.js +0 -318
  272. package/lib/views/SubViewZustandLogs.js +0 -279
  273. package/lib/views/TabView.js +0 -66
  274. package/lib/views/TrackLogDetails.js +0 -481
  275. package/lib/views/ZustandLogDetails.js +0 -355
  276. package/react-native-debug-toolkit.podspec +0 -25
  277. package/react-native.config.js +0 -18
@@ -0,0 +1,74 @@
1
+ import { NavigationLogTab } from '../components/NavigationLogTab';
2
+ import type { DebugFeature, NavigationLogEntry } from '../types';
3
+ import { createEventChannel } from '../utils/createEventChannel';
4
+ import { createObservableStore } from '../utils/createObservableStore';
5
+
6
+ type NavigationLogPayload = Omit<NavigationLogEntry, 'id'>;
7
+
8
+ const DEFAULT_MAX_LOGS = 200;
9
+ const navigationChannel = createEventChannel<NavigationLogPayload>();
10
+
11
+ export const addNavigationLog = (
12
+ action: string,
13
+ from: string,
14
+ to: string,
15
+ startTime?: number,
16
+ duration?: number,
17
+ debugLog?: string,
18
+ ): void => {
19
+ navigationChannel.emit({
20
+ timestamp: Date.now(),
21
+ action,
22
+ from,
23
+ to,
24
+ startTime,
25
+ duration,
26
+ debugLog,
27
+ });
28
+ };
29
+
30
+ export interface NavigationFeatureConfig {
31
+ /** Maximum number of navigation logs to keep (default: 200) */
32
+ maxLogs?: number;
33
+ }
34
+
35
+ export const createNavigationLogFeature = (config?: NavigationFeatureConfig): DebugFeature<NavigationLogEntry> => {
36
+ const maxLogs = config?.maxLogs ?? DEFAULT_MAX_LOGS;
37
+ const logStore = createObservableStore<NavigationLogEntry>();
38
+ let nextId = 0;
39
+ let unsubscribeLogs: (() => void) | null = null;
40
+ let initialized = false;
41
+
42
+ return {
43
+ name: 'navigation',
44
+ label: 'Navigation',
45
+ renderContent: NavigationLogTab,
46
+ setup: () => {
47
+ if (initialized) {
48
+ return;
49
+ }
50
+
51
+ unsubscribeLogs = navigationChannel.subscribe((entry) => {
52
+ logStore.push(
53
+ {
54
+ ...entry,
55
+ id: String(nextId++),
56
+ },
57
+ maxLogs,
58
+ );
59
+ });
60
+ initialized = true;
61
+ },
62
+ getData: () => logStore.getData(),
63
+ clear: () => {
64
+ logStore.clear();
65
+ },
66
+ cleanup: () => {
67
+ unsubscribeLogs?.();
68
+ unsubscribeLogs = null;
69
+ logStore.clear();
70
+ initialized = false;
71
+ },
72
+ subscribe: (listener) => logStore.subscribe(listener),
73
+ };
74
+ };
@@ -0,0 +1,488 @@
1
+ import { NetworkLogTab } from '../components/NetworkLogTab';
2
+ import type { DebugFeature, NetworkLogEntry } from '../types';
3
+ import { createEventChannel } from '../utils/createEventChannel';
4
+ import { createObservableStore } from '../utils/createObservableStore';
5
+
6
+ type NetworkLogPayload = Omit<NetworkLogEntry, 'id'>;
7
+
8
+ interface AxiosInterceptorManager {
9
+ use: (onFulfilled: unknown, onRejected?: unknown) => number | void;
10
+ eject?: (id: number) => void;
11
+ }
12
+
13
+ interface AxiosRequestConfigLike {
14
+ baseURL?: string;
15
+ url?: string;
16
+ method?: string;
17
+ headers?: Record<string, string>;
18
+ data?: unknown;
19
+ params?: unknown;
20
+ }
21
+
22
+ interface AxiosResponseLike {
23
+ status: number;
24
+ statusText: string;
25
+ headers?: Record<string, string>;
26
+ data?: unknown;
27
+ config: AxiosRequestConfigLike;
28
+ }
29
+
30
+ interface AxiosErrorLike {
31
+ message: string;
32
+ config?: AxiosRequestConfigLike;
33
+ }
34
+
35
+ interface AxiosInstanceLike {
36
+ interceptors: {
37
+ request: AxiosInterceptorManager;
38
+ response: AxiosInterceptorManager;
39
+ };
40
+ }
41
+
42
+ interface AxiosRegistration {
43
+ refCount: number;
44
+ requestId?: number;
45
+ responseId?: number;
46
+ }
47
+
48
+ const DEFAULT_MAX_LOGS = 200;
49
+ const networkChannel = createEventChannel<NetworkLogPayload>();
50
+ const pendingAxiosRequests = new Map<string, { startTime: number; timestamp: number }>();
51
+ const axiosRegistrations = new WeakMap<AxiosInstanceLike, AxiosRegistration>();
52
+
53
+ let originalFetch: typeof globalThis.fetch | null = null;
54
+ let fetchInterceptorRefCount = 0;
55
+ let urlRewriter: ((url: string) => string) | null = null;
56
+
57
+ export function setUrlRewriter(rewriter: ((url: string) => string) | null): void {
58
+ urlRewriter = rewriter;
59
+ }
60
+
61
+ function rewriteUrl(url: string): string {
62
+ if (!urlRewriter) return url;
63
+ try {
64
+ return urlRewriter(url);
65
+ } catch {
66
+ return url;
67
+ }
68
+ }
69
+
70
+ function emitNetworkLog(entry: NetworkLogPayload): void {
71
+ networkChannel.emit(entry);
72
+ }
73
+
74
+ function headersToObject(
75
+ headers: Record<string, string> | Headers | undefined,
76
+ ): Record<string, string> {
77
+ const result: Record<string, string> = {};
78
+
79
+ if (!headers) {
80
+ return result;
81
+ }
82
+
83
+ if (typeof (headers as Headers).forEach === 'function') {
84
+ (headers as Headers).forEach((value: string, key: string) => {
85
+ result[key] = value;
86
+ });
87
+ return result;
88
+ }
89
+
90
+ Object.keys(headers).forEach((key) => {
91
+ result[key] = (headers as Record<string, string>)[key]!;
92
+ });
93
+
94
+ return result;
95
+ }
96
+
97
+ function getRequestSnapshot(
98
+ input: unknown,
99
+ init?: RequestInit,
100
+ ): NetworkLogEntry['request'] {
101
+ const request = input instanceof Request ? input : null;
102
+
103
+ return {
104
+ url:
105
+ typeof input === 'string'
106
+ ? input
107
+ : request?.url ?? String(input),
108
+ method: (init?.method || request?.method || 'GET').toUpperCase(),
109
+ headers: init?.headers
110
+ ? headersToObject(init.headers as Record<string, string> | Headers)
111
+ : request?.headers
112
+ ? headersToObject(request.headers)
113
+ : undefined,
114
+ body: init?.body,
115
+ };
116
+ }
117
+
118
+ async function parseResponseBody(response: Response): Promise<unknown> {
119
+ const raw = await response.clone().text();
120
+
121
+ if (!raw) {
122
+ return undefined;
123
+ }
124
+
125
+ try {
126
+ return JSON.parse(raw);
127
+ } catch {
128
+ return raw;
129
+ }
130
+ }
131
+
132
+ function stopFetchInterceptor(): void {
133
+ fetchInterceptorRefCount = Math.max(0, fetchInterceptorRefCount - 1);
134
+
135
+ if (fetchInterceptorRefCount === 0 && originalFetch) {
136
+ globalThis.fetch = originalFetch;
137
+ originalFetch = null;
138
+ }
139
+ }
140
+
141
+ function startFetchInterceptor(): () => void {
142
+ fetchInterceptorRefCount += 1;
143
+
144
+ if (originalFetch) {
145
+ return () => {
146
+ stopFetchInterceptor();
147
+ };
148
+ }
149
+
150
+ originalFetch = globalThis.fetch;
151
+
152
+ globalThis.fetch = async function interceptedFetch(
153
+ input: Parameters<typeof fetch>[0],
154
+ init?: Parameters<typeof fetch>[1],
155
+ ) {
156
+ const startTime = Date.now();
157
+
158
+ let rewrittenInput: typeof input = input;
159
+ if (urlRewriter) {
160
+ if (typeof input === 'string') {
161
+ rewrittenInput = rewriteUrl(input);
162
+ } else if (input instanceof Request) {
163
+ rewrittenInput = new Request(rewriteUrl(input.url), input as RequestInit);
164
+ }
165
+ }
166
+
167
+ const request = getRequestSnapshot(rewrittenInput, init);
168
+
169
+ try {
170
+ const response = await originalFetch!.call(globalThis, rewrittenInput, init);
171
+ const duration = Date.now() - startTime;
172
+
173
+ try {
174
+ const data = await parseResponseBody(response);
175
+ emitNetworkLog({
176
+ timestamp: startTime,
177
+ duration,
178
+ request,
179
+ response: {
180
+ status: response.status,
181
+ statusText: response.statusText,
182
+ data,
183
+ },
184
+ });
185
+ } catch {
186
+ emitNetworkLog({
187
+ timestamp: startTime,
188
+ duration,
189
+ request,
190
+ response: {
191
+ status: response.status,
192
+ statusText: response.statusText,
193
+ },
194
+ });
195
+ }
196
+
197
+ return response;
198
+ } catch (error) {
199
+ emitNetworkLog({
200
+ timestamp: startTime,
201
+ duration: Date.now() - startTime,
202
+ request,
203
+ error: error instanceof Error ? error.message : String(error),
204
+ });
205
+ throw error;
206
+ }
207
+ };
208
+
209
+ return () => {
210
+ stopFetchInterceptor();
211
+ };
212
+ }
213
+
214
+ function teardownAxiosInterceptors(axiosInstance: AxiosInstanceLike): void {
215
+ const registration = axiosRegistrations.get(axiosInstance);
216
+
217
+ if (!registration) {
218
+ return;
219
+ }
220
+
221
+ registration.refCount -= 1;
222
+
223
+ if (registration.refCount > 0) {
224
+ return;
225
+ }
226
+
227
+ if (typeof registration.requestId === 'number') {
228
+ axiosInstance.interceptors.request.eject?.(registration.requestId);
229
+ }
230
+
231
+ if (typeof registration.responseId === 'number') {
232
+ axiosInstance.interceptors.response.eject?.(registration.responseId);
233
+ }
234
+
235
+ axiosRegistrations.delete(axiosInstance);
236
+ }
237
+
238
+ function setupAxiosInterceptorsInternal(
239
+ axiosInstance: AxiosInstanceLike,
240
+ ): () => void {
241
+ const existingRegistration = axiosRegistrations.get(axiosInstance);
242
+
243
+ if (existingRegistration) {
244
+ existingRegistration.refCount += 1;
245
+ return () => {
246
+ teardownAxiosInterceptors(axiosInstance);
247
+ };
248
+ }
249
+
250
+ const requestId = axiosInstance.interceptors.request.use(
251
+ (config: AxiosRequestConfigLike) => {
252
+ // Rewrite URL if rewriter is set
253
+ if (urlRewriter && config.url) {
254
+ const fullUrl = config.baseURL
255
+ ? `${config.baseURL}${config.url}`
256
+ : config.url;
257
+ const rewritten = rewriteUrl(fullUrl);
258
+ if (rewritten !== fullUrl) {
259
+ if (config.baseURL && rewritten.startsWith(config.baseURL)) {
260
+ config.url = rewritten.slice(config.baseURL.length);
261
+ } else {
262
+ config.baseURL = '';
263
+ config.url = rewritten;
264
+ }
265
+ }
266
+ }
267
+
268
+ if (!config.headers) {
269
+ config.headers = {};
270
+ }
271
+
272
+ if (!config.headers['X-Request-Id']) {
273
+ config.headers['X-Request-Id'] =
274
+ Date.now().toString() + Math.random().toString(36).substring(2, 10);
275
+ }
276
+
277
+ const trackId = config.headers['X-Request-Id'] as string;
278
+
279
+ pendingAxiosRequests.set(trackId, {
280
+ startTime: Date.now(),
281
+ timestamp: Date.now(),
282
+ });
283
+
284
+ return config;
285
+ },
286
+ (error: unknown) => Promise.reject(error),
287
+ );
288
+
289
+ const responseId = axiosInstance.interceptors.response.use(
290
+ (response: AxiosResponseLike) => {
291
+ const trackId = response.config?.headers?.['X-Request-Id'] as string;
292
+ const pending = pendingAxiosRequests.get(trackId);
293
+
294
+ if (pending) {
295
+ emitNetworkLog({
296
+ timestamp: pending.timestamp,
297
+ duration: Date.now() - pending.startTime,
298
+ request: {
299
+ url: `${response.config.baseURL || ''}${response.config.url}`,
300
+ method: (response.config.method || 'GET').toUpperCase(),
301
+ headers: response.config.headers,
302
+ body: response.config.data || response.config.params,
303
+ },
304
+ response: {
305
+ status: response.status,
306
+ statusText: response.statusText,
307
+ headers: response.headers,
308
+ data: response.data,
309
+ success:
310
+ response.data && typeof response.data === 'object'
311
+ ? (response.data as Record<string, unknown>).success !== false
312
+ : response.status >= 200 && response.status < 300,
313
+ },
314
+ });
315
+
316
+ pendingAxiosRequests.delete(trackId);
317
+ }
318
+
319
+ return response;
320
+ },
321
+ (error: AxiosErrorLike) => {
322
+ if (error.config) {
323
+ const trackId = error.config.headers?.['X-Request-Id'] as string;
324
+ const pending = pendingAxiosRequests.get(trackId);
325
+
326
+ emitNetworkLog({
327
+ timestamp: pending ? pending.timestamp : Date.now(),
328
+ duration: pending ? Date.now() - pending.startTime : undefined,
329
+ request: {
330
+ url: `${error.config.baseURL || ''}${error.config.url}`,
331
+ method: (error.config.method || 'GET').toUpperCase(),
332
+ headers: error.config.headers,
333
+ body: error.config.data || error.config.params,
334
+ },
335
+ error: error.message,
336
+ });
337
+
338
+ pendingAxiosRequests.delete(trackId);
339
+ }
340
+
341
+ return Promise.reject(error);
342
+ },
343
+ );
344
+
345
+ axiosRegistrations.set(axiosInstance, {
346
+ refCount: 1,
347
+ requestId: typeof requestId === 'number' ? requestId : undefined,
348
+ responseId: typeof responseId === 'number' ? responseId : undefined,
349
+ });
350
+
351
+ return () => {
352
+ teardownAxiosInterceptors(axiosInstance);
353
+ };
354
+ }
355
+
356
+ function isUrlBlacklisted(
357
+ url: string,
358
+ blacklist: Array<string | RegExp>,
359
+ ): boolean {
360
+ if (!url) {
361
+ return false;
362
+ }
363
+
364
+ return blacklist.some((pattern) =>
365
+ pattern instanceof RegExp ? pattern.test(url) : url.includes(pattern),
366
+ );
367
+ }
368
+
369
+ export interface NetworkFeatureAPI extends DebugFeature<NetworkLogEntry> {
370
+ setupAxiosInterceptors: (axiosInstance: AxiosInstanceLike) => () => void;
371
+ addUrlToBlacklist: (pattern: string | RegExp) => void;
372
+ removeUrlFromBlacklist: (pattern: string | RegExp) => void;
373
+ clearBlacklist: () => void;
374
+ getBlacklist: () => Array<string | RegExp>;
375
+ }
376
+
377
+ export interface NetworkFeatureConfig {
378
+ /** Maximum number of network logs to keep (default: 200) */
379
+ maxLogs?: number;
380
+ /** URLs to filter out from logging */
381
+ blacklist?: Array<string | RegExp>;
382
+ }
383
+
384
+ export const createNetworkFeature = (config?: NetworkFeatureConfig): NetworkFeatureAPI => {
385
+ const maxLogs = config?.maxLogs ?? DEFAULT_MAX_LOGS;
386
+ const blacklist: Array<string | RegExp> = config?.blacklist ? [...config.blacklist] : [];
387
+ const logStore = createObservableStore<NetworkLogEntry>();
388
+ const axiosCleanupMap = new Map<AxiosInstanceLike, () => void>();
389
+
390
+ let nextId = 0;
391
+ let initialized = false;
392
+ let unsubscribeLogs: (() => void) | null = null;
393
+ let stopFetch: (() => void) | null = null;
394
+
395
+ const handleLog = (entry: NetworkLogPayload) => {
396
+ if (isUrlBlacklisted(entry.request.url, blacklist)) {
397
+ return;
398
+ }
399
+
400
+ logStore.push(
401
+ {
402
+ ...entry,
403
+ id: String(nextId++),
404
+ },
405
+ maxLogs,
406
+ );
407
+ };
408
+
409
+ const cleanupAxiosRegistrations = () => {
410
+ axiosCleanupMap.forEach((cleanup) => cleanup());
411
+ axiosCleanupMap.clear();
412
+ };
413
+
414
+ return {
415
+ name: 'network',
416
+ label: 'Network',
417
+ renderContent: NetworkLogTab,
418
+ setup: () => {
419
+ if (initialized) {
420
+ return;
421
+ }
422
+
423
+ unsubscribeLogs = networkChannel.subscribe(handleLog);
424
+ stopFetch = startFetchInterceptor();
425
+ initialized = true;
426
+ },
427
+ getData: () => logStore.getData(),
428
+ clear: () => {
429
+ logStore.clear();
430
+ },
431
+ cleanup: () => {
432
+ if (!initialized) {
433
+ return;
434
+ }
435
+
436
+ setUrlRewriter(null);
437
+ unsubscribeLogs?.();
438
+ unsubscribeLogs = null;
439
+ stopFetch?.();
440
+ stopFetch = null;
441
+ cleanupAxiosRegistrations();
442
+ logStore.clear();
443
+ initialized = false;
444
+ },
445
+ subscribe: (listener) => logStore.subscribe(listener),
446
+ setupAxiosInterceptors: (axiosInstance) => {
447
+ const existingCleanup = axiosCleanupMap.get(axiosInstance);
448
+
449
+ if (existingCleanup) {
450
+ return existingCleanup;
451
+ }
452
+
453
+ const cleanup = setupAxiosInterceptorsInternal(axiosInstance);
454
+ const trackedCleanup = () => {
455
+ cleanup();
456
+ axiosCleanupMap.delete(axiosInstance);
457
+ };
458
+
459
+ axiosCleanupMap.set(axiosInstance, trackedCleanup);
460
+ return trackedCleanup;
461
+ },
462
+ addUrlToBlacklist: (pattern) => {
463
+ const alreadyExists = blacklist.some((item) =>
464
+ item instanceof RegExp && pattern instanceof RegExp
465
+ ? item.toString() === pattern.toString()
466
+ : item === pattern,
467
+ );
468
+
469
+ if (!alreadyExists) {
470
+ blacklist.push(pattern);
471
+ }
472
+ },
473
+ removeUrlFromBlacklist: (pattern) => {
474
+ const nextBlacklist = blacklist.filter((item) =>
475
+ item instanceof RegExp && pattern instanceof RegExp
476
+ ? item.toString() !== pattern.toString()
477
+ : item !== pattern,
478
+ );
479
+
480
+ blacklist.length = 0;
481
+ blacklist.push(...nextBlacklist);
482
+ },
483
+ clearBlacklist: () => {
484
+ blacklist.length = 0;
485
+ },
486
+ getBlacklist: () => [...blacklist],
487
+ };
488
+ };
@@ -0,0 +1,42 @@
1
+ import { Platform } from 'react-native';
2
+ import { ThirdPartyLibsTab } from '../components/ThirdPartyLibsTab';
3
+ import { NativeDebugLibs } from '../native/NativeDebugLibs';
4
+ import type { DebugFeature, ThirdPartyLib } from '../types';
5
+
6
+ const availableLibs: ThirdPartyLib[] = [
7
+ {
8
+ id: 'flex',
9
+ name: 'FLEX',
10
+ description: 'In-app debugging and exploration tool for iOS',
11
+ platform: 'ios',
12
+ actions: [
13
+ { id: 'showExplorer', label: 'Show Explorer', onPress: NativeDebugLibs.showExplorer },
14
+ { id: 'hideExplorer', label: 'Hide Explorer', onPress: NativeDebugLibs.hideExplorer },
15
+ ],
16
+ },
17
+ {
18
+ id: 'doraemonkit',
19
+ name: 'DoraemonKit',
20
+ description: 'A full-featured iOS & Android development assistant',
21
+ platform: 'both',
22
+ actions: [
23
+ { id: 'showDoraemonKit', label: 'Show DoraemonKit', onPress: NativeDebugLibs.showDoraemonKit },
24
+ { id: 'hideDoraemonKit', label: 'Hide DoraemonKit', onPress: NativeDebugLibs.hideDoraemonKit },
25
+ ],
26
+ },
27
+ ];
28
+
29
+ function getPlatformLibs(): ThirdPartyLib[] {
30
+ return availableLibs.filter(
31
+ (lib) => lib.platform === 'both' || lib.platform === Platform.OS,
32
+ );
33
+ }
34
+
35
+ export const createThirdPartyLibsFeature = (): DebugFeature<ThirdPartyLib> => ({
36
+ name: 'thirdPartyLibs',
37
+ label: 'Debug Libraries',
38
+ renderContent: ThirdPartyLibsTab,
39
+ setup: () => {},
40
+ getData: () => getPlatformLibs(),
41
+ cleanup: () => {},
42
+ });
@@ -0,0 +1,69 @@
1
+ import { TrackLogTab } from '../components/TrackLogTab';
2
+ import type { DebugFeature, TrackLogEntry } from '../types';
3
+ import { createEventChannel } from '../utils/createEventChannel';
4
+ import { createObservableStore } from '../utils/createObservableStore';
5
+
6
+ export interface TrackEventData {
7
+ eventName: string;
8
+ [key: string]: unknown;
9
+ }
10
+
11
+ type TrackLogPayload = TrackEventData & {
12
+ timestamp: number;
13
+ };
14
+
15
+ const DEFAULT_MAX_LOGS = 200;
16
+ const trackChannel = createEventChannel<TrackLogPayload>();
17
+
18
+ export const addTrackLog = (eventData: TrackEventData): void => {
19
+ trackChannel.emit({
20
+ timestamp: Date.now(),
21
+ ...eventData,
22
+ });
23
+ };
24
+
25
+ export interface TrackFeatureConfig {
26
+ /** Maximum number of track events to keep (default: 200) */
27
+ maxLogs?: number;
28
+ }
29
+
30
+ export const createTrackFeature = (config?: TrackFeatureConfig): DebugFeature<TrackLogEntry> => {
31
+ const maxLogs = config?.maxLogs ?? DEFAULT_MAX_LOGS;
32
+ const logStore = createObservableStore<TrackLogEntry>();
33
+ let nextId = 0;
34
+ let unsubscribeLogs: (() => void) | null = null;
35
+ let initialized = false;
36
+
37
+ return {
38
+ name: 'track',
39
+ label: 'Track',
40
+ renderContent: TrackLogTab,
41
+ setup: () => {
42
+ if (initialized) {
43
+ return;
44
+ }
45
+
46
+ unsubscribeLogs = trackChannel.subscribe((entry) => {
47
+ logStore.push(
48
+ {
49
+ ...entry,
50
+ id: String(nextId++),
51
+ },
52
+ maxLogs,
53
+ );
54
+ });
55
+ initialized = true;
56
+ },
57
+ getData: () => logStore.getData(),
58
+ clear: () => {
59
+ logStore.clear();
60
+ },
61
+ cleanup: () => {
62
+ unsubscribeLogs?.();
63
+ unsubscribeLogs = null;
64
+ logStore.clear();
65
+ initialized = false;
66
+ },
67
+ subscribe: (listener) => logStore.subscribe(listener),
68
+ };
69
+ };