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.
- package/LICENSE +20 -0
- package/README.md +899 -0
- package/android/build.gradle +60 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/rnnetworkquality/CellularInfo.kt +34 -0
- package/android/src/main/java/com/rnnetworkquality/DownloadPolicy.kt +26 -0
- package/android/src/main/java/com/rnnetworkquality/Mappers.kt +106 -0
- package/android/src/main/java/com/rnnetworkquality/NetworkMonitor.kt +263 -0
- package/android/src/main/java/com/rnnetworkquality/NetworkProbe.kt +668 -0
- package/android/src/main/java/com/rnnetworkquality/NetworkQualityModule.kt +287 -0
- package/android/src/main/java/com/rnnetworkquality/NetworkQualityPackage.kt +25 -0
- package/android/src/main/java/com/rnnetworkquality/NetworkSnapshot.kt +83 -0
- package/android/src/main/java/com/rnnetworkquality/SnapshotBuilder.kt +92 -0
- package/android/src/main/java/com/rnnetworkquality/Throttler.kt +100 -0
- package/ios/CellularInfo.swift +47 -0
- package/ios/NetworkProbe.swift +391 -0
- package/ios/NetworkQuality.h +8 -0
- package/ios/NetworkQuality.mm +88 -0
- package/ios/NetworkQualityImpl.swift +371 -0
- package/ios/PathSnapshot.swift +109 -0
- package/ios/PrivacyInfo.xcprivacy +23 -0
- package/ios/Throttler.swift +174 -0
- package/lib/module/NativeNetworkQuality.js +5 -0
- package/lib/module/NativeNetworkQuality.js.map +1 -0
- package/lib/module/classify.js +126 -0
- package/lib/module/classify.js.map +1 -0
- package/lib/module/constants.js +52 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/errors.js +18 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/hooks.js +78 -0
- package/lib/module/hooks.js.map +1 -0
- package/lib/module/index.js +19 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/manager.js +595 -0
- package/lib/module/manager.js.map +1 -0
- package/lib/module/normalize.js +35 -0
- package/lib/module/normalize.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeNetworkQuality.d.ts +50 -0
- package/lib/typescript/src/NativeNetworkQuality.d.ts.map +1 -0
- package/lib/typescript/src/classify.d.ts +12 -0
- package/lib/typescript/src/classify.d.ts.map +1 -0
- package/lib/typescript/src/constants.d.ts +15 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +13 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/hooks.d.ts +18 -0
- package/lib/typescript/src/hooks.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +13 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/manager.d.ts +98 -0
- package/lib/typescript/src/manager.d.ts.map +1 -0
- package/lib/typescript/src/normalize.d.ts +5 -0
- package/lib/typescript/src/normalize.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +171 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/mock.js +318 -0
- package/package.json +161 -0
- package/rn-network-quality.podspec +32 -0
- package/src/NativeNetworkQuality.ts +58 -0
- package/src/classify.ts +208 -0
- package/src/constants.ts +48 -0
- package/src/errors.ts +23 -0
- package/src/hooks.ts +110 -0
- package/src/index.tsx +25 -0
- package/src/manager.ts +891 -0
- package/src/normalize.ts +81 -0
- package/src/types.ts +207 -0
package/src/normalize.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { NativeNetworkSnapshot } from './NativeNetworkQuality';
|
|
2
|
+
import type {
|
|
3
|
+
CellularGeneration,
|
|
4
|
+
NetworkSnapshot,
|
|
5
|
+
Transport,
|
|
6
|
+
UnsatisfiedReason,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
const TRANSPORTS = [
|
|
10
|
+
'wifi',
|
|
11
|
+
'cellular',
|
|
12
|
+
'ethernet',
|
|
13
|
+
'bluetooth',
|
|
14
|
+
'vpn',
|
|
15
|
+
'other',
|
|
16
|
+
'none',
|
|
17
|
+
'unknown',
|
|
18
|
+
] as const satisfies readonly Transport[];
|
|
19
|
+
|
|
20
|
+
const CELLULAR_GENERATIONS = [
|
|
21
|
+
'2g',
|
|
22
|
+
'3g',
|
|
23
|
+
'4g',
|
|
24
|
+
'5g',
|
|
25
|
+
] as const satisfies readonly CellularGeneration[];
|
|
26
|
+
|
|
27
|
+
const UNSATISFIED_REASONS = [
|
|
28
|
+
'notAvailable',
|
|
29
|
+
'cellularDenied',
|
|
30
|
+
'wifiDenied',
|
|
31
|
+
'localNetworkDenied',
|
|
32
|
+
'vpnInactive',
|
|
33
|
+
'unknown',
|
|
34
|
+
] as const satisfies readonly UnsatisfiedReason[];
|
|
35
|
+
|
|
36
|
+
function includes<T extends string>(
|
|
37
|
+
values: readonly T[],
|
|
38
|
+
value: string
|
|
39
|
+
): value is T {
|
|
40
|
+
return values.some((candidate) => candidate === value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Narrows a native snapshot to the stable public string unions. */
|
|
44
|
+
export function normalizeSnapshot(
|
|
45
|
+
snapshot: NativeNetworkSnapshot
|
|
46
|
+
): NetworkSnapshot {
|
|
47
|
+
const transport = includes(TRANSPORTS, snapshot.transport)
|
|
48
|
+
? snapshot.transport
|
|
49
|
+
: 'unknown';
|
|
50
|
+
const cellularGeneration =
|
|
51
|
+
snapshot.cellularGeneration !== null &&
|
|
52
|
+
includes(CELLULAR_GENERATIONS, snapshot.cellularGeneration)
|
|
53
|
+
? snapshot.cellularGeneration
|
|
54
|
+
: null;
|
|
55
|
+
const unsatisfiedReason =
|
|
56
|
+
snapshot.unsatisfiedReason === null
|
|
57
|
+
? null
|
|
58
|
+
: includes(UNSATISFIED_REASONS, snapshot.unsatisfiedReason)
|
|
59
|
+
? snapshot.unsatisfiedReason
|
|
60
|
+
: 'unknown';
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
isConnected: snapshot.isConnected,
|
|
64
|
+
isValidated: snapshot.isValidated,
|
|
65
|
+
isCaptivePortal: snapshot.isCaptivePortal,
|
|
66
|
+
transport,
|
|
67
|
+
isVpn: snapshot.isVpn,
|
|
68
|
+
isExpensive: snapshot.isExpensive,
|
|
69
|
+
isConstrained: snapshot.isConstrained,
|
|
70
|
+
isRoaming: snapshot.isRoaming,
|
|
71
|
+
downlinkKbps: snapshot.downlinkKbps,
|
|
72
|
+
uplinkKbps: snapshot.uplinkKbps,
|
|
73
|
+
signalStrength: snapshot.signalStrength,
|
|
74
|
+
cellularGeneration,
|
|
75
|
+
supportsIPv4: snapshot.supportsIPv4,
|
|
76
|
+
supportsIPv6: snapshot.supportsIPv6,
|
|
77
|
+
supportsDNS: snapshot.supportsDNS,
|
|
78
|
+
unsatisfiedReason,
|
|
79
|
+
timestamp: snapshot.timestamp,
|
|
80
|
+
};
|
|
81
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/** The network interface carrying the default route. */
|
|
2
|
+
export type Transport =
|
|
3
|
+
| 'wifi'
|
|
4
|
+
| 'cellular'
|
|
5
|
+
| 'ethernet'
|
|
6
|
+
| 'bluetooth'
|
|
7
|
+
| 'vpn'
|
|
8
|
+
| 'other'
|
|
9
|
+
| 'none'
|
|
10
|
+
| 'unknown';
|
|
11
|
+
|
|
12
|
+
/** A coarse cellular radio generation reported by the operating system. */
|
|
13
|
+
export type CellularGeneration = '2g' | '3g' | '4g' | '5g';
|
|
14
|
+
|
|
15
|
+
/** The derived connection-quality tier. */
|
|
16
|
+
export type NetworkQuality =
|
|
17
|
+
'unknown' | 'offline' | 'poor' | 'moderate' | 'good' | 'excellent';
|
|
18
|
+
|
|
19
|
+
/** The signal source that determined the current quality tier. */
|
|
20
|
+
export type QualitySource = 'probe' | 'os-estimate' | 'heuristic' | 'none';
|
|
21
|
+
|
|
22
|
+
/** The reason iOS reported an unsatisfied network path. */
|
|
23
|
+
export type UnsatisfiedReason =
|
|
24
|
+
| 'notAvailable'
|
|
25
|
+
| 'cellularDenied'
|
|
26
|
+
| 'wifiDenied'
|
|
27
|
+
| 'localNetworkDenied'
|
|
28
|
+
| 'vpnInactive'
|
|
29
|
+
| 'unknown';
|
|
30
|
+
|
|
31
|
+
/** Raw link signals reported by the operating system. `null` means unavailable. */
|
|
32
|
+
export interface NetworkSnapshot {
|
|
33
|
+
/** Whether the default network can carry internet traffic. */
|
|
34
|
+
isConnected: boolean;
|
|
35
|
+
/** Whether the operating system validated internet access. */
|
|
36
|
+
isValidated: boolean | null;
|
|
37
|
+
/** Whether the operating system detected a captive portal. */
|
|
38
|
+
isCaptivePortal: boolean | null;
|
|
39
|
+
/** The interface carrying the default route. */
|
|
40
|
+
transport: Transport;
|
|
41
|
+
/** Whether a VPN transport is present, when detectable. */
|
|
42
|
+
isVpn: boolean | null;
|
|
43
|
+
/** Whether use of the connection may incur monetary or data cost. */
|
|
44
|
+
isExpensive: boolean;
|
|
45
|
+
/** Whether the user enabled a reduced-data mode. */
|
|
46
|
+
isConstrained: boolean;
|
|
47
|
+
/** Whether the cellular connection is roaming, when detectable. */
|
|
48
|
+
isRoaming: boolean | null;
|
|
49
|
+
/** Operating-system downstream bandwidth estimate in kilobits per second. */
|
|
50
|
+
downlinkKbps: number | null;
|
|
51
|
+
/** Operating-system upstream bandwidth estimate in kilobits per second. */
|
|
52
|
+
uplinkKbps: number | null;
|
|
53
|
+
/** Bearer-dependent signal-strength value reported by the operating system. */
|
|
54
|
+
signalStrength: number | null;
|
|
55
|
+
/** Coarse cellular radio generation, when available. */
|
|
56
|
+
cellularGeneration: CellularGeneration | null;
|
|
57
|
+
/** Whether the current route supports IPv4. */
|
|
58
|
+
supportsIPv4: boolean | null;
|
|
59
|
+
/** Whether the current route supports IPv6. */
|
|
60
|
+
supportsIPv6: boolean | null;
|
|
61
|
+
/** Whether the current route has DNS support. */
|
|
62
|
+
supportsDNS: boolean | null;
|
|
63
|
+
/** Why an iOS path is unsatisfied, when available. */
|
|
64
|
+
unsatisfiedReason: UnsatisfiedReason | null;
|
|
65
|
+
/** Milliseconds since the Unix epoch. */
|
|
66
|
+
timestamp: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Measurements returned by an explicitly requested active network probe. */
|
|
70
|
+
export interface ProbeResult {
|
|
71
|
+
/** Median round-trip time to the latency endpoint in milliseconds. */
|
|
72
|
+
rttMs: number | null;
|
|
73
|
+
/** Measured downstream throughput in kilobits per second. */
|
|
74
|
+
downlinkKbps: number | null;
|
|
75
|
+
/** Number of response-body bytes read during the throughput phase. */
|
|
76
|
+
bytesReceived: number;
|
|
77
|
+
/** Total probe duration in milliseconds. */
|
|
78
|
+
durationMs: number;
|
|
79
|
+
/** Transport active when the probe was run. */
|
|
80
|
+
transport: Transport;
|
|
81
|
+
/** Non-fatal throughput-phase error, if one occurred. */
|
|
82
|
+
downloadError: string | null;
|
|
83
|
+
/** Milliseconds since the Unix epoch. */
|
|
84
|
+
timestamp: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Metadata captured when an active probe fails before producing a result. */
|
|
88
|
+
export interface ProbeFailure {
|
|
89
|
+
/** Stable library error code returned by the failed probe. */
|
|
90
|
+
code: NetworkQualityErrorCode;
|
|
91
|
+
/** Human-readable native or JavaScript error message. */
|
|
92
|
+
message: string;
|
|
93
|
+
/** Transport active when the failed probe was started. */
|
|
94
|
+
transport: Transport;
|
|
95
|
+
/** Failure time in Unix-epoch milliseconds. */
|
|
96
|
+
timestamp: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Optional manager-owned inputs used by the pure classifier. */
|
|
100
|
+
export interface NetworkQualityClassificationContext {
|
|
101
|
+
/** Most recent probe failure, when one is still relevant. */
|
|
102
|
+
lastProbeFailure?: ProbeFailure | null;
|
|
103
|
+
/** Time when connectivity or transport last changed. */
|
|
104
|
+
networkChangedAt?: number | null;
|
|
105
|
+
/** TTL used by the most recent successful probe override. */
|
|
106
|
+
probeResultTtlMs?: number;
|
|
107
|
+
/** TTL used by the most recent failed probe override. */
|
|
108
|
+
probeFailureTtlMs?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A raw snapshot enriched with a classified quality tier and probe metadata. */
|
|
112
|
+
export interface NetworkQualityState extends NetworkSnapshot {
|
|
113
|
+
/** The derived quality tier. */
|
|
114
|
+
quality: NetworkQuality;
|
|
115
|
+
/** The signal source used to derive `quality`. */
|
|
116
|
+
qualitySource: QualitySource;
|
|
117
|
+
/** Downstream value used by the classifier. */
|
|
118
|
+
effectiveDownlinkKbps: number | null;
|
|
119
|
+
/** Round-trip value used by the classifier. */
|
|
120
|
+
effectiveRttMs: number | null;
|
|
121
|
+
/** Most recent successful probe result. */
|
|
122
|
+
lastProbe: ProbeResult | null;
|
|
123
|
+
/** Most recent relevant failed probe. */
|
|
124
|
+
lastProbeFailure: ProbeFailure | null;
|
|
125
|
+
/** Human-readable classifier decisions, intended for diagnostics. */
|
|
126
|
+
reasons: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Boundaries for a single connection-quality tier. */
|
|
130
|
+
export interface TierThreshold {
|
|
131
|
+
/** Minimum downstream bandwidth in kilobits per second. */
|
|
132
|
+
minDownlinkKbps: number;
|
|
133
|
+
/** Maximum round-trip time in milliseconds. */
|
|
134
|
+
maxRttMs: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Configurable excellent, good, and moderate quality boundaries. */
|
|
138
|
+
export interface QualityThresholds {
|
|
139
|
+
/** Excellent-quality boundaries. */
|
|
140
|
+
excellent: TierThreshold;
|
|
141
|
+
/** Good-quality boundaries. */
|
|
142
|
+
good: TierThreshold;
|
|
143
|
+
/** Moderate-quality boundaries. */
|
|
144
|
+
moderate: TierThreshold;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Options controlling an active latency and throughput probe. */
|
|
148
|
+
export interface ProbeConfig {
|
|
149
|
+
/** HTTP(S) endpoint used for latency samples. */
|
|
150
|
+
latencyUrl: string;
|
|
151
|
+
/** HTTP(S) payload endpoint, or `null` to skip throughput measurement. */
|
|
152
|
+
downloadUrl: string | null;
|
|
153
|
+
/** Retained latency samples (1–100), excluding one warm-up request. */
|
|
154
|
+
latencySamples: number;
|
|
155
|
+
/** Positive whole-probe timeout in milliseconds, capped at 2,147,483,647. */
|
|
156
|
+
timeoutMs: number;
|
|
157
|
+
/** Maximum throughput-measurement time after the first response byte. */
|
|
158
|
+
downloadMaxDurationMs: number;
|
|
159
|
+
/** Maximum response-body bytes consumed by the throughput measurement. */
|
|
160
|
+
downloadMaxBytes: number;
|
|
161
|
+
/** Time for which a result may influence classification. */
|
|
162
|
+
resultTtlMs: number;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Options controlling background-safe automatic probes. */
|
|
166
|
+
export interface AutoProbeConfig {
|
|
167
|
+
/** Whether automatic probing is enabled. */
|
|
168
|
+
enabled: boolean;
|
|
169
|
+
/** Delay between automatic probes in milliseconds. */
|
|
170
|
+
intervalMs: number;
|
|
171
|
+
/** Whether a transport change schedules a probe. */
|
|
172
|
+
onTransportChange: boolean;
|
|
173
|
+
/** Whether automatic probes may run on expensive networks. */
|
|
174
|
+
allowOnExpensive: boolean;
|
|
175
|
+
/** Whether automatic probes may run on constrained networks. */
|
|
176
|
+
allowOnConstrained: boolean;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Complete runtime configuration for monitoring, classification, and probing. */
|
|
180
|
+
export interface NetworkQualityConfig {
|
|
181
|
+
/** Minimum interval between minor native signal updates. */
|
|
182
|
+
throttleMs: number;
|
|
183
|
+
/** Percentage change required for bandwidth or signal updates. */
|
|
184
|
+
bandwidthChangeThresholdPct: number;
|
|
185
|
+
/** Grace period before a newly connected, unvalidated network is poor. */
|
|
186
|
+
validationGraceMs: number;
|
|
187
|
+
/** Classifier boundaries. */
|
|
188
|
+
thresholds: QualityThresholds;
|
|
189
|
+
/** Active-probe defaults. */
|
|
190
|
+
probe: ProbeConfig;
|
|
191
|
+
/** Automatic-probe behavior. */
|
|
192
|
+
autoProbe: AutoProbeConfig;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Error codes surfaced by native and JavaScript operations. */
|
|
196
|
+
export type NetworkQualityErrorCode =
|
|
197
|
+
| 'E_UNSUPPORTED'
|
|
198
|
+
| 'E_OFFLINE'
|
|
199
|
+
| 'E_INVALID_URL'
|
|
200
|
+
| 'E_PROBE_TIMEOUT'
|
|
201
|
+
| 'E_PROBE_FAILED'
|
|
202
|
+
| 'E_PROBE_SKIPPED';
|
|
203
|
+
|
|
204
|
+
/** Recursively optional object fields accepted by `configure`. */
|
|
205
|
+
export type DeepPartial<T> = {
|
|
206
|
+
[Key in keyof T]?: T[Key] extends object ? DeepPartial<T[Key]> : T[Key];
|
|
207
|
+
};
|