react-native-debug-toolkit 3.1.4 → 3.2.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 (125) hide show
  1. package/README.md +88 -65
  2. package/README.zh-CN.md +87 -64
  3. package/bin/debug-toolkit.js +10 -2
  4. package/lib/commonjs/core/initialize.js +5 -3
  5. package/lib/commonjs/core/initialize.js.map +1 -1
  6. package/lib/commonjs/features/devConnect/DevConnectQrScanner.js +146 -0
  7. package/lib/commonjs/features/devConnect/DevConnectQrScanner.js.map +1 -0
  8. package/lib/commonjs/features/devConnect/DevConnectTab.js +426 -0
  9. package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -0
  10. package/lib/commonjs/features/devConnect/cameraKit.js +54 -0
  11. package/lib/commonjs/features/devConnect/cameraKit.js.map +1 -0
  12. package/lib/commonjs/features/devConnect/devConnectPreferences.js +35 -0
  13. package/lib/commonjs/features/devConnect/devConnectPreferences.js.map +1 -0
  14. package/lib/commonjs/features/devConnect/devConnectUtils.js +53 -0
  15. package/lib/commonjs/features/devConnect/devConnectUtils.js.map +1 -0
  16. package/lib/commonjs/features/devConnect/index.js +92 -0
  17. package/lib/commonjs/features/devConnect/index.js.map +1 -0
  18. package/lib/commonjs/features/devConnect/platformDetect.js +30 -0
  19. package/lib/commonjs/features/devConnect/platformDetect.js.map +1 -0
  20. package/lib/commonjs/features/devConnect/types.js +2 -0
  21. package/lib/commonjs/features/devConnect/types.js.map +1 -0
  22. package/lib/commonjs/features/network/NetworkLogTab.js +7 -3
  23. package/lib/commonjs/features/network/NetworkLogTab.js.map +1 -1
  24. package/lib/commonjs/index.js +7 -0
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/commonjs/ui/DebugView.js +1 -0
  27. package/lib/commonjs/ui/DebugView.js.map +1 -1
  28. package/lib/commonjs/ui/panel/DebugPanel.js +0 -25
  29. package/lib/commonjs/ui/panel/DebugPanel.js.map +1 -1
  30. package/lib/commonjs/utils/DaemonClient.js +25 -1
  31. package/lib/commonjs/utils/DaemonClient.js.map +1 -1
  32. package/lib/commonjs/utils/debugPreferences.js +2 -1
  33. package/lib/commonjs/utils/debugPreferences.js.map +1 -1
  34. package/lib/commonjs/utils/deviceReport.js +1 -0
  35. package/lib/commonjs/utils/deviceReport.js.map +1 -1
  36. package/lib/module/core/initialize.js +5 -3
  37. package/lib/module/core/initialize.js.map +1 -1
  38. package/lib/module/features/devConnect/DevConnectQrScanner.js +141 -0
  39. package/lib/module/features/devConnect/DevConnectQrScanner.js.map +1 -0
  40. package/lib/module/features/devConnect/DevConnectTab.js +421 -0
  41. package/lib/module/features/devConnect/DevConnectTab.js.map +1 -0
  42. package/lib/module/features/devConnect/cameraKit.js +49 -0
  43. package/lib/module/features/devConnect/cameraKit.js.map +1 -0
  44. package/lib/module/features/devConnect/devConnectPreferences.js +29 -0
  45. package/lib/module/features/devConnect/devConnectPreferences.js.map +1 -0
  46. package/lib/module/features/devConnect/devConnectUtils.js +47 -0
  47. package/lib/module/features/devConnect/devConnectUtils.js.map +1 -0
  48. package/lib/module/features/devConnect/index.js +52 -0
  49. package/lib/module/features/devConnect/index.js.map +1 -0
  50. package/lib/module/features/devConnect/platformDetect.js +26 -0
  51. package/lib/module/features/devConnect/platformDetect.js.map +1 -0
  52. package/lib/module/features/devConnect/types.js +2 -0
  53. package/lib/module/features/devConnect/types.js.map +1 -0
  54. package/lib/module/features/network/NetworkLogTab.js +7 -3
  55. package/lib/module/features/network/NetworkLogTab.js.map +1 -1
  56. package/lib/module/index.js +1 -1
  57. package/lib/module/index.js.map +1 -1
  58. package/lib/module/ui/DebugView.js +1 -0
  59. package/lib/module/ui/DebugView.js.map +1 -1
  60. package/lib/module/ui/panel/DebugPanel.js +1 -26
  61. package/lib/module/ui/panel/DebugPanel.js.map +1 -1
  62. package/lib/module/utils/DaemonClient.js +25 -1
  63. package/lib/module/utils/DaemonClient.js.map +1 -1
  64. package/lib/module/utils/debugPreferences.js +2 -1
  65. package/lib/module/utils/debugPreferences.js.map +1 -1
  66. package/lib/module/utils/deviceReport.js +1 -0
  67. package/lib/module/utils/deviceReport.js.map +1 -1
  68. package/lib/typescript/src/core/initialize.d.ts +1 -0
  69. package/lib/typescript/src/core/initialize.d.ts.map +1 -1
  70. package/lib/typescript/src/features/devConnect/DevConnectQrScanner.d.ts +9 -0
  71. package/lib/typescript/src/features/devConnect/DevConnectQrScanner.d.ts.map +1 -0
  72. package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts +5 -0
  73. package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -0
  74. package/lib/typescript/src/features/devConnect/cameraKit.d.ts +47 -0
  75. package/lib/typescript/src/features/devConnect/cameraKit.d.ts.map +1 -0
  76. package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts +7 -0
  77. package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts.map +1 -0
  78. package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts +12 -0
  79. package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts.map +1 -0
  80. package/lib/typescript/src/features/devConnect/index.d.ts +7 -0
  81. package/lib/typescript/src/features/devConnect/index.d.ts.map +1 -0
  82. package/lib/typescript/src/features/devConnect/platformDetect.d.ts +2 -0
  83. package/lib/typescript/src/features/devConnect/platformDetect.d.ts.map +1 -0
  84. package/lib/typescript/src/features/devConnect/types.d.ts +7 -0
  85. package/lib/typescript/src/features/devConnect/types.d.ts.map +1 -0
  86. package/lib/typescript/src/features/network/NetworkLogTab.d.ts.map +1 -1
  87. package/lib/typescript/src/index.d.ts +2 -0
  88. package/lib/typescript/src/index.d.ts.map +1 -1
  89. package/lib/typescript/src/types/feature.d.ts +1 -1
  90. package/lib/typescript/src/types/feature.d.ts.map +1 -1
  91. package/lib/typescript/src/ui/DebugView.d.ts.map +1 -1
  92. package/lib/typescript/src/ui/panel/DebugPanel.d.ts.map +1 -1
  93. package/lib/typescript/src/utils/DaemonClient.d.ts +1 -0
  94. package/lib/typescript/src/utils/DaemonClient.d.ts.map +1 -1
  95. package/lib/typescript/src/utils/debugPreferences.d.ts +1 -0
  96. package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
  97. package/lib/typescript/src/utils/deviceReport.d.ts +6 -0
  98. package/lib/typescript/src/utils/deviceReport.d.ts.map +1 -1
  99. package/node/daemon/src/console/console.html +166 -27
  100. package/node/daemon/src/store.js +45 -6
  101. package/package.json +15 -3
  102. package/src/core/initialize.ts +7 -1
  103. package/src/features/devConnect/DevConnectQrScanner.tsx +122 -0
  104. package/src/features/devConnect/DevConnectTab.tsx +357 -0
  105. package/src/features/devConnect/cameraKit.ts +93 -0
  106. package/src/features/devConnect/devConnectPreferences.ts +33 -0
  107. package/src/features/devConnect/devConnectUtils.ts +59 -0
  108. package/src/features/devConnect/index.ts +64 -0
  109. package/src/features/devConnect/platformDetect.ts +26 -0
  110. package/src/features/devConnect/types.ts +6 -0
  111. package/src/features/network/NetworkLogTab.tsx +6 -3
  112. package/src/index.ts +2 -0
  113. package/src/types/feature.ts +2 -1
  114. package/src/ui/DebugView.tsx +1 -0
  115. package/src/ui/panel/DebugPanel.tsx +1 -23
  116. package/src/utils/DaemonClient.ts +26 -1
  117. package/src/utils/debugPreferences.ts +1 -0
  118. package/src/utils/deviceReport.ts +8 -1
  119. package/lib/commonjs/ui/panel/StreamingSettingsModal.js +0 -495
  120. package/lib/commonjs/ui/panel/StreamingSettingsModal.js.map +0 -1
  121. package/lib/module/ui/panel/StreamingSettingsModal.js +0 -490
  122. package/lib/module/ui/panel/StreamingSettingsModal.js.map +0 -1
  123. package/lib/typescript/src/ui/panel/StreamingSettingsModal.d.ts +0 -8
  124. package/lib/typescript/src/ui/panel/StreamingSettingsModal.d.ts.map +0 -1
  125. package/src/ui/panel/StreamingSettingsModal.tsx +0 -528
