react-native-debug-toolkit 3.2.1 → 3.2.3
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.
- package/README.md +13 -2
- package/README.zh-CN.md +13 -2
- package/android/build.gradle +34 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/reactnativedebugtoolkit/DebugToolkitDevConnectModule.java +154 -0
- package/android/src/main/java/com/reactnativedebugtoolkit/ReactNativeDebugToolkitPackage.java +25 -0
- package/ios/DebugToolkitDevConnect.mm +81 -0
- package/lib/commonjs/features/devConnect/DevConnectQrScanner.js +115 -70
- package/lib/commonjs/features/devConnect/DevConnectQrScanner.js.map +1 -1
- package/lib/commonjs/features/devConnect/DevConnectTab.js +232 -161
- package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/commonjs/features/devConnect/devConnectPreferences.js +35 -5
- package/lib/commonjs/features/devConnect/devConnectPreferences.js.map +1 -1
- package/lib/commonjs/features/devConnect/devConnectUtils.js +99 -15
- package/lib/commonjs/features/devConnect/devConnectUtils.js.map +1 -1
- package/lib/commonjs/features/devConnect/index.js +39 -2
- package/lib/commonjs/features/devConnect/index.js.map +1 -1
- package/lib/commonjs/features/devConnect/nativeDevConnect.js +108 -0
- package/lib/commonjs/features/devConnect/nativeDevConnect.js.map +1 -0
- package/lib/commonjs/features/devConnect/platformDetect.js +7 -11
- package/lib/commonjs/features/devConnect/platformDetect.js.map +1 -1
- package/lib/commonjs/utils/debugPreferences.js +43 -6
- package/lib/commonjs/utils/debugPreferences.js.map +1 -1
- package/lib/module/features/devConnect/DevConnectQrScanner.js +116 -71
- package/lib/module/features/devConnect/DevConnectQrScanner.js.map +1 -1
- package/lib/module/features/devConnect/DevConnectTab.js +235 -164
- package/lib/module/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/module/features/devConnect/devConnectPreferences.js +33 -6
- package/lib/module/features/devConnect/devConnectPreferences.js.map +1 -1
- package/lib/module/features/devConnect/devConnectUtils.js +94 -15
- package/lib/module/features/devConnect/devConnectUtils.js.map +1 -1
- package/lib/module/features/devConnect/index.js +11 -3
- package/lib/module/features/devConnect/index.js.map +1 -1
- package/lib/module/features/devConnect/nativeDevConnect.js +102 -0
- package/lib/module/features/devConnect/nativeDevConnect.js.map +1 -0
- package/lib/module/features/devConnect/platformDetect.js +8 -12
- package/lib/module/features/devConnect/platformDetect.js.map +1 -1
- package/lib/module/utils/debugPreferences.js +43 -6
- package/lib/module/utils/debugPreferences.js.map +1 -1
- package/lib/typescript/src/features/devConnect/DevConnectQrScanner.d.ts +3 -2
- package/lib/typescript/src/features/devConnect/DevConnectQrScanner.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts +6 -0
- package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts +18 -1
- package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/index.d.ts +2 -2
- package/lib/typescript/src/features/devConnect/index.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts +17 -0
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts.map +1 -0
- package/lib/typescript/src/features/devConnect/platformDetect.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/types.d.ts +3 -0
- package/lib/typescript/src/features/devConnect/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/debugPreferences.d.ts +2 -0
- package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
- package/package.json +4 -1
- package/react-native-debug-toolkit.podspec +18 -0
- package/src/features/devConnect/DevConnectQrScanner.tsx +101 -60
- package/src/features/devConnect/DevConnectTab.tsx +227 -105
- package/src/features/devConnect/devConnectPreferences.ts +50 -5
- package/src/features/devConnect/devConnectUtils.ts +122 -15
- package/src/features/devConnect/index.ts +13 -0
- package/src/features/devConnect/nativeDevConnect.ts +126 -0
- package/src/features/devConnect/platformDetect.ts +8 -13
- package/src/features/devConnect/types.ts +3 -0
- package/src/utils/debugPreferences.ts +49 -4
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
const
|
|
1
|
+
export const DEFAULT_METRO_PORT = '8081';
|
|
2
|
+
export const DEFAULT_DAEMON_PORT = '3799';
|
|
2
3
|
|
|
3
4
|
export interface MetroUrls {
|
|
4
5
|
expUrl: string;
|
|
5
6
|
httpUrl: string;
|
|
6
7
|
}
|
|
7
8
|
|
|
9
|
+
export interface MetroTarget {
|
|
10
|
+
host: string;
|
|
11
|
+
port: string;
|
|
12
|
+
hostPort: string;
|
|
13
|
+
statusUrl: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ParsedComputerTarget {
|
|
17
|
+
computerHost: string;
|
|
18
|
+
metroPort: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
8
21
|
export interface ParsedMetroQrPayload {
|
|
9
22
|
computerHost: string;
|
|
23
|
+
metroPort: string;
|
|
10
24
|
source: string;
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
function isValidIpv4(host: string): boolean {
|
|
14
28
|
const parts = host.split('.');
|
|
15
|
-
if (parts.length !== 4)
|
|
29
|
+
if (parts.length !== 4) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
16
32
|
|
|
17
33
|
return parts.every((part) => {
|
|
18
|
-
if (!/^\d{1,3}$/.test(part))
|
|
34
|
+
if (!/^\d{1,3}$/.test(part)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
19
37
|
const value = Number(part);
|
|
20
38
|
return value >= 0 && value <= 255 && String(value) === part;
|
|
21
39
|
});
|
|
@@ -29,31 +47,120 @@ function toUrlInput(raw: string): string {
|
|
|
29
47
|
return `http://${trimmed}`;
|
|
30
48
|
}
|
|
31
49
|
|
|
32
|
-
|
|
50
|
+
function parseHostAndPort(raw: string): { host: string; port: string } | null {
|
|
33
51
|
const trimmed = raw.trim();
|
|
34
|
-
if (!trimmed)
|
|
52
|
+
if (!trimmed) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
35
55
|
|
|
36
56
|
try {
|
|
37
57
|
const parsed = new URL(toUrlInput(trimmed));
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
return {
|
|
59
|
+
host: parsed.hostname.trim(),
|
|
60
|
+
port: parsed.port.trim(),
|
|
61
|
+
};
|
|
40
62
|
} catch {
|
|
41
63
|
return null;
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
|
|
45
|
-
export function
|
|
46
|
-
const
|
|
47
|
-
if (!
|
|
67
|
+
export function normalizeComputerHost(raw: string): string | null {
|
|
68
|
+
const parsed = parseHostAndPort(raw);
|
|
69
|
+
if (!parsed) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return isValidIpv4(parsed.host) ? parsed.host : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function normalizePort(raw: string): string | null {
|
|
77
|
+
const trimmed = raw.trim();
|
|
78
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const value = Number(trimmed);
|
|
83
|
+
if (!Number.isInteger(value) || value < 1 || value > 65535) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return String(value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function parseComputerTarget(raw: string): ParsedComputerTarget | null {
|
|
90
|
+
const parsed = parseHostAndPort(raw);
|
|
91
|
+
if (!parsed || !isValidIpv4(parsed.host)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const metroPort = parsed.port
|
|
96
|
+
? normalizePort(parsed.port)
|
|
97
|
+
: DEFAULT_METRO_PORT;
|
|
98
|
+
if (!metroPort) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
computerHost: parsed.host,
|
|
104
|
+
metroPort,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function normalizeMetroHost(rawHost: string): string | null {
|
|
109
|
+
const parsed = parseHostAndPort(rawHost);
|
|
110
|
+
if (!parsed) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (parsed.host === 'localhost') {
|
|
114
|
+
return parsed.host;
|
|
115
|
+
}
|
|
116
|
+
return isValidIpv4(parsed.host) ? parsed.host : null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function buildMetroTarget(rawHost: string, rawPort = DEFAULT_METRO_PORT): MetroTarget | null {
|
|
120
|
+
const host = normalizeMetroHost(rawHost);
|
|
121
|
+
if (!host) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const port = normalizePort(rawPort);
|
|
126
|
+
if (!port) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
host,
|
|
132
|
+
port,
|
|
133
|
+
hostPort: `${host}:${port}`,
|
|
134
|
+
statusUrl: `http://${host}:${port}/status`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function buildMetroUrls(rawHost: string, rawPort = DEFAULT_METRO_PORT): MetroUrls | null {
|
|
139
|
+
const target = buildMetroTarget(rawHost, rawPort);
|
|
140
|
+
if (!target) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
48
143
|
|
|
49
144
|
return {
|
|
50
|
-
expUrl: `exp://${
|
|
51
|
-
httpUrl: `http://${
|
|
145
|
+
expUrl: `exp://${target.hostPort}`,
|
|
146
|
+
httpUrl: `http://${target.hostPort}`,
|
|
52
147
|
};
|
|
53
148
|
}
|
|
54
149
|
|
|
55
150
|
export function parseMetroQrPayload(payload: string): ParsedMetroQrPayload | null {
|
|
56
|
-
const
|
|
57
|
-
if (!
|
|
58
|
-
|
|
151
|
+
const target = parseComputerTarget(payload);
|
|
152
|
+
if (!target) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return { ...target, source: payload };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function buildDaemonDeviceHost(computerHost: string, daemonPort: string): string {
|
|
159
|
+
const host = normalizeComputerHost(computerHost);
|
|
160
|
+
if (!host) {
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const port = normalizePort(daemonPort) ?? DEFAULT_DAEMON_PORT;
|
|
165
|
+
return port === DEFAULT_DAEMON_PORT ? host : `${host}:${port}`;
|
|
59
166
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DevConnectTab } from './DevConnectTab';
|
|
2
2
|
import { isCameraKitAvailable } from './cameraKit';
|
|
3
3
|
import { loadDevConnectPreferences } from './devConnectPreferences';
|
|
4
|
+
import { DEFAULT_DAEMON_PORT, DEFAULT_METRO_PORT } from './devConnectUtils';
|
|
5
|
+
import { isNativeDevConnectAvailable } from './nativeDevConnect';
|
|
4
6
|
import { isSimulator } from './platformDetect';
|
|
5
7
|
import { daemonClient } from '../../utils/DaemonClient';
|
|
6
8
|
import type { DebugFeature, DebugFeatureListener } from '../../types';
|
|
@@ -10,12 +12,17 @@ export type { DevConnectState } from './types';
|
|
|
10
12
|
export {
|
|
11
13
|
buildMetroUrls,
|
|
12
14
|
normalizeComputerHost,
|
|
15
|
+
normalizePort,
|
|
16
|
+
parseComputerTarget,
|
|
13
17
|
parseMetroQrPayload,
|
|
14
18
|
} from './devConnectUtils';
|
|
15
19
|
export {
|
|
16
20
|
loadDevConnectPreferences,
|
|
17
21
|
restoreDevConnectSettingsToDaemon,
|
|
18
22
|
saveComputerHost,
|
|
23
|
+
saveComputerTarget,
|
|
24
|
+
saveDaemonPort,
|
|
25
|
+
saveMetroPort,
|
|
19
26
|
} from './devConnectPreferences';
|
|
20
27
|
|
|
21
28
|
export const createDevConnectFeature = (): DebugFeature<DevConnectState> => {
|
|
@@ -23,7 +30,10 @@ export const createDevConnectFeature = (): DebugFeature<DevConnectState> => {
|
|
|
23
30
|
let state: DevConnectState = {
|
|
24
31
|
isSimulator: isSimulator(),
|
|
25
32
|
computerHost: '',
|
|
33
|
+
metroPort: DEFAULT_METRO_PORT,
|
|
34
|
+
daemonPort: DEFAULT_DAEMON_PORT,
|
|
26
35
|
qrAvailable: isCameraKitAvailable(),
|
|
36
|
+
nativeMetroAvailable: isNativeDevConnectAvailable(),
|
|
27
37
|
streaming: daemonClient.isConnected(),
|
|
28
38
|
};
|
|
29
39
|
|
|
@@ -44,6 +54,9 @@ export const createDevConnectFeature = (): DebugFeature<DevConnectState> => {
|
|
|
44
54
|
state = {
|
|
45
55
|
...state,
|
|
46
56
|
computerHost: preferences.computerHost,
|
|
57
|
+
metroPort: preferences.metroPort,
|
|
58
|
+
daemonPort: preferences.daemonPort,
|
|
59
|
+
nativeMetroAvailable: isNativeDevConnectAvailable(),
|
|
47
60
|
};
|
|
48
61
|
notify();
|
|
49
62
|
}).catch(() => {
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { buildMetroTarget } from './devConnectUtils';
|
|
4
|
+
|
|
5
|
+
interface DebugToolkitDevConnectNativeModule {
|
|
6
|
+
applyMetroHost: (hostPort: string) => Promise<{ hostPort?: string } | void>;
|
|
7
|
+
resetMetroHost: () => Promise<void>;
|
|
8
|
+
getMetroHost?: () => Promise<string | null>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type MetroBundleFailureReason =
|
|
12
|
+
| 'invalid_target'
|
|
13
|
+
| 'native_unavailable'
|
|
14
|
+
| 'fetch_unavailable'
|
|
15
|
+
| 'metro_unreachable'
|
|
16
|
+
| 'native_error';
|
|
17
|
+
|
|
18
|
+
export type MetroBundleResult =
|
|
19
|
+
| { ok: true; hostPort: string }
|
|
20
|
+
| { ok: false; reason: MetroBundleFailureReason; error?: string; statusUrl?: string };
|
|
21
|
+
|
|
22
|
+
type FetchLike = (
|
|
23
|
+
url: string,
|
|
24
|
+
init: { method: string },
|
|
25
|
+
) => Promise<{
|
|
26
|
+
ok?: boolean;
|
|
27
|
+
text?: () => Promise<string>;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
30
|
+
function getNativeModule(): DebugToolkitDevConnectNativeModule | null {
|
|
31
|
+
const nativeModule = NativeModules.DebugToolkitDevConnect as Partial<DebugToolkitDevConnectNativeModule> | undefined;
|
|
32
|
+
if (
|
|
33
|
+
nativeModule &&
|
|
34
|
+
typeof nativeModule.applyMetroHost === 'function' &&
|
|
35
|
+
typeof nativeModule.resetMetroHost === 'function'
|
|
36
|
+
) {
|
|
37
|
+
return nativeModule as DebugToolkitDevConnectNativeModule;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isNativeDevConnectAvailable(): boolean {
|
|
43
|
+
return getNativeModule() !== null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function checkMetroStatus(statusUrl: string): Promise<MetroBundleResult | null> {
|
|
47
|
+
const fetchImpl = globalThis.fetch as FetchLike | undefined;
|
|
48
|
+
if (!fetchImpl) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: 'fetch_unavailable',
|
|
52
|
+
statusUrl,
|
|
53
|
+
error: 'global fetch is not available',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetchImpl(statusUrl, { method: 'GET' });
|
|
59
|
+
const body = typeof response.text === 'function' ? await response.text() : '';
|
|
60
|
+
if (response.ok === false || !body.includes('packager-status:running')) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
reason: 'metro_unreachable',
|
|
64
|
+
statusUrl,
|
|
65
|
+
error: body || 'Metro status endpoint did not report running',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
reason: 'metro_unreachable',
|
|
73
|
+
statusUrl,
|
|
74
|
+
error: error instanceof Error ? error.message : String(error),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function applyMetroBundle(host: string, port: string): Promise<MetroBundleResult> {
|
|
80
|
+
const target = buildMetroTarget(host, port);
|
|
81
|
+
if (!target) {
|
|
82
|
+
return { ok: false, reason: 'invalid_target' };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const nativeModule = getNativeModule();
|
|
86
|
+
if (!nativeModule) {
|
|
87
|
+
return { ok: false, reason: 'native_unavailable' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const statusError = await checkMetroStatus(target.statusUrl);
|
|
91
|
+
if (statusError) {
|
|
92
|
+
return statusError;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const result = await nativeModule.applyMetroHost(target.hostPort);
|
|
97
|
+
return {
|
|
98
|
+
ok: true,
|
|
99
|
+
hostPort: result && typeof result.hostPort === 'string' ? result.hostPort : target.hostPort,
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
reason: 'native_error',
|
|
105
|
+
error: error instanceof Error ? error.message : String(error),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function resetMetroBundle(): Promise<MetroBundleResult | { ok: true }> {
|
|
111
|
+
const nativeModule = getNativeModule();
|
|
112
|
+
if (!nativeModule) {
|
|
113
|
+
return { ok: false, reason: 'native_unavailable' };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await nativeModule.resetMetroHost();
|
|
118
|
+
return { ok: true };
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
reason: 'native_error',
|
|
123
|
+
error: error instanceof Error ? error.message : String(error),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
2
|
|
|
3
3
|
export function isSimulator(): boolean {
|
|
4
4
|
const { OS } = Platform;
|
|
5
|
+
const constants = (Platform.constants ?? {}) as Record<string, unknown>;
|
|
5
6
|
|
|
6
7
|
if (OS === 'android') {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
if (constants.isEmulator === true) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
const model = String(constants.Model ?? constants.model ?? '').toLowerCase();
|
|
10
12
|
return model.includes('sdk') || model.includes('emulator') || model.includes('google_sdk');
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
if (OS === 'ios') {
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
|
16
|
+
const model = String(constants.model ?? '').toLowerCase();
|
|
17
|
+
return model.includes('simulator');
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
return false;
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
1
|
type AsyncStorageLike = { getItem: (k: string) => Promise<string | null>; setItem: (k: string, v: string) => Promise<void> };
|
|
2
|
+
type NativePreferencesLike = { getPreference: (k: string) => Promise<string | null>; setPreference: (k: string, v: string) => Promise<void> };
|
|
3
3
|
|
|
4
4
|
const memoryStore = new Map<string, string>();
|
|
5
5
|
|
|
6
6
|
function loadAsyncStorage(): AsyncStorageLike | null {
|
|
7
7
|
try {
|
|
8
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
9
8
|
const mod = require('@react-native-async-storage/async-storage');
|
|
10
|
-
if (mod && typeof mod.getItem === 'function')
|
|
9
|
+
if (mod && typeof mod.getItem === 'function') {
|
|
10
|
+
return mod;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadNativePreferences(): NativePreferencesLike | null {
|
|
19
|
+
try {
|
|
20
|
+
const { NativeModules } = require('react-native') as { NativeModules?: { DebugToolkitDevConnect?: Partial<NativePreferencesLike> } };
|
|
21
|
+
const mod = NativeModules?.DebugToolkitDevConnect;
|
|
22
|
+
if (
|
|
23
|
+
mod &&
|
|
24
|
+
typeof mod.getPreference === 'function' &&
|
|
25
|
+
typeof mod.setPreference === 'function'
|
|
26
|
+
) {
|
|
27
|
+
return mod as NativePreferencesLike;
|
|
28
|
+
}
|
|
11
29
|
return null;
|
|
12
30
|
} catch {
|
|
13
31
|
return null;
|
|
@@ -20,6 +38,16 @@ export async function setPreference(key: string, value: string): Promise<void> {
|
|
|
20
38
|
if (AsyncStorage) {
|
|
21
39
|
try {
|
|
22
40
|
await AsyncStorage.setItem(key, value);
|
|
41
|
+
return;
|
|
42
|
+
} catch {
|
|
43
|
+
// degrade to memory only
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const nativePreferences = loadNativePreferences();
|
|
48
|
+
if (nativePreferences) {
|
|
49
|
+
try {
|
|
50
|
+
await nativePreferences.setPreference(key, value);
|
|
23
51
|
} catch {
|
|
24
52
|
// degrade to memory only
|
|
25
53
|
}
|
|
@@ -31,11 +59,26 @@ export async function getPreference(key: string): Promise<string | null> {
|
|
|
31
59
|
if (AsyncStorage) {
|
|
32
60
|
try {
|
|
33
61
|
const val = await AsyncStorage.getItem(key);
|
|
34
|
-
if (val !== null)
|
|
62
|
+
if (val !== null) {
|
|
63
|
+
return val;
|
|
64
|
+
}
|
|
35
65
|
} catch {
|
|
36
66
|
// fall through to memory
|
|
37
67
|
}
|
|
38
68
|
}
|
|
69
|
+
|
|
70
|
+
const nativePreferences = loadNativePreferences();
|
|
71
|
+
if (nativePreferences) {
|
|
72
|
+
try {
|
|
73
|
+
const val = await nativePreferences.getPreference(key);
|
|
74
|
+
if (val !== null) {
|
|
75
|
+
return val;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// fall through to memory
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
39
82
|
return memoryStore.get(key) ?? null;
|
|
40
83
|
}
|
|
41
84
|
|
|
@@ -46,4 +89,6 @@ export const KEYS = {
|
|
|
46
89
|
networkLogs: '@react_native_debug_toolkit/network_logs',
|
|
47
90
|
trackLogs: '@react_native_debug_toolkit/track_logs',
|
|
48
91
|
computerHost: '@react_native_debug_toolkit/computer_host',
|
|
92
|
+
metroPort: '@react_native_debug_toolkit/metro_port',
|
|
93
|
+
daemonPort: '@react_native_debug_toolkit/daemon_port',
|
|
49
94
|
} as const;
|