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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_CONFIG, QUALITY_ORDER } from "./constants.js";
|
|
4
|
+
function formatValue(value) {
|
|
5
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(1);
|
|
6
|
+
}
|
|
7
|
+
function downlinkTier(value, thresholds) {
|
|
8
|
+
if (value >= thresholds.excellent.minDownlinkKbps) return 'excellent';
|
|
9
|
+
if (value >= thresholds.good.minDownlinkKbps) return 'good';
|
|
10
|
+
if (value >= thresholds.moderate.minDownlinkKbps) return 'moderate';
|
|
11
|
+
return 'poor';
|
|
12
|
+
}
|
|
13
|
+
function rttTier(value, thresholds) {
|
|
14
|
+
if (value <= thresholds.excellent.maxRttMs) return 'excellent';
|
|
15
|
+
if (value <= thresholds.good.maxRttMs) return 'good';
|
|
16
|
+
if (value <= thresholds.moderate.maxRttMs) return 'moderate';
|
|
17
|
+
return 'poor';
|
|
18
|
+
}
|
|
19
|
+
function worseQuality(first, second) {
|
|
20
|
+
const firstIndex = QUALITY_ORDER.indexOf(first);
|
|
21
|
+
const secondIndex = QUALITY_ORDER.indexOf(second);
|
|
22
|
+
return firstIndex <= secondIndex ? first : second;
|
|
23
|
+
}
|
|
24
|
+
function forcedClassification(quality, reason, qualitySource = 'none') {
|
|
25
|
+
return {
|
|
26
|
+
quality,
|
|
27
|
+
qualitySource,
|
|
28
|
+
effectiveDownlinkKbps: null,
|
|
29
|
+
effectiveRttMs: null,
|
|
30
|
+
reasons: [reason]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Classifies a snapshot using a fresh active probe, an OS estimate, or a
|
|
36
|
+
* cellular-generation heuristic. The function is pure and deterministic.
|
|
37
|
+
*/
|
|
38
|
+
export function classifyNetworkQuality(snapshot, probe, config = DEFAULT_CONFIG, now = Date.now(), context = {}) {
|
|
39
|
+
if (!snapshot.isConnected) {
|
|
40
|
+
return forcedClassification('offline', 'not-connected');
|
|
41
|
+
}
|
|
42
|
+
if (snapshot.isCaptivePortal === true) {
|
|
43
|
+
return forcedClassification('poor', 'captive-portal');
|
|
44
|
+
}
|
|
45
|
+
const failure = context.lastProbeFailure ?? null;
|
|
46
|
+
const failureTtlMs = context.probeFailureTtlMs ?? config.probe.resultTtlMs;
|
|
47
|
+
if (failure !== null && failure.transport === snapshot.transport && now - failure.timestamp <= failureTtlMs && (probe === null || failure.timestamp > probe.timestamp)) {
|
|
48
|
+
return forcedClassification('poor', `probe failed: ${failure.code}`, 'probe');
|
|
49
|
+
}
|
|
50
|
+
if (snapshot.isValidated === false) {
|
|
51
|
+
const networkChangedAt = context.networkChangedAt ?? null;
|
|
52
|
+
const validationGraceMs = config.validationGraceMs ?? DEFAULT_CONFIG.validationGraceMs;
|
|
53
|
+
if (networkChangedAt !== null && now - networkChangedAt < validationGraceMs) {
|
|
54
|
+
return forcedClassification('unknown', 'validating');
|
|
55
|
+
}
|
|
56
|
+
return forcedClassification('poor', 'not-validated');
|
|
57
|
+
}
|
|
58
|
+
const reasons = [];
|
|
59
|
+
let freshProbe = null;
|
|
60
|
+
if (probe !== null) {
|
|
61
|
+
if (probe.transport !== snapshot.transport) {
|
|
62
|
+
reasons.push('probe stale (transport changed)');
|
|
63
|
+
} else if (now - probe.timestamp > (context.probeResultTtlMs ?? config.probe.resultTtlMs)) {
|
|
64
|
+
reasons.push('probe stale (expired)');
|
|
65
|
+
} else {
|
|
66
|
+
freshProbe = probe;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const usedProbeDownlink = freshProbe?.downlinkKbps != null;
|
|
70
|
+
const usedProbeRtt = freshProbe?.rttMs != null;
|
|
71
|
+
const usedOsDownlink = !usedProbeDownlink && snapshot.downlinkKbps !== null;
|
|
72
|
+
const effectiveDownlinkKbps = freshProbe?.downlinkKbps ?? snapshot.downlinkKbps ?? null;
|
|
73
|
+
const effectiveRttMs = freshProbe?.rttMs ?? null;
|
|
74
|
+
let qualitySource = 'none';
|
|
75
|
+
if (usedProbeDownlink || usedProbeRtt) {
|
|
76
|
+
qualitySource = 'probe';
|
|
77
|
+
} else if (usedOsDownlink) {
|
|
78
|
+
qualitySource = 'os-estimate';
|
|
79
|
+
}
|
|
80
|
+
let quality = null;
|
|
81
|
+
if (effectiveDownlinkKbps !== null) {
|
|
82
|
+
const tier = downlinkTier(effectiveDownlinkKbps, config.thresholds);
|
|
83
|
+
reasons.push(`downlink ${formatValue(effectiveDownlinkKbps)} kbps → ${tier}`);
|
|
84
|
+
quality = tier;
|
|
85
|
+
}
|
|
86
|
+
if (effectiveRttMs !== null) {
|
|
87
|
+
const tier = rttTier(effectiveRttMs, config.thresholds);
|
|
88
|
+
reasons.push(`rtt ${formatValue(effectiveRttMs)} ms → ${tier}`);
|
|
89
|
+
quality = quality === null ? tier : worseQuality(quality, tier);
|
|
90
|
+
}
|
|
91
|
+
if (quality !== null) {
|
|
92
|
+
return {
|
|
93
|
+
quality,
|
|
94
|
+
qualitySource,
|
|
95
|
+
effectiveDownlinkKbps,
|
|
96
|
+
effectiveRttMs,
|
|
97
|
+
reasons
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const heuristicQuality = snapshot.cellularGeneration === '5g' || snapshot.cellularGeneration === '4g' ? 'good' : snapshot.cellularGeneration === '3g' ? 'moderate' : snapshot.cellularGeneration === '2g' ? 'poor' : null;
|
|
101
|
+
if (heuristicQuality !== null) {
|
|
102
|
+
reasons.push(`cellular generation ${snapshot.cellularGeneration} → ${heuristicQuality}`);
|
|
103
|
+
return {
|
|
104
|
+
quality: heuristicQuality,
|
|
105
|
+
qualitySource: 'heuristic',
|
|
106
|
+
effectiveDownlinkKbps: null,
|
|
107
|
+
effectiveRttMs: null,
|
|
108
|
+
reasons
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
reasons.push('no quality metrics available');
|
|
112
|
+
return {
|
|
113
|
+
quality: 'unknown',
|
|
114
|
+
qualitySource: 'none',
|
|
115
|
+
effectiveDownlinkKbps: null,
|
|
116
|
+
effectiveRttMs: null,
|
|
117
|
+
reasons
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Returns whether a ranked quality tier meets or exceeds a minimum tier. */
|
|
122
|
+
export function isQualityAtLeast(quality, minimum) {
|
|
123
|
+
if (quality === 'unknown' || minimum === 'unknown') return false;
|
|
124
|
+
return QUALITY_ORDER.indexOf(quality) >= QUALITY_ORDER.indexOf(minimum);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["DEFAULT_CONFIG","QUALITY_ORDER","formatValue","value","Number","isInteger","String","toFixed","downlinkTier","thresholds","excellent","minDownlinkKbps","good","moderate","rttTier","maxRttMs","worseQuality","first","second","firstIndex","indexOf","secondIndex","forcedClassification","quality","reason","qualitySource","effectiveDownlinkKbps","effectiveRttMs","reasons","classifyNetworkQuality","snapshot","probe","config","now","Date","context","isConnected","isCaptivePortal","failure","lastProbeFailure","failureTtlMs","probeFailureTtlMs","resultTtlMs","transport","timestamp","code","isValidated","networkChangedAt","validationGraceMs","freshProbe","push","probeResultTtlMs","usedProbeDownlink","downlinkKbps","usedProbeRtt","rttMs","usedOsDownlink","tier","heuristicQuality","cellularGeneration","isQualityAtLeast","minimum"],"sourceRoot":"..\\..\\src","sources":["classify.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** Default monitoring, classifier, and probe configuration. */
|
|
4
|
+
export const DEFAULT_CONFIG = {
|
|
5
|
+
throttleMs: 1_000,
|
|
6
|
+
bandwidthChangeThresholdPct: 10,
|
|
7
|
+
validationGraceMs: 10_000,
|
|
8
|
+
thresholds: {
|
|
9
|
+
excellent: {
|
|
10
|
+
minDownlinkKbps: 20_000,
|
|
11
|
+
maxRttMs: 50
|
|
12
|
+
},
|
|
13
|
+
good: {
|
|
14
|
+
minDownlinkKbps: 5_000,
|
|
15
|
+
maxRttMs: 150
|
|
16
|
+
},
|
|
17
|
+
moderate: {
|
|
18
|
+
minDownlinkKbps: 1_000,
|
|
19
|
+
maxRttMs: 400
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
probe: {
|
|
23
|
+
latencyUrl: 'https://www.gstatic.com/generate_204',
|
|
24
|
+
downloadUrl: 'https://speed.cloudflare.com/__down?bytes=3000000',
|
|
25
|
+
latencySamples: 3,
|
|
26
|
+
timeoutMs: 8_000,
|
|
27
|
+
downloadMaxDurationMs: 3_000,
|
|
28
|
+
downloadMaxBytes: 3_000_000,
|
|
29
|
+
resultTtlMs: 60_000
|
|
30
|
+
},
|
|
31
|
+
autoProbe: {
|
|
32
|
+
enabled: false,
|
|
33
|
+
intervalMs: 60_000,
|
|
34
|
+
onTransportChange: true,
|
|
35
|
+
allowOnExpensive: false,
|
|
36
|
+
allowOnConstrained: false
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Ranked quality tiers from worst to best. `unknown` is intentionally unranked. */
|
|
41
|
+
export const QUALITY_ORDER = ['offline', 'poor', 'moderate', 'good', 'excellent'];
|
|
42
|
+
|
|
43
|
+
/** Error codes that can be returned by the package. */
|
|
44
|
+
export const ERROR_CODES = {
|
|
45
|
+
unsupported: 'E_UNSUPPORTED',
|
|
46
|
+
offline: 'E_OFFLINE',
|
|
47
|
+
invalidUrl: 'E_INVALID_URL',
|
|
48
|
+
probeTimeout: 'E_PROBE_TIMEOUT',
|
|
49
|
+
probeFailed: 'E_PROBE_FAILED',
|
|
50
|
+
probeSkipped: 'E_PROBE_SKIPPED'
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["DEFAULT_CONFIG","throttleMs","bandwidthChangeThresholdPct","validationGraceMs","thresholds","excellent","minDownlinkKbps","maxRttMs","good","moderate","probe","latencyUrl","downloadUrl","latencySamples","timeoutMs","downloadMaxDurationMs","downloadMaxBytes","resultTtlMs","autoProbe","enabled","intervalMs","onTransportChange","allowOnExpensive","allowOnConstrained","QUALITY_ORDER","ERROR_CODES","unsupported","offline","invalidUrl","probeTimeout","probeFailed","probeSkipped"],"sourceRoot":"..\\..\\src","sources":["constants.ts"],"mappings":";;AAEA;AACA,OAAO,MAAMA,cAAoC,GAAG;EAClDC,UAAU,EAAE,KAAK;EACjBC,2BAA2B,EAAE,EAAE;EAC/BC,iBAAiB,EAAE,MAAM;EACzBC,UAAU,EAAE;IACVC,SAAS,EAAE;MAAEC,eAAe,EAAE,MAAM;MAAEC,QAAQ,EAAE;IAAG,CAAC;IACpDC,IAAI,EAAE;MAAEF,eAAe,EAAE,KAAK;MAAEC,QAAQ,EAAE;IAAI,CAAC;IAC/CE,QAAQ,EAAE;MAAEH,eAAe,EAAE,KAAK;MAAEC,QAAQ,EAAE;IAAI;EACpD,CAAC;EACDG,KAAK,EAAE;IACLC,UAAU,EAAE,sCAAsC;IAClDC,WAAW,EAAE,mDAAmD;IAChEC,cAAc,EAAE,CAAC;IACjBC,SAAS,EAAE,KAAK;IAChBC,qBAAqB,EAAE,KAAK;IAC5BC,gBAAgB,EAAE,SAAS;IAC3BC,WAAW,EAAE;EACf,CAAC;EACDC,SAAS,EAAE;IACTC,OAAO,EAAE,KAAK;IACdC,UAAU,EAAE,MAAM;IAClBC,iBAAiB,EAAE,IAAI;IACvBC,gBAAgB,EAAE,KAAK;IACvBC,kBAAkB,EAAE;EACtB;AACF,CAAC;;AAED;AACA,OAAO,MAAMC,aAA+B,GAAG,CAC7C,SAAS,EACT,MAAM,EACN,UAAU,EACV,MAAM,EACN,WAAW,CACZ;;AAED;AACA,OAAO,MAAMC,WAAW,GAAG;EACzBC,WAAW,EAAE,eAAe;EAC5BC,OAAO,EAAE,WAAW;EACpBC,UAAU,EAAE,eAAe;EAC3BC,YAAY,EAAE,iBAAiB;EAC/BC,WAAW,EAAE,gBAAgB;EAC7BC,YAAY,EAAE;AAChB,CAAU","ignoreList":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** Error raised by monitoring and probing operations. */
|
|
4
|
+
export class NetworkQualityError extends Error {
|
|
5
|
+
/** Stable machine-readable error code. */
|
|
6
|
+
|
|
7
|
+
/** Original platform error, when one is available. */
|
|
8
|
+
|
|
9
|
+
/** Creates a network-quality error with a stable code and helpful message. */
|
|
10
|
+
constructor(code, message, options) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'NetworkQualityError';
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.cause = options?.cause;
|
|
15
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NetworkQualityError","Error","constructor","code","message","options","name","cause","Object","setPrototypeOf","new","target","prototype"],"sourceRoot":"..\\..\\src","sources":["errors.ts"],"mappings":";;AAEA;AACA,OAAO,MAAMA,mBAAmB,SAASC,KAAK,CAAC;EAC7C;;EAGA;;EAGA;EACOC,WAAWA,CAChBC,IAA6B,EAC7BC,OAAe,EACfC,OAA6B,EAC7B;IACA,KAAK,CAACD,OAAO,CAAC;IACd,IAAI,CAACE,IAAI,GAAG,qBAAqB;IACjC,IAAI,CAACH,IAAI,GAAGA,IAAI;IAChB,IAAI,CAACI,KAAK,GAAGF,OAAO,EAAEE,KAAK;IAC3BC,MAAM,CAACC,cAAc,CAAC,IAAI,EAAEC,GAAG,CAACC,MAAM,CAACC,SAAS,CAAC;EACnD;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
4
|
+
import { NetworkQualityError } from "./errors.js";
|
|
5
|
+
import { addNetworkQualityListener, assertNetworkQualitySupported, getCachedNetworkQualityState, probeNetwork } from "./manager.js";
|
|
6
|
+
function subscribe(onStoreChange) {
|
|
7
|
+
const subscription = addNetworkQualityListener(() => {
|
|
8
|
+
onStoreChange();
|
|
9
|
+
});
|
|
10
|
+
return () => subscription.remove();
|
|
11
|
+
}
|
|
12
|
+
function getServerSnapshot() {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Subscribes a React component to the referentially stable live state. */
|
|
17
|
+
export function useNetworkQuality() {
|
|
18
|
+
const state = useSyncExternalStore(subscribe, getCachedNetworkQualityState, getServerSnapshot);
|
|
19
|
+
assertNetworkQualitySupported();
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** State and trigger returned by `useNetworkProbe`. */
|
|
24
|
+
|
|
25
|
+
function asNetworkQualityError(error) {
|
|
26
|
+
return error instanceof NetworkQualityError ? error : new NetworkQualityError('E_PROBE_FAILED', error instanceof Error ? error.message : 'The network probe failed', {
|
|
27
|
+
cause: error
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Provides imperative probing with React-friendly loading and error state. */
|
|
32
|
+
export function useNetworkProbe() {
|
|
33
|
+
const mounted = useRef(true);
|
|
34
|
+
const requestId = useRef(0);
|
|
35
|
+
const [isProbing, setIsProbing] = useState(false);
|
|
36
|
+
const [result, setResult] = useState(null);
|
|
37
|
+
const [error, setError] = useState(null);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
mounted.current = true;
|
|
40
|
+
return () => {
|
|
41
|
+
mounted.current = false;
|
|
42
|
+
requestId.current += 1;
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
const probe = useCallback(async options => {
|
|
46
|
+
const currentRequest = requestId.current + 1;
|
|
47
|
+
requestId.current = currentRequest;
|
|
48
|
+
if (mounted.current) {
|
|
49
|
+
setIsProbing(true);
|
|
50
|
+
setError(null);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const nextResult = await probeNetwork(options);
|
|
54
|
+
if (mounted.current && requestId.current === currentRequest) {
|
|
55
|
+
setResult(nextResult);
|
|
56
|
+
}
|
|
57
|
+
return nextResult;
|
|
58
|
+
} catch (caught) {
|
|
59
|
+
const nextError = asNetworkQualityError(caught);
|
|
60
|
+
if (mounted.current && requestId.current === currentRequest) {
|
|
61
|
+
setError(nextError);
|
|
62
|
+
}
|
|
63
|
+
throw nextError;
|
|
64
|
+
} finally {
|
|
65
|
+
if (mounted.current && requestId.current === currentRequest) {
|
|
66
|
+
setIsProbing(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}, []);
|
|
70
|
+
assertNetworkQualitySupported();
|
|
71
|
+
return {
|
|
72
|
+
probe,
|
|
73
|
+
isProbing,
|
|
74
|
+
result,
|
|
75
|
+
error
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useRef","useState","useSyncExternalStore","NetworkQualityError","addNetworkQualityListener","assertNetworkQualitySupported","getCachedNetworkQualityState","probeNetwork","subscribe","onStoreChange","subscription","remove","getServerSnapshot","useNetworkQuality","state","asNetworkQualityError","error","Error","message","cause","useNetworkProbe","mounted","requestId","isProbing","setIsProbing","result","setResult","setError","current","probe","options","currentRequest","nextResult","caught","nextError"],"sourceRoot":"..\\..\\src","sources":["hooks.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** Purely classifies a network snapshot and optional active-probe result. */
|
|
4
|
+
export { classifyNetworkQuality, isQualityAtLeast } from "./classify.js";
|
|
5
|
+
|
|
6
|
+
/** Default configuration and ranked quality tiers. */
|
|
7
|
+
export { DEFAULT_CONFIG, QUALITY_ORDER } from "./constants.js";
|
|
8
|
+
|
|
9
|
+
/** Error type used by all package operations. */
|
|
10
|
+
export { NetworkQualityError } from "./errors.js";
|
|
11
|
+
|
|
12
|
+
/** React hooks for live state and on-demand active probes. */
|
|
13
|
+
export { useNetworkProbe, useNetworkQuality } from "./hooks.js";
|
|
14
|
+
|
|
15
|
+
/** Runtime configuration, snapshots, listeners, and active probing. */
|
|
16
|
+
export { addNetworkQualityListener, configure, getConfig, getLastProbeResult, getNetworkQuality, isSupported, probeNetwork } from "./manager.js";
|
|
17
|
+
|
|
18
|
+
/** Public network-quality types. */
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["classifyNetworkQuality","isQualityAtLeast","DEFAULT_CONFIG","QUALITY_ORDER","NetworkQualityError","useNetworkProbe","useNetworkQuality","addNetworkQualityListener","configure","getConfig","getLastProbeResult","getNetworkQuality","isSupported","probeNetwork"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA;AACA,SAASA,sBAAsB,EAAEC,gBAAgB,QAAQ,eAAY;;AAErE;AACA,SAASC,cAAc,EAAEC,aAAa,QAAQ,gBAAa;;AAE3D;AACA,SAASC,mBAAmB,QAAQ,aAAU;;AAE9C;AACA,SAASC,eAAe,EAAEC,iBAAiB,QAAQ,YAAS;;AAE5D;AACA,SACEC,yBAAyB,EACzBC,SAAS,EACTC,SAAS,EACTC,kBAAkB,EAClBC,iBAAiB,EACjBC,WAAW,EACXC,YAAY,QACP,cAAW;;AAElB","ignoreList":[]}
|