npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

capacitor-network-metrics-sdk

v1.0.42

Published

Capacitor plugin for network metrics measurement (Android + iOS)

Readme

capacitor-network-metrics-sdk

Capacitor plugin for comprehensive network quality measurement on Android and iOS. Thin bridge over:

Install

npm install capacitor-network-metrics-sdk
npx cap sync

Android setup

android/build.gradle — add JitPack

repositories {
    maven { url 'https://jitpack.io' }
}

android/app/src/main/AndroidManifest.xml — permissions

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Background work -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Register plugin in MainActivity.java

import com.kevindupas.networkmetricssdk.NetworkMetricsSdkPlugin;

public class MainActivity extends BridgeActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        registerPlugin(NetworkMetricsSdkPlugin.class);
        super.onCreate(savedInstanceState);
    }
}

iOS setup

Info.plist — required keys

<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to tag network measurements with GPS coordinates.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Used to tag background network measurements with GPS coordinates.</string>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>com.networkmetrics.refresh</string>
</array>
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
</array>

Usage

import { NetworkMetricsSdk } from 'capacitor-network-metrics-sdk';

// Initialize once (e.g. in app startup)
await NetworkMetricsSdk.initialize({
  backendUrl:          'https://your-backend.com/api/measurements',
  authHeader:          'Bearer YOUR_TOKEN',
  intervalMinutes:     15,
  enableSpeed:         true,
  enablePacketLoss:    true,
  enableStreaming:     true,
  enableSocialLatency: true,
  enableDns:           true,
  enableWebBrowsing:   true,
  udpHost:             'your-udp-server.com',
  udpPort:             5005,
  tcpPort:             5006,
  remoteConfigUrl:     'https://your-backend.com/api/config/targets', // optional
});

// Trigger a measurement immediately
await NetworkMetricsSdk.measureNow();

// Read last stored result
const { json, timestamp } = await NetworkMetricsSdk.getLastResult();
if (json) {
  const record = JSON.parse(json);
  console.log('Download:', record.speed?.downloadMbps, 'Mbps');
  console.log('MOS:', record.mos);
}

API

initialize(options)

Starts the SDK and schedules periodic background measurements.

| Option | Type | Default | Description | |---|---|---|---| | backendUrl | string | required | POST endpoint for measurements | | authHeader | string | — | Authorization header | | intervalMinutes | number | 15 | Background interval | | enableSpeed | boolean | true | Speed test | | enablePacketLoss | boolean | true | Packet loss | | enableStreaming | boolean | true | HLS simulation | | enableSocialLatency | boolean | true | Social platform TTFB | | enableDns | boolean | true | DNS timing | | enableWebBrowsing | boolean | true | Web phase timing | | udpHost | string | "" | UDP echo server | | udpPort | number | 5005 | UDP port | | tcpPort | number | 5006 | TCP fallback port | | remoteConfigUrl | string | — | Remote web targets URL (1h cache) |

measureNow()

Triggers an immediate one-shot measurement cycle. Returns as soon as the measurement is queued (non-blocking).

getLastResult()

Returns the last stored measurement.

{ json: string | null, timestamp: number }
// timestamp = ms since epoch, 0 if no result yet

getGnssSatellites() (Android only)

Passive snapshot of GNSS satellites currently visible (GPS, GLONASS, Galileo, BeiDou, QZSS, SBAS, IRNSS). Registers a GnssStatus.Callback on first call and reuses it.

interface GnssSatellite {
  svid: number;
  constellation: 'GPS' | 'GLO' | 'GAL' | 'BDS' | 'QZS' | 'SBAS' | 'IRN' | 'OTH';
  azimuth: number;     // degrees, 0 = north, clockwise
  elevation: number;   // degrees above horizon
  cn0DbHz: number;     // carrier-to-noise density
  usedInFix: boolean;
}
interface GnssSatellitesSnapshot {
  satellites: GnssSatellite[];
  inView: number;
  usedInFix: number;
  avgCn0DbHz: number;
}

Requires ACCESS_FINE_LOCATION at runtime and location services enabled. Returns empty when permission missing.

getNeighborCells() (Android only)

Snapshot of all visible cells (registered + neighbors) per TelephonyManager.getAllCellInfo().