@@ -0,0 +1,93 @@
1
+ import type { ComponentType } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+
4
+ // ---- react-native-camera-kit types ----
5
+
6
+ export interface CameraKitReadCodeEvent {
7
+ nativeEvent?: {
8
+ codeStringValue?: string;
9
+ };
10
+ }
11
+
12
+ export interface CameraKitCameraProps {
13
+ style?: StyleProp<ViewStyle>;
14
+ cameraType?: unknown;
15
+ scanBarcode?: boolean;
16
+ onReadCode?: (event: CameraKitReadCodeEvent) => void;
17
+ showFrame?: boolean;
18
+ laserColor?: string;
19
+ frameColor?: string;
20
+ allowedBarcodeTypes?: string[];
21
+ }
22
+
23
+ export interface CameraKitModule {
24
+ Camera: ComponentType<CameraKitCameraProps>;
25
+ CameraType?: { Back?: unknown };
26
+ }
27
+
28
+ // ---- expo-camera types ----
29
+
30
+ export interface ExpoCameraScanResult {
31
+ boundingBox?: unknown;
32
+ cornerPoints?: unknown;
33
+ type?: string;
34
+ value?: string;
35
+ }
36
+
37
+ export interface ExpoCameraModule {
38
+ Camera: ComponentType<{
39
+ style?: StyleProp<ViewStyle>;
40
+ onBarCodeScanned?: (result: ExpoCameraScanResult) => void;
41
+ barCodeScannerSettings?: { barCodeTypes: string[] };
42
+ }>;
43
+ }
44
+
45
+ // ---- Unified scanner ----
46
+
47
+ export type ScannerKind = 'camera-kit' | 'expo-camera';
48
+
49
+ export interface ScannerModule {
50
+ kind: ScannerKind;
51
+ CameraKit?: CameraKitModule;
52
+ ExpoCamera?: ExpoCameraModule;
53
+ }
54
+
55
+ let cached: ScannerModule | null | false = false;
56
+
57
+ function tryCameraKit(): ScannerModule | null {
58
+ try {
59
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
60
+ const mod = require('react-native-camera-kit') as Partial<CameraKitModule>;
61
+ if (mod.Camera) {
62
+ return {
63
+ kind: 'camera-kit',
64
+ CameraKit: { Camera: mod.Camera, CameraType: mod.CameraType },
65
+ };
66
+ }
67
+ } catch { /* not installed */ }
68
+ return null;
69
+ }
70
+
71
+ function tryExpoCamera(): ScannerModule | null {
72
+ try {
73
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
74
+ const mod = require('expo-camera') as Partial<ExpoCameraModule>;
75
+ if (mod.Camera) {
76
+ return {
77
+ kind: 'expo-camera',
78
+ ExpoCamera: { Camera: mod.Camera },
79
+ };
80
+ }
81
+ } catch { /* not installed */ }
82
+ return null;
83
+ }
84
+
85
+ export function getScannerModule(): ScannerModule | null {
86
+ if (cached !== false) return cached;
87
+ cached = tryCameraKit() ?? tryExpoCamera();
88
+ return cached;
89
+ }
90
+
91
+ export function isCameraKitAvailable(): boolean {
92
+ return getScannerModule() !== null;
93
+ }
@@ -0,0 +1,33 @@
1
+ import { daemonClient } from '../../utils/DaemonClient';
2
+ import { getPreference, KEYS, setPreference } from '../../utils/debugPreferences';
3
+ import { normalizeComputerHost } from './devConnectUtils';
4
+ import { isSimulator } from './platformDetect';
5
+
6
+ export interface DevConnectPreferences {
7
+ computerHost: string;
8
+ }
9
+
10
+ export async function loadDevConnectPreferences(): Promise<DevConnectPreferences> {
11
+ const storedHost = await getPreference(KEYS.computerHost);
12
+ return {
13
+ computerHost: storedHost ? normalizeComputerHost(storedHost) ?? '' : '',
14
+ };
15
+ }
16
+
17
+ export async function saveComputerHost(value: string): Promise<string | null> {
18
+ const normalized = normalizeComputerHost(value);
19
+ if (!normalized) return null;
20
+ await setPreference(KEYS.computerHost, normalized);
21
+ return normalized;
22
+ }
23
+
24
+ export async function restoreDevConnectSettingsToDaemon(): Promise<void> {
25
+ const preferences = await loadDevConnectPreferences();
26
+ const mode = isSimulator() ? 'simulator' as const : 'device' as const;
27
+ daemonClient.configure({
28
+ mode,
29
+ endpoint: '',
30
+ deviceHost: mode === 'simulator' ? '' : preferences.computerHost,
31
+ token: '',
32
+ });
33
+ }
@@ -0,0 +1,59 @@
1
+ const METRO_PORT = '8081';
2
+
3
+ export interface MetroUrls {
4
+ expUrl: string;
5
+ httpUrl: string;
6
+ }
7
+
8
+ export interface ParsedMetroQrPayload {
9
+ computerHost: string;
10
+ source: string;
11
+ }
12
+
13
+ function isValidIpv4(host: string): boolean {
14
+ const parts = host.split('.');
15
+ if (parts.length !== 4) return false;
16
+
17
+ return parts.every((part) => {
18
+ if (!/^\d{1,3}$/.test(part)) return false;
19
+ const value = Number(part);
20
+ return value >= 0 && value <= 255 && String(value) === part;
21
+ });
22
+ }
23
+
24
+ function toUrlInput(raw: string): string {
25
+ const trimmed = raw.trim();
26
+ if (/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//.test(trimmed)) {
27
+ return trimmed;
28
+ }
29
+ return `http://${trimmed}`;
30
+ }
31
+
32
+ export function normalizeComputerHost(raw: string): string | null {
33
+ const trimmed = raw.trim();
34
+ if (!trimmed) return null;
35
+
36
+ try {
37
+ const parsed = new URL(toUrlInput(trimmed));
38
+ const host = parsed.hostname.trim();
39
+ return isValidIpv4(host) ? host : null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ export function buildMetroUrls(rawHost: string): MetroUrls | null {
46
+ const host = normalizeComputerHost(rawHost);
47
+ if (!host) return null;
48
+
49
+ return {
50
+ expUrl: `exp://${host}:${METRO_PORT}`,
51
+ httpUrl: `http://${host}:${METRO_PORT}`,
52
+ };
53
+ }
54
+
55
+ export function parseMetroQrPayload(payload: string): ParsedMetroQrPayload | null {
56
+ const computerHost = normalizeComputerHost(payload);
57
+ if (!computerHost) return null;
58
+ return { computerHost, source: payload };
59
+ }
@@ -0,0 +1,64 @@
1
+ import { DevConnectTab } from './DevConnectTab';
2
+ import { isCameraKitAvailable } from './cameraKit';
3
+ import { loadDevConnectPreferences } from './devConnectPreferences';
4
+ import { isSimulator } from './platformDetect';
5
+ import { daemonClient } from '../../utils/DaemonClient';
6
+ import type { DebugFeature, DebugFeatureListener } from '../../types';
7
+ import type { DevConnectState } from './types';
8
+
9
+ export type { DevConnectState } from './types';
10
+ export {
11
+ buildMetroUrls,
12
+ normalizeComputerHost,
13
+ parseMetroQrPayload,
14
+ } from './devConnectUtils';
15
+ export {
16
+ loadDevConnectPreferences,
17
+ restoreDevConnectSettingsToDaemon,
18
+ saveComputerHost,
19
+ } from './devConnectPreferences';
20
+
21
+ export const createDevConnectFeature = (): DebugFeature<DevConnectState> => {
22
+ const listeners = new Set<DebugFeatureListener>();
23
+ let state: DevConnectState = {
24
+ isSimulator: isSimulator(),
25
+ computerHost: '',
26
+ qrAvailable: isCameraKitAvailable(),
27
+ streaming: daemonClient.isConnected(),
28
+ };
29
+
30
+ const notify = () => {
31
+ state = {
32
+ ...state,
33
+ streaming: daemonClient.isConnected(),
34
+ };
35
+ listeners.forEach((listener) => listener());
36
+ };
37
+
38
+ return {
39
+ name: 'devConnect',
40
+ label: 'DevConnect',
41
+ renderContent: DevConnectTab,
42
+ setup() {
43
+ loadDevConnectPreferences().then((preferences) => {
44
+ state = {
45
+ ...state,
46
+ computerHost: preferences.computerHost,
47
+ };
48
+ notify();
49
+ }).catch(() => {
50
+ notify();
51
+ });
52
+ },
53
+ getSnapshot: () => state,
54
+ cleanup() {
55
+ listeners.clear();
56
+ },
57
+ subscribe(listener) {
58
+ listeners.add(listener);
59
+ return () => {
60
+ listeners.delete(listener);
61
+ };
62
+ },
63
+ };
64
+ };
@@ -0,0 +1,26 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ export function isSimulator(): boolean {
4
+ const { OS } = Platform;
5
+
6
+ if (OS === 'android') {
7
+ const constants = Platform.constants as Record<string, unknown>;
8
+ if (constants.isEmulator === true) return true;
9
+ const model = String(constants.Model ?? '').toLowerCase();
10
+ return model.includes('sdk') || model.includes('emulator') || model.includes('google_sdk');
11
+ }
12
+
13
+ if (OS === 'ios') {
14
+ // NativeModules.KCKFCSupportManager is absent on simulator,
15
+ // but the most reliable check is the DeviceInfo model name.
16
+ const deviceInfo = NativeModules.DeviceInfo
17
+ ?? NativeModules.PlatformConstants;
18
+ if (deviceInfo) {
19
+ const model = String(deviceInfo.model ?? '').toLowerCase();
20
+ if (model.includes('simulator')) return true;
21
+ }
22
+ return false;
23
+ }
24
+
25
+ return false;
26
+ }
@@ -0,0 +1,6 @@
1
+ export interface DevConnectState {
2
+ isSimulator: boolean;
3
+ computerHost: string;
4
+ qrAvailable: boolean;
5
+ streaming: boolean;
6
+ }
@@ -26,13 +26,16 @@ const formatSize = (data: unknown): string => {
26
26
  }
27
27
  };
28
28
 
29
+ // Keep in sync with console.html buildCurlCommand()
29
30
  const buildCurl = (log: NetworkLogEntry): string => {
30
- let c = `curl -X ${log.request.method} '${log.request.url}'`;
31
+ const q = (s: string) => s.replace(/'/g, "'\\''");
32
+ let c = `curl -X ${log.request.method} '${q(log.request.url)}'`;
31
33
  if (log.request.headers) {
32
- Object.entries(log.request.headers).forEach(([k, v]) => (c += ` \\\n -H '${k}: ${v}'`));
34
+ Object.entries(log.request.headers).forEach(([k, v]) => (c += ` \\\n -H '${q(k)}: ${q(String(v))}'`));
33
35
  }
34
36
  if (log.request.body) {
35
- c += ` \\\n -d '${typeof log.request.body === 'string' ? log.request.body : JSON.stringify(log.request.body)}'`;
37
+ const bodyStr = typeof log.request.body === 'string' ? log.request.body : JSON.stringify(log.request.body);
38
+ c += ` \\\n -d '${q(bodyStr)}'`;
36
39
  }
37
40
  return c;
38
41
  };
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ export type { TrackFeatureConfig, TrackEventData } from './features/track';
20
20
  export { createEnvironmentFeature } from './features/environment';
21
21
  export type { EnvironmentFeatureAPI } from './features/environment';
22
22
  export { createClipboardFeature } from './features/clipboard';
23
+ export { createDevConnectFeature } from './features/devConnect';
24
+ export type { DevConnectState } from './features/devConnect';
23
25
 
24
26
  // Hooks
25
27
  export { useNavigationLogger } from './features/navigation/useNavigationLogger';
@@ -9,7 +9,8 @@ export type BuiltInFeatureName =
9
9
  | 'navigation'
10
10
  | 'track'
11
11
  | 'environment'
12
- | 'clipboard';
12
+ | 'clipboard'
13
+ | 'devConnect';
13
14
 
14
15
  export interface DebugFeatureRenderProps<TSnapshot = unknown> {
15
16
  snapshot: TSnapshot;
@@ -51,6 +51,7 @@ export function DebugView({
51
51
  navigation: true,
52
52
  track: true,
53
53
  clipboard: true,
54
+ devConnect: true,
54
55
  ...features,
55
56
  };
56
57
 
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useRef } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
@@ -10,7 +10,6 @@ import {
10
10
  useWindowDimensions,
11
11
  } from 'react-native';
12
12
  import { Colors } from '../theme/colors';
13
- import { StreamingSettingsModal } from './StreamingSettingsModal';
14
13
 
15
14
  interface DebugPanelProps {
16
15
  onClose: () => void;
@@ -22,7 +21,6 @@ export function DebugPanel({ onClose, onClearAll, children }: DebugPanelProps) {
22
21
  const { height: screenHeight } = useWindowDimensions();
23
22
  const panelTranslateY = useRef(new Animated.Value(screenHeight)).current;
24
23
  const backdropOpacity = useRef(new Animated.Value(0)).current;
25
- const [settingsVisible, setSettingsVisible] = useState(false);
26
24
 
27
25
  useEffect(() => {
28
26
  requestAnimationFrame(() => {
@@ -103,13 +101,6 @@ export function DebugPanel({ onClose, onClearAll, children }: DebugPanelProps) {
103
101
  <View style={styles.header}>
104
102
  <Text style={styles.headerTitle}>Debug Toolkit</Text>
105
103
  <View style={styles.headerButtons}>
106
- <TouchableOpacity
107
- onPress={() => setSettingsVisible(true)}
108
- style={styles.settingsButton}
109
- activeOpacity={0.6}
110
- >
111
- <Text style={styles.settingsButtonText}>⚙</Text>
112
- </TouchableOpacity>
113
104
  <TouchableOpacity
114
105
  onPress={() => {
115
106
  onClearAll();
@@ -128,7 +119,6 @@ export function DebugPanel({ onClose, onClearAll, children }: DebugPanelProps) {
128
119
  </View>
129
120
  <View style={styles.panelContent}>{children}</View>
130
121
  </Animated.View>
131
- <StreamingSettingsModal visible={settingsVisible} onClose={() => setSettingsVisible(false)} />
132
122
  </View>
133
123
  );
134
124
  }
@@ -208,18 +198,6 @@ const styles = StyleSheet.create({
208
198
  fontSize: 14,
209
199
  fontWeight: '500',
210
200
  },
211
- settingsButton: {
212
- width: 32,
213
- height: 32,
214
- borderRadius: 16,
215
- backgroundColor: Colors.background,
216
- alignItems: 'center',
217
- justifyContent: 'center',
218
- },
219
- settingsButtonText: {
220
- fontSize: 16,
221
- color: Colors.textSecondary,
222
- },
223
201
  closeButton: {
224
202
  width: 30,
225
203
  height: 30,
@@ -6,6 +6,7 @@ import {
6
6
  createDebugDeviceReport,
7
7
  type DebugDeviceReport,
8
8
  type DebugDeviceReportOptions,
9
+ type SessionInfo,
9
10
  } from './deviceReport';
10
11
  import { safeStringify } from './safeStringify';
11
12
 
@@ -147,6 +148,7 @@ interface StreamState {
147
148
  debounceMs: number;
148
149
  timeoutMs: number;
149
150
  deviceId: string | null;
151
+ session: SessionInfo;
150
152
  sending: boolean;
151
153
  debounceTimer: ReturnType<typeof setTimeout> | null;
152
154
  retryTimer: ReturnType<typeof setTimeout> | null;
@@ -183,6 +185,7 @@ export class DaemonClient {
183
185
  private _featureProvider: FeatureDataProvider;
184
186
  private _onEndpointDetected: ((url: string) => void) | undefined;
185
187
  private _restorePromise: Promise<void> | null = null;
188
+ private _sessionId: SessionInfo | null = null;
186
189
 
187
190
  constructor(options: DaemonClientOptions) {
188
191
  this._fetch = options.fetch;
@@ -264,6 +267,10 @@ export class DaemonClient {
264
267
  connect(options: StreamToDaemonOptions = {}): void {
265
268
  if (this._stream) return;
266
269
 
270
+ if (!this._sessionId) {
271
+ this._sessionId = { id: generateSessionId(), startedAt: Date.now() };
272
+ }
273
+
267
274
  const endpoint = options.endpoint || this.resolveEndpoint();
268
275
  const reportUrl = buildDaemonUrl(endpoint, '/report');
269
276
  const ingestUrl = buildDaemonUrl(endpoint, '/ingest');
@@ -280,6 +287,7 @@ export class DaemonClient {
280
287
  debounceMs: options.debounceMs || DEFAULT_DEBOUNCE_MS,
281
288
  timeoutMs: Math.max(0, options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
282
289
  deviceId: null,
290
+ session: this._sessionId,
283
291
  sending: false,
284
292
  debounceTimer: null,
285
293
  retryTimer: null,
@@ -315,6 +323,7 @@ export class DaemonClient {
315
323
  if (!this._stream) return;
316
324
  const state = this._stream;
317
325
  this._stream = null;
326
+ this._sessionId = null;
318
327
 
319
328
  if (state.debounceTimer) clearTimeout(state.debounceTimer);
320
329
  if (state.retryTimer) clearTimeout(state.retryTimer);
@@ -458,6 +467,7 @@ export class DaemonClient {
458
467
  this._settings = { mode: 'simulator', endpoint: '', deviceHost: '', token: '' };
459
468
  this._streamingEnabled = null;
460
469
  this._restorePromise = null;
470
+ this._sessionId = null;
461
471
  }
462
472
 
463
473
  // ---- Private: Transport ----
@@ -629,7 +639,7 @@ export class DaemonClient {
629
639
  }
630
640
 
631
641
  private async doSendFullReport(state: StreamState): Promise<SendResult> {
632
- const report = createDebugDeviceReport({ featureProvider: this._featureProvider });
642
+ const report = createDebugDeviceReport({ featureProvider: this._featureProvider, session: state.session });
633
643
  const response = await this.doPost(
634
644
  state.reportUrl,
635
645
  this.fetchHeaders(state),
@@ -843,3 +853,18 @@ function readLogCount(value: unknown): Record<string, number> | undefined {
843
853
  export function _resetDaemonClientForTesting(): void {
844
854
  daemonClient._resetForTesting();
845
855
  }
856
+
857
+ function generateSessionId(): string {
858
+ try {
859
+ return (globalThis as { crypto?: { randomUUID?: () => string } }).crypto?.randomUUID?.() ?? fallbackSessionId();
860
+ } catch {
861
+ return fallbackSessionId();
862
+ }
863
+ }
864
+
865
+ function fallbackSessionId(): string {
866
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
867
+ const r = Math.random() * 16 | 0;
868
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
869
+ });
870
+ }
@@ -45,4 +45,5 @@ export const KEYS = {
45
45
  consoleLogs: '@react_native_debug_toolkit/console_logs',
46
46
  networkLogs: '@react_native_debug_toolkit/network_logs',
47
47
  trackLogs: '@react_native_debug_toolkit/track_logs',
48
+ computerHost: '@react_native_debug_toolkit/computer_host',
48
49
  } as const;
@@ -21,9 +21,15 @@ export interface DeviceInfo {
21
21
  appVersion: string;
22
22
  }
23
23
 
24
+ export interface SessionInfo {
25
+ id: string;
26
+ startedAt: number;
27
+ }
28
+
24
29
  export interface DebugDeviceReport {
25
30
  version: 2;
26
31
  device: DeviceInfo;
32
+ session?: SessionInfo;
27
33
  logs: Record<string, unknown[] | undefined>;
28
34
  }
29
35
 
@@ -161,7 +167,7 @@ function sanitizeValue(
161
167
  }
162
168
 
163
169
  export function createDebugDeviceReport(
164
- options: DebugDeviceReportOptions & { featureProvider?: FeatureDataProvider } = {},
170
+ options: DebugDeviceReportOptions & { featureProvider?: FeatureDataProvider; session?: SessionInfo } = {},
165
171
  ): DebugDeviceReport {
166
172
  const provider = options.featureProvider ?? debugToolkit;
167
173
  const maxPerType = Math.max(1, Math.floor(options.maxPerType ?? DEFAULT_MAX_PER_TYPE));
@@ -200,6 +206,7 @@ export function createDebugDeviceReport(
200
206
  osVersion: Platform.Version == null ? 'unknown' : String(Platform.Version),
201
207
  appVersion: (constants?.appVersion as string) || 'unknown',
202
208
  },
209
+ session: options.session,
203
210
  logs,
204
211
  };
205
212
  }