rn-network-quality 0.1.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 (72) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +899 -0
  3. package/android/build.gradle +60 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/rnnetworkquality/CellularInfo.kt +34 -0
  6. package/android/src/main/java/com/rnnetworkquality/DownloadPolicy.kt +26 -0
  7. package/android/src/main/java/com/rnnetworkquality/Mappers.kt +106 -0
  8. package/android/src/main/java/com/rnnetworkquality/NetworkMonitor.kt +263 -0
  9. package/android/src/main/java/com/rnnetworkquality/NetworkProbe.kt +668 -0
  10. package/android/src/main/java/com/rnnetworkquality/NetworkQualityModule.kt +287 -0
  11. package/android/src/main/java/com/rnnetworkquality/NetworkQualityPackage.kt +25 -0
  12. package/android/src/main/java/com/rnnetworkquality/NetworkSnapshot.kt +83 -0
  13. package/android/src/main/java/com/rnnetworkquality/SnapshotBuilder.kt +92 -0
  14. package/android/src/main/java/com/rnnetworkquality/Throttler.kt +100 -0
  15. package/ios/CellularInfo.swift +47 -0
  16. package/ios/NetworkProbe.swift +391 -0
  17. package/ios/NetworkQuality.h +8 -0
  18. package/ios/NetworkQuality.mm +88 -0
  19. package/ios/NetworkQualityImpl.swift +371 -0
  20. package/ios/PathSnapshot.swift +109 -0
  21. package/ios/PrivacyInfo.xcprivacy +23 -0
  22. package/ios/Throttler.swift +174 -0
  23. package/lib/module/NativeNetworkQuality.js +5 -0
  24. package/lib/module/NativeNetworkQuality.js.map +1 -0
  25. package/lib/module/classify.js +126 -0
  26. package/lib/module/classify.js.map +1 -0
  27. package/lib/module/constants.js +52 -0
  28. package/lib/module/constants.js.map +1 -0
  29. package/lib/module/errors.js +18 -0
  30. package/lib/module/errors.js.map +1 -0
  31. package/lib/module/hooks.js +78 -0
  32. package/lib/module/hooks.js.map +1 -0
  33. package/lib/module/index.js +19 -0
  34. package/lib/module/index.js.map +1 -0
  35. package/lib/module/manager.js +595 -0
  36. package/lib/module/manager.js.map +1 -0
  37. package/lib/module/normalize.js +35 -0
  38. package/lib/module/normalize.js.map +1 -0
  39. package/lib/module/package.json +1 -0
  40. package/lib/module/types.js +2 -0
  41. package/lib/module/types.js.map +1 -0
  42. package/lib/typescript/package.json +1 -0
  43. package/lib/typescript/src/NativeNetworkQuality.d.ts +50 -0
  44. package/lib/typescript/src/NativeNetworkQuality.d.ts.map +1 -0
  45. package/lib/typescript/src/classify.d.ts +12 -0
  46. package/lib/typescript/src/classify.d.ts.map +1 -0
  47. package/lib/typescript/src/constants.d.ts +15 -0
  48. package/lib/typescript/src/constants.d.ts.map +1 -0
  49. package/lib/typescript/src/errors.d.ts +13 -0
  50. package/lib/typescript/src/errors.d.ts.map +1 -0
  51. package/lib/typescript/src/hooks.d.ts +18 -0
  52. package/lib/typescript/src/hooks.d.ts.map +1 -0
  53. package/lib/typescript/src/index.d.ts +13 -0
  54. package/lib/typescript/src/index.d.ts.map +1 -0
  55. package/lib/typescript/src/manager.d.ts +98 -0
  56. package/lib/typescript/src/manager.d.ts.map +1 -0
  57. package/lib/typescript/src/normalize.d.ts +5 -0
  58. package/lib/typescript/src/normalize.d.ts.map +1 -0
  59. package/lib/typescript/src/types.d.ts +171 -0
  60. package/lib/typescript/src/types.d.ts.map +1 -0
  61. package/mock.js +318 -0
  62. package/package.json +161 -0
  63. package/rn-network-quality.podspec +32 -0
  64. package/src/NativeNetworkQuality.ts +58 -0
  65. package/src/classify.ts +208 -0
  66. package/src/constants.ts +48 -0
  67. package/src/errors.ts +23 -0
  68. package/src/hooks.ts +110 -0
  69. package/src/index.tsx +25 -0
  70. package/src/manager.ts +891 -0
  71. package/src/normalize.ts +81 -0
  72. package/src/types.ts +207 -0
package/mock.js ADDED
@@ -0,0 +1,318 @@
1
+ 'use strict';
2
+
3
+ const React = require('react');
4
+
5
+ const DEFAULT_CONFIG = {
6
+ throttleMs: 1_000,
7
+ bandwidthChangeThresholdPct: 10,
8
+ validationGraceMs: 10_000,
9
+ thresholds: {
10
+ excellent: { minDownlinkKbps: 20_000, maxRttMs: 50 },
11
+ good: { minDownlinkKbps: 5_000, maxRttMs: 150 },
12
+ moderate: { minDownlinkKbps: 1_000, maxRttMs: 400 },
13
+ },
14
+ probe: {
15
+ latencyUrl: 'https://www.gstatic.com/generate_204',
16
+ downloadUrl: 'https://speed.cloudflare.com/__down?bytes=3000000',
17
+ latencySamples: 3,
18
+ timeoutMs: 8_000,
19
+ downloadMaxDurationMs: 3_000,
20
+ downloadMaxBytes: 3_000_000,
21
+ resultTtlMs: 60_000,
22
+ },
23
+ autoProbe: {
24
+ enabled: false,
25
+ intervalMs: 60_000,
26
+ onTransportChange: true,
27
+ allowOnExpensive: false,
28
+ allowOnConstrained: false,
29
+ },
30
+ };
31
+
32
+ const QUALITY_ORDER = ['offline', 'poor', 'moderate', 'good', 'excellent'];
33
+
34
+ class NetworkQualityError extends Error {
35
+ constructor(code, message, options) {
36
+ super(message);
37
+ this.name = 'NetworkQualityError';
38
+ this.code = code;
39
+ this.cause = options?.cause;
40
+ }
41
+ }
42
+
43
+ const GOOD_WIFI_STATE = {
44
+ isConnected: true,
45
+ isValidated: true,
46
+ isCaptivePortal: false,
47
+ transport: 'wifi',
48
+ isVpn: false,
49
+ isExpensive: false,
50
+ isConstrained: false,
51
+ isRoaming: false,
52
+ downlinkKbps: 10_000,
53
+ uplinkKbps: 5_000,
54
+ signalStrength: null,
55
+ cellularGeneration: null,
56
+ supportsIPv4: true,
57
+ supportsIPv6: true,
58
+ supportsDNS: true,
59
+ unsatisfiedReason: null,
60
+ timestamp: 0,
61
+ quality: 'good',
62
+ qualitySource: 'os-estimate',
63
+ effectiveDownlinkKbps: 10_000,
64
+ effectiveRttMs: null,
65
+ lastProbe: null,
66
+ lastProbeFailure: null,
67
+ reasons: ['downlink 10000 kbps → good'],
68
+ };
69
+
70
+ const GOOD_PROBE_RESULT = {
71
+ rttMs: 75,
72
+ downlinkKbps: 10_000,
73
+ bytesReceived: 200_000,
74
+ durationMs: 250,
75
+ transport: 'wifi',
76
+ downloadError: null,
77
+ timestamp: 0,
78
+ };
79
+
80
+ function clone(value) {
81
+ return JSON.parse(JSON.stringify(value));
82
+ }
83
+
84
+ let config = clone(DEFAULT_CONFIG);
85
+ let lastProbe = null;
86
+
87
+ function classifyNetworkQuality(
88
+ snapshot,
89
+ probe,
90
+ classifierConfig = config,
91
+ now = Date.now(),
92
+ context = {}
93
+ ) {
94
+ if (!snapshot.isConnected) {
95
+ return classification('offline', 'none', null, null, ['not-connected']);
96
+ }
97
+ if (snapshot.isCaptivePortal === true) {
98
+ return classification('poor', 'none', null, null, ['captive-portal']);
99
+ }
100
+ const failure = context.lastProbeFailure ?? null;
101
+ if (
102
+ failure !== null &&
103
+ failure.transport === snapshot.transport &&
104
+ now - failure.timestamp <=
105
+ (context.probeFailureTtlMs ?? classifierConfig.probe.resultTtlMs) &&
106
+ (probe === null || failure.timestamp > probe.timestamp)
107
+ ) {
108
+ return classification('poor', 'probe', null, null, [
109
+ `probe failed: ${failure.code}`,
110
+ ]);
111
+ }
112
+ if (snapshot.isValidated === false) {
113
+ const networkChangedAt = context.networkChangedAt ?? null;
114
+ if (
115
+ networkChangedAt !== null &&
116
+ now - networkChangedAt < classifierConfig.validationGraceMs
117
+ ) {
118
+ return classification('unknown', 'none', null, null, ['validating']);
119
+ }
120
+ return classification('poor', 'none', null, null, ['not-validated']);
121
+ }
122
+
123
+ const reasons = [];
124
+ let freshProbe = null;
125
+ if (probe !== null) {
126
+ if (probe.transport !== snapshot.transport) {
127
+ reasons.push('probe stale (transport changed)');
128
+ } else if (
129
+ now - probe.timestamp >
130
+ (context.probeResultTtlMs ?? classifierConfig.probe.resultTtlMs)
131
+ ) {
132
+ reasons.push('probe stale (expired)');
133
+ } else {
134
+ freshProbe = probe;
135
+ }
136
+ }
137
+
138
+ const usedProbeDownlink = freshProbe?.downlinkKbps != null;
139
+ const usedProbeRtt = freshProbe?.rttMs != null;
140
+ const downlink = freshProbe?.downlinkKbps ?? snapshot.downlinkKbps ?? null;
141
+ const rtt = freshProbe?.rttMs ?? null;
142
+ const tiers = [];
143
+ if (downlink !== null) {
144
+ const tier = downlinkTier(downlink, classifierConfig.thresholds);
145
+ tiers.push(tier);
146
+ reasons.push(`downlink ${downlink} kbps → ${tier}`);
147
+ }
148
+ if (rtt !== null) {
149
+ const tier = rttTier(rtt, classifierConfig.thresholds);
150
+ tiers.push(tier);
151
+ reasons.push(`rtt ${rtt} ms → ${tier}`);
152
+ }
153
+ if (tiers.length > 0) {
154
+ const quality = tiers.reduce((worst, tier) =>
155
+ QUALITY_ORDER.indexOf(tier) < QUALITY_ORDER.indexOf(worst) ? tier : worst
156
+ );
157
+ return classification(
158
+ quality,
159
+ usedProbeDownlink || usedProbeRtt ? 'probe' : 'os-estimate',
160
+ downlink,
161
+ rtt,
162
+ reasons
163
+ );
164
+ }
165
+
166
+ const heuristic =
167
+ snapshot.cellularGeneration === '4g' || snapshot.cellularGeneration === '5g'
168
+ ? 'good'
169
+ : snapshot.cellularGeneration === '3g'
170
+ ? 'moderate'
171
+ : snapshot.cellularGeneration === '2g'
172
+ ? 'poor'
173
+ : 'unknown';
174
+ return classification(
175
+ heuristic,
176
+ heuristic === 'unknown' ? 'none' : 'heuristic',
177
+ null,
178
+ null,
179
+ [
180
+ heuristic === 'unknown'
181
+ ? 'no quality metrics available'
182
+ : `cellular generation ${snapshot.cellularGeneration} → ${heuristic}`,
183
+ ]
184
+ );
185
+ }
186
+
187
+ function classification(quality, qualitySource, downlink, rtt, reasons) {
188
+ return {
189
+ quality,
190
+ qualitySource,
191
+ effectiveDownlinkKbps: downlink,
192
+ effectiveRttMs: rtt,
193
+ reasons,
194
+ };
195
+ }
196
+
197
+ function downlinkTier(value, thresholds) {
198
+ if (value >= thresholds.excellent.minDownlinkKbps) return 'excellent';
199
+ if (value >= thresholds.good.minDownlinkKbps) return 'good';
200
+ if (value >= thresholds.moderate.minDownlinkKbps) return 'moderate';
201
+ return 'poor';
202
+ }
203
+
204
+ function rttTier(value, thresholds) {
205
+ if (value <= thresholds.excellent.maxRttMs) return 'excellent';
206
+ if (value <= thresholds.good.maxRttMs) return 'good';
207
+ if (value <= thresholds.moderate.maxRttMs) return 'moderate';
208
+ return 'poor';
209
+ }
210
+
211
+ function isQualityAtLeast(quality, minimum) {
212
+ if (quality === 'unknown' || minimum === 'unknown') return false;
213
+ return QUALITY_ORDER.indexOf(quality) >= QUALITY_ORDER.indexOf(minimum);
214
+ }
215
+
216
+ function isSupported() {
217
+ return true;
218
+ }
219
+
220
+ function configure(patch) {
221
+ config = {
222
+ ...config,
223
+ ...patch,
224
+ thresholds: {
225
+ ...config.thresholds,
226
+ ...patch.thresholds,
227
+ excellent: {
228
+ ...config.thresholds.excellent,
229
+ ...patch.thresholds?.excellent,
230
+ },
231
+ good: { ...config.thresholds.good, ...patch.thresholds?.good },
232
+ moderate: {
233
+ ...config.thresholds.moderate,
234
+ ...patch.thresholds?.moderate,
235
+ },
236
+ },
237
+ probe: { ...config.probe, ...patch.probe },
238
+ autoProbe: { ...config.autoProbe, ...patch.autoProbe },
239
+ };
240
+ }
241
+
242
+ function getConfig() {
243
+ return clone(config);
244
+ }
245
+
246
+ function getNetworkQuality() {
247
+ return Promise.resolve(GOOD_WIFI_STATE);
248
+ }
249
+
250
+ function addNetworkQualityListener(listener) {
251
+ let active = true;
252
+ Promise.resolve().then(() => {
253
+ if (active) listener(GOOD_WIFI_STATE);
254
+ });
255
+ return {
256
+ remove() {
257
+ active = false;
258
+ },
259
+ };
260
+ }
261
+
262
+ function probeNetwork() {
263
+ lastProbe = GOOD_PROBE_RESULT;
264
+ return Promise.resolve(GOOD_PROBE_RESULT);
265
+ }
266
+
267
+ function getLastProbeResult() {
268
+ return lastProbe;
269
+ }
270
+
271
+ function useNetworkQuality() {
272
+ return GOOD_WIFI_STATE;
273
+ }
274
+
275
+ function useNetworkProbe() {
276
+ const mounted = React.useRef(true);
277
+ const [isProbing, setIsProbing] = React.useState(false);
278
+ const [result, setResult] = React.useState(null);
279
+ const [error, setError] = React.useState(null);
280
+ React.useEffect(() => {
281
+ mounted.current = true;
282
+ return () => {
283
+ mounted.current = false;
284
+ };
285
+ }, []);
286
+ const probe = React.useCallback(async (options) => {
287
+ setIsProbing(true);
288
+ setError(null);
289
+ try {
290
+ const nextResult = await probeNetwork(options);
291
+ if (mounted.current) setResult(nextResult);
292
+ return nextResult;
293
+ } catch (nextError) {
294
+ if (mounted.current) setError(nextError);
295
+ throw nextError;
296
+ } finally {
297
+ if (mounted.current) setIsProbing(false);
298
+ }
299
+ }, []);
300
+ return { probe, isProbing, result, error };
301
+ }
302
+
303
+ module.exports = {
304
+ DEFAULT_CONFIG,
305
+ QUALITY_ORDER,
306
+ NetworkQualityError,
307
+ classifyNetworkQuality,
308
+ isQualityAtLeast,
309
+ isSupported,
310
+ configure,
311
+ getConfig,
312
+ getNetworkQuality,
313
+ addNetworkQualityListener,
314
+ probeNetwork,
315
+ getLastProbeResult,
316
+ useNetworkQuality,
317
+ useNetworkProbe,
318
+ };
package/package.json ADDED
@@ -0,0 +1,161 @@
1
+ {
2
+ "name": "rn-network-quality",
3
+ "version": "0.1.0",
4
+ "description": "Live network quality signals for React Native — bandwidth, link quality and connectivity via a New Architecture TurboModule",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "react-native": "./src/index.tsx",
8
+ "source": "./src/index.tsx",
9
+ "exports": {
10
+ ".": {
11
+ "rn-network-quality-source": "./src/index.tsx",
12
+ "types": "./lib/typescript/src/index.d.ts",
13
+ "default": "./lib/module/index.js"
14
+ },
15
+ "./mock": "./mock.js",
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "src",
20
+ "lib",
21
+ "android",
22
+ "ios",
23
+ "*.podspec",
24
+ "mock.js",
25
+ "!ios/build",
26
+ "!android/build",
27
+ "!android/.gradle",
28
+ "!android/gradle",
29
+ "!android/gradlew",
30
+ "!android/gradlew.bat",
31
+ "!android/local.properties",
32
+ "!android/src/test",
33
+ "!**/__tests__",
34
+ "!**/__fixtures__",
35
+ "!**/__mocks__",
36
+ "!**/.*"
37
+ ],
38
+ "scripts": {
39
+ "example": "yarn workspace rn-network-quality-example",
40
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
41
+ "prepare": "bob build",
42
+ "test": "jest",
43
+ "test:coverage": "jest --coverage --runInBand",
44
+ "typecheck": "tsc",
45
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
46
+ "lint:fix": "yarn lint --fix",
47
+ "codegen:check": "node scripts/check-codegen.mjs",
48
+ "pack:check": "node scripts/check-package.mjs",
49
+ "commitlint": "commitlint",
50
+ "release": "release-it"
51
+ },
52
+ "keywords": [
53
+ "react-native",
54
+ "network",
55
+ "network-quality",
56
+ "bandwidth",
57
+ "connectivity",
58
+ "turbomodule",
59
+ "new-architecture",
60
+ "nwpathmonitor",
61
+ "networkcapabilities",
62
+ "ios",
63
+ "android"
64
+ ],
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "https://github.com/kruthikRgowda/rn-network-quality"
68
+ },
69
+ "author": "kruthik <kruthikbr2@gmail.com> (https://github.com/kruthikRgowda)",
70
+ "license": "MIT",
71
+ "bugs": {
72
+ "url": "https://github.com/kruthikRgowda/rn-network-quality/issues"
73
+ },
74
+ "homepage": "https://github.com/kruthikRgowda/rn-network-quality",
75
+ "sideEffects": false,
76
+ "publishConfig": {
77
+ "access": "public",
78
+ "registry": "https://registry.npmjs.org/",
79
+ "provenance": true
80
+ },
81
+ "devDependencies": {
82
+ "@commitlint/cli": "^21.2.3",
83
+ "@commitlint/config-conventional": "^21.2.3",
84
+ "@eslint/compat": "^2.1.0",
85
+ "@eslint/eslintrc": "^3.3.6",
86
+ "@eslint/js": "^9.39.5",
87
+ "@react-native/babel-preset": "0.87.1",
88
+ "@react-native/eslint-config": "0.87.1",
89
+ "@react-native/jest-preset": "0.87.1",
90
+ "@release-it/conventional-changelog": "^12.0.2",
91
+ "@testing-library/react-native": "13.3.3",
92
+ "@types/jest": "30.0.0",
93
+ "@types/react": "^19.2.0",
94
+ "@types/react-test-renderer": "^19",
95
+ "del-cli": "^7.0.0",
96
+ "eslint": "^9.39.5",
97
+ "eslint-config-prettier": "^10.1.8",
98
+ "eslint-plugin-ft-flow": "^3.0.11",
99
+ "eslint-plugin-prettier": "^5.5.6",
100
+ "jest": "29.7.0",
101
+ "lefthook": "^2.1.14",
102
+ "prettier": "^3.9.8",
103
+ "react": "19.2.3",
104
+ "react-native": "0.87.1",
105
+ "react-native-builder-bob": "^0.43.1",
106
+ "react-test-renderer": "19.2.3",
107
+ "release-it": "^21.1.0",
108
+ "turbo": "^2.10.8",
109
+ "typescript": "^6.0.3"
110
+ },
111
+ "peerDependencies": {
112
+ "react": "*",
113
+ "react-native": ">=0.80.0"
114
+ },
115
+ "workspaces": [
116
+ "example"
117
+ ],
118
+ "packageManager": "yarn@4.11.0",
119
+ "react-native-builder-bob": {
120
+ "source": "src",
121
+ "output": "lib",
122
+ "targets": [
123
+ [
124
+ "module",
125
+ {
126
+ "esm": true
127
+ }
128
+ ],
129
+ [
130
+ "typescript",
131
+ {
132
+ "project": "tsconfig.build.json"
133
+ }
134
+ ]
135
+ ]
136
+ },
137
+ "codegenConfig": {
138
+ "name": "RNNetworkQualitySpec",
139
+ "type": "modules",
140
+ "jsSrcsDir": "src",
141
+ "android": {
142
+ "javaPackageName": "com.rnnetworkquality"
143
+ },
144
+ "ios": {
145
+ "modules": {
146
+ "NetworkQuality": {
147
+ "className": "NetworkQuality",
148
+ "unstableRequiresMainQueueSetup": false
149
+ }
150
+ }
151
+ }
152
+ },
153
+ "create-react-native-library": {
154
+ "type": "turbo-module",
155
+ "languages": "kotlin-objc",
156
+ "tools": [
157
+ "eslint"
158
+ ],
159
+ "version": "0.63.1"
160
+ }
161
+ }
@@ -0,0 +1,32 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "rn-network-quality"
7
+ s.module_name = "rn_network_quality"
8
+ s.version = package["version"]
9
+ s.summary = package["description"]
10
+ s.homepage = package["homepage"]
11
+ s.license = package["license"]
12
+ s.authors = package["author"]
13
+
14
+ s.platforms = { :ios => min_ios_version_supported }
15
+ s.source = {
16
+ :git => "https://github.com/kruthikRgowda/rn-network-quality",
17
+ :tag => "v#{s.version}"
18
+ }
19
+
20
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
21
+ # The adapter header imports a C++ Codegen interface and must stay out of the
22
+ # Swift module umbrella. The ObjC++ implementation includes it directly.
23
+ s.private_header_files = "ios/NetworkQuality.h"
24
+ s.frameworks = "Network", "CoreTelephony"
25
+ s.swift_version = "5.9"
26
+ s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }
27
+ s.resource_bundles = {
28
+ "RNNetworkQualityPrivacy" => ["ios/PrivacyInfo.xcprivacy"]
29
+ }
30
+
31
+ install_modules_dependencies(s)
32
+ end
@@ -0,0 +1,58 @@
1
+ import {
2
+ TurboModuleRegistry,
3
+ type CodegenTypes,
4
+ type TurboModule,
5
+ } from 'react-native';
6
+
7
+ export type NativeNetworkSnapshot = {
8
+ isConnected: boolean;
9
+ isValidated: boolean | null;
10
+ isCaptivePortal: boolean | null;
11
+ transport: string;
12
+ isVpn: boolean | null;
13
+ isExpensive: boolean;
14
+ isConstrained: boolean;
15
+ isRoaming: boolean | null;
16
+ downlinkKbps: number | null;
17
+ uplinkKbps: number | null;
18
+ signalStrength: number | null;
19
+ cellularGeneration: string | null;
20
+ supportsIPv4: boolean | null;
21
+ supportsIPv6: boolean | null;
22
+ supportsDNS: boolean | null;
23
+ unsatisfiedReason: string | null;
24
+ timestamp: number;
25
+ };
26
+
27
+ export type NativeMonitorOptions = {
28
+ throttleMs: number;
29
+ bandwidthChangeThresholdPct: number;
30
+ };
31
+
32
+ export type NativeProbeOptions = {
33
+ latencyUrl: string;
34
+ downloadUrl: string | null;
35
+ latencySamples: number;
36
+ timeoutMs: number;
37
+ downloadMaxDurationMs: number;
38
+ downloadMaxBytes: number;
39
+ };
40
+
41
+ export type NativeProbeResult = {
42
+ rttMs: number | null;
43
+ downlinkKbps: number | null;
44
+ bytesReceived: number;
45
+ durationMs: number;
46
+ downloadError: string | null;
47
+ timestamp: number;
48
+ };
49
+
50
+ export interface Spec extends TurboModule {
51
+ getCurrentState(): Promise<NativeNetworkSnapshot>;
52
+ startMonitoring(options: NativeMonitorOptions): void;
53
+ stopMonitoring(): void;
54
+ probe(options: NativeProbeOptions): Promise<NativeProbeResult>;
55
+ readonly onNetworkStateChange: CodegenTypes.EventEmitter<NativeNetworkSnapshot>;
56
+ }
57
+
58
+ export default TurboModuleRegistry.get<Spec>('NetworkQuality');