interface NeighborCell {
  technology: 'LTE' | 'NR' | 'WCDMA' | 'GSM';
  isRegistered: boolean;
  mcc: string | null;
  mnc: string | null;
  tac: number | null;       // LTE/NR: tac; WCDMA/GSM: lac
  ci: number | null;        // LTE/WCDMA/GSM cell id
  nci: number | null;       // NR 5G cell id (64-bit)
  pci: number | null;       // physical cell id (LTE/NR)
  psc: number | null;       // WCDMA primary scrambling code
  arfcn: number | null;     // EARFCN/NRARFCN/UARFCN/GSM-ARFCN
  rsrp: number | null;
  rsrq: number | null;
  sinr: number | null;
  asuLevel: number | null;
  dbm: number | null;
  timestampNanos: number;
}
interface NeighborCellsSnapshot {
  cells: NeighborCell[];
  count: number;
}

Requires ACCESS_FINE_LOCATION + READ_PHONE_STATE. Returns { cells: [], count: 0 } if missing.

Payload reference

Full payload documented in android-network-metrics-sdk/PAYLOAD_REFERENCE.md.

Key fields:

{
  "testId":    "uuid",
  "deviceId":  "platform-device-id",
  "timestamp": "2026-04-19T10:00:00Z",
  "speed":     { "downloadMbps": 12.4, "uploadMbps": 5.1, "latencyMs": 28, "jitterMs": 3.2 },
  "udpPacketLoss": { "lossPercent": 1.0, "method": "UDP" },
  "radio":     { "rsrp": -85, "networkGeneration": "4G", "nrMode": null },
  "network":   { "isp": "Safaricom", "cfColo": "NBO", "isLocallyServed": true },
  "device":    { "model": "Pixel 8", "thermalStatus": "NONE" },
  "scores":    { "streaming": { "score": 4, "label": "Good" } },
  "mos":       4.2
}

Platform feature matrix

| Feature | Android | iOS | |---|:---:|:---:| | Speed DL/UL/Latency/Jitter | ✅ | ✅ | | Packet loss UDP/TCP | ✅ | ✅ | | HLS streaming | ✅ | ✅ FG / ⚠️ BG | | Social latency | ✅ | ✅ | | DNS timing | ✅ | ✅ | | Web browsing phases | ✅ | ✅ | | GPS location | ✅ | ✅ | | ISP / ASN / CF PoP | ✅ | ✅ | | Device info / battery / thermal | ✅ | ✅ | | RAM usage | ✅ | ✅ | | CPU load % | ✅ | ❌ | | MCC / MNC / Operator | ✅ | ❌ deprecated | | RSRP / RSRQ / Cell ID | ✅ | ❌ private API | | Neighbouring cells | ✅ | ❌ private API | | 5G NSA/SA | ✅ | ❌ no public API | | VoLTE / VoNR | ✅ | ❌ no public API | | Background guaranteed | ✅ WorkManager | ⚠️ BGAppRefresh | | MOS G.107 + QoS scores | ✅ | ✅ |

Changelog

v1.0.40

  • Feat: LTE Timing Advance — radio.timingAdvance (raw units, null when unavailable; distance ≈ TA × 78.07 m, conversion left to consumers)
  • Feat: dual-SIM — new radioPerSim[] array on getRadioSnapshot() and on the cycle payload, one entry per active subscription with subscriptionId, slotIndex, carrierName, and full radio block. Legacy top-level radio (default data SIM) is preserved unchanged
  • Feat: Android SDK v1.0.20 — RadioMeasurement.measurePerSim(), TA read on CellSignalStrengthLte

v1.0.28

  • Perf: Android SDK v1.0.17 — Cloudflare/Ookla-grade methodology. HTTP/2 + ConnectionPool keep-alive (drops ping handshake overhead on cellular), 2 s TCP slow-start warmup excluded from throughput, outlier-trimmed latency + stddev jitter, 25 MB download / 1 MB upload chunks. Loaded latency now probed concurrently during the download stream (true bufferbloat measurement) instead of on an idle post-transfer connection

v1.0.27

  • Feat: SPEED phase payload — measurementProgress SPEED event now carries downloadMbps, uploadMbps, latencyMs, jitterMs, loadedLatencyMs, serverName, serverLocation so consumers can display ping/jitter/server as soon as the speed test finishes, without waiting for the full cycle

v1.0.26

  • Feat: live speed progress events — SPEED_DOWNLOAD_PROGRESS / SPEED_UPLOAD_PROGRESS phases on measurementProgress, each carrying mbps (instantaneous throughput sampled every 400 ms during the speed phase)
  • Feat: Android SDK v1.0.15 — Worker wires onDownloadProgress / onUploadProgress callbacks into SpeedMeasurement

v1.0.25

  • Feat: measurementProgress event — emitted after each phase (SPEED, PACKET_LOSS, STREAMING, SOCIAL_LATENCY, DNS, WEB_BROWSING, RADIO, NETWORK, DEVICE, GEO, COMPLETE)
  • Feat: streamingUrl param in initialize() — custom HLS URL for streaming measurement
  • Feat: Android SDK v1.0.14 — socialTargets + streamingUrl in remote config
  • Feat: notifyListeners("measurementProgress", { phase }) on Android

v1.0.24

  • Fix: bump ios-network-metrics-sdk to v1.0.19 — filter CTCarrier placeholder "--" on iOS 16+

v1.0.23

  • Feat: bump ios-network-metrics-sdk to v1.0.18 — MCC/MNC/operator (iOS 14–16.3), loaded latency under download

v1.0.22

  • Fix: bump ios-network-metrics-sdk to v1.0.17 — parallel chunk download loop, no delegate, no race

v1.0.21

  • Fix: bump ios-network-metrics-sdk to v1.0.16 — download speed: URLSessionDataDelegate.didReceive for in-memory chunk accumulation (disk write flush delay was causing 0 Mbps)

v1.0.20

  • Fix: bump ios-network-metrics-sdk to v1.0.15 — download speed: URLSessionDownloadTask + didWriteData delegate (real chunk counts, no byte loop)

v1.0.19

  • Fix: bump ios-network-metrics-sdk to v1.0.14 — download speed: URLSessionDataDelegate chunk counting

v1.0.18

  • Fix: bump ios-network-metrics-sdk to v1.0.13 — minimum iOS target bumped to 15.0

v1.0.17

  • Fix: bump ios-network-metrics-sdk to v1.0.12 — download speed ~0 Mbps fixed: use URLSession.bytes(from:) streaming.

v1.0.16

  • Fix: bump ios-network-metrics-sdk to v1.0.11 — download speed concurrent threads via Task.detached.

v1.0.15

  • Fix: bump ios-network-metrics-sdk to v1.0.10 — remove all withTaskGroup in measurements + os_log debug at each step.

v1.0.14

  • Fix: bump ios-network-metrics-sdk to v1.0.9 — CLLocationManager main thread crash + DispatchSemaphore blocking Swift cooperative thread pool.

v1.0.13

  • Fix: bump ios-network-metrics-sdk to v1.0.8 — Swift runtime async let heap corruption (swift#75501). Sequential await + Task.detached.

v1.0.12

  • Fix: bump ios-network-metrics-sdk to v1.0.7 — DispatchQueue.main.sync deadlock on main thread replaced with MainActor.run.

v1.0.11

  • Fix: bump ios-network-metrics-sdk to v1.0.6 (CI workflow fix + all UIDevice main thread fixes)

v1.0.10

  • Fix: bump ios-network-metrics-sdk to v1.0.3 (SIGABRT crash fix — UIDevice.identifierForVendor moved to MainActor.run in runCycle)

v1.0.9

  • Fix: bump ios-network-metrics-sdk to v1.0.2 (SIGABRT crash fix on measureNow() — UIDevice battery access on main thread)

v1.0.8

  • Fix: bump ios-network-metrics-sdk to v1.0.1 (BGTaskScheduler crash fix)
  • iOS: AppDelegate must call NetworkMetricsSdk.shared.registerForBackgroundTask() in didFinishLaunching — see iOS Setup in README

v1.0.7

  • Feat: add speedDownloadDurationMs, speedUploadDurationMs, speedThreadCount to initialize() API
  • Fix: pass all speed tuning params through to Android Builder and iOS Config

v1.0.6

  • Fix: Android intervalMinutesintervalMs conversion (Builder API mismatch)
  • Fix: Android initialize()init() + start() (correct SDK API)

v1.0.5

  • Fix: Android SDK dependency bumped to v1.0.13

v1.0.4

  • Fix: Android SDK dependency bumped to v1.0.13 (fixes VoLTE/VoNR reflection compilation)

v1.0.3

  • Fix: add root Package.swift for Capacitor SPM auto-detection on iOS

v1.0.2

  • Fix: tsconfig ES2022 + moduleResolution bundler (fixes dynamic import error in CI)
  • Build script: remove docgen dependency
  • Add CI + release GitHub Actions workflows (npm publish on tag)

v1.0.1

  • Fix: TypeScript build configuration

v1.0.0

  • Initial release — Android + iOS
  • Full API: initialize, measureNow, getLastResult
  • Android: delegates to android-network-metrics-sdk v1.0.12
  • iOS: delegates to ios-network-metrics-sdk v1.0.0