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

@bglocation/capacitor

v1.1.1

Published

Capacitor plugin for continuous background location tracking on iOS and Android

Readme

@bglocation/capacitor

Capacitor 8 plugin for continuous background location tracking on iOS and Android with optional native HTTP POST.

Status: POC (Proof of Concept)

Features

  • Continuous GPS tracking in background (iOS + Android)
  • Configurable distanceFilter, desiredAccuracy, update intervals
  • Adaptive distance filterdistanceFilter: 'auto' dynamically adjusts based on speed (EMA smoothing) to maintain ~10s update interval
  • Heartbeat timer — periodic event even when stationary
  • Native HTTP POST — send location to backend from native layer
  • Offline buffer — SQLite-backed queue for failed HTTP requests with automatic retry
  • Configurable notification — custom title and text for Android foreground service notification (i18n ready)
  • Debug mode — verbose logs, onDebug events, optional system sounds, dynamic Android notification with GPS counters
  • OEM battery killer detection — warns when battery optimization may kill tracking (Android), with dontkillmyapp.com links
  • Approximate location warning — detects and warns when user granted only approximate location (iOS 14+)
  • Significant Location Change fallback — SLC watchdog auto-restarts GPS after iOS kills the app (iOS)
  • Mock location detection — detects mock/test location providers (Android)
  • Permission rationale — emits event before requesting background location on Android 11+ for custom rationale UI
  • Geofencing — monitor enter/exit/dwell for up to 20 circular regions (iOS + Android), with persistence across app restarts
  • Events: onLocation, onHeartbeat, onHttp, onProviderChange, onDebug, onBatteryWarning, onAccuracyWarning, onMockLocation, onPermissionRationale, onTrialExpired, onGeofence
  • Web fallback for development/testing in browser
  • 774 unit tests across 3 platforms

Documentation

Detailed documentation is in the .wiki/ directory:

| Document | Description | |----------|-------------| | Architecture | System architecture, data flow, platform differences | | API Reference | Full API: methods, events, interfaces, error codes | | iOS | CLLocationManager, permissions, heartbeat, background modes | | Android | Foreground Service, FusedLocation, permissions, binding | | Web | Browser fallback implementation | | HTTP | Native HTTP sender: config, format, timeouts, errors | | Testing | Test strategy, running tests, CI integration | | Geofencing | Geofence API, dwell, persistence, platform differences | | Deployment | Production checklist: license key, platform requirements, troubleshooting | | Known Limitations | POC limitations and production roadmap |

Platforms

| Platform | Min Version | Implementation | |----------|-------------|----------------| | iOS | 15.0 | CLLocationManager with background modes | | Android | API 26 | FusedLocationProviderClient + Foreground Service | | Web | — | navigator.geolocation (dev/testing fallback) |

Installation

npm install @bglocation/capacitor
npx cap sync

iOS

Add all of the following keys to Info.plist:

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location to track the tour route</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show your position on the map</string>
<key>UIBackgroundModes</key>
<array>
  <string>location</string>
</array>

Important: Both NSLocationAlwaysAndWhenInUseUsageDescription and NSLocationWhenInUseUsageDescription are required. Missing either will cause a runtime crash with a privacy violation. The plugin calls requestAlwaysAuthorization() automatically when tracking starts if the user hasn't been prompted yet.

allowsBackgroundLocationUpdates = true is set internally by the plugin during configure(), before the app enters background — this is required by iOS.

Android

Permissions are declared in the plugin's AndroidManifest.xml and merged automatically:

  • ACCESS_FINE_LOCATION
  • ACCESS_COARSE_LOCATION
  • ACCESS_BACKGROUND_LOCATION (Android 10+)
  • FOREGROUND_SERVICE / FOREGROUND_SERVICE_LOCATION

Your app must request permissions before calling start(). The plugin only checks permissions — it does not prompt the user. On Android 10+ (API 29), the user must explicitly grant "Allow all the time" for background location to work. If permissions are not granted, start() will reject with an error.

iOS vs Android difference: iOS auto-prompts for Always authorization via requestAlwaysAuthorization(). Android requires the app to handle permission requests explicitly.

Quick Start

import { BackgroundLocation } from '@bglocation/capacitor';

// 1. Request permissions
const perms = await BackgroundLocation.checkPermissions();
if (perms.location !== 'granted') {
  await BackgroundLocation.requestPermissions({ permissions: ['location'] });
}
if (perms.backgroundLocation !== 'granted') {
  await BackgroundLocation.requestPermissions({ permissions: ['backgroundLocation'] });
}

// 2. Configure
await BackgroundLocation.configure({
  distanceFilter: 15,                  // meters
  desiredAccuracy: 'high',             // 'high' | 'balanced' | 'low'
  locationUpdateInterval: 5000,        // ms (Android only)
  fastestLocationUpdateInterval: 2000, // ms (Android only)
  heartbeatInterval: 15,               // seconds
  http: {                              // optional — native HTTP POST
    url: 'https://api.example.com/location',
    headers: { Authorization: 'Bearer <token>' },
    buffer: { maxSize: 1000 },         // optional — offline buffer for failed requests
  },
  notification: {                      // optional — Android foreground service notification
    title: 'Tour Active',
    text: 'Tracking your position',
  },
  debug: true,                         // optional — verbose logging + onDebug events
  debugSounds: true,                   // optional — system sounds on events (requires debug: true)
});

// 3. Listen for events
BackgroundLocation.addListener('onLocation', (location) => {
  console.log('Location:', location.latitude, location.longitude);
});

BackgroundLocation.addListener('onHeartbeat', (event) => {
  console.log('Heartbeat:', event.location);
});

BackgroundLocation.addListener('onHttp', (event) => {
  console.log('HTTP:', event.success, event.statusCode, 'buffered:', event.bufferedCount);
});

BackgroundLocation.addListener('onDebug', (event) => {
  console.log('Debug:', event.message);
});

// 4. Start tracking
await BackgroundLocation.start();

// ... later ...

// 5. Stop tracking
await BackgroundLocation.stop();
await BackgroundLocation.removeAllListeners();

API Overview

Methods

| Method | Returns | Description | |--------|---------|-------------| | checkPermissions() | LocationPermissionStatus | Check current permission state | | requestPermissions(options?) | LocationPermissionStatus | Request location permissions | | configure(options) | ConfigureResult | Set tracking parameters (call before start()). Supports partial reconfiguration — omitted fields are merged with previous config. | | start() | LocationState | Start background tracking | | stop() | LocationState | Stop tracking | | getState() | LocationState | Get current plugin state | | getCurrentPosition(options?) | Location | One-shot position fix | | checkBatteryOptimization() | BatteryWarningEvent | Check OEM battery optimization state (Android) | | requestBatteryOptimization() | BatteryWarningEvent | Open battery optimization settings (Android) | | addGeofence(options) | void | Add a geofence (max 20) | | addGeofences(options) | void | Add multiple geofences atomically | | removeGeofence(options) | void | Remove a geofence by identifier | | removeAllGeofences() | void | Remove all geofences | | getGeofences() | { geofences: Geofence[] } | Get registered geofences | | removeAllListeners() | void | Remove all listeners and stop tracking |

Events

| Event | Payload | Trigger | |-------|---------|---------| | onLocation | Location | Each GPS update (per distanceFilter) | | onHeartbeat | HeartbeatEvent | Every heartbeatInterval seconds | | onHttp | HttpEvent | After each native HTTP POST response (includes bufferedCount) | | onProviderChange | ProviderChangeEvent | GPS/permission status change | | onDebug | DebugEvent | Debug log message (when debug: true) | | onBatteryWarning | BatteryWarningEvent | Battery optimization may kill tracking (Android only) | | onAccuracyWarning | AccuracyWarningEvent | Approximate location granted (iOS only) | | onMockLocation | MockLocationEvent | Mock location detected — once per session (Android only) | | onPermissionRationale | PermissionRationaleEvent | Before requesting background location (Android 11+ only) | | onGeofence | GeofenceEvent | Geofence enter/exit/dwell transition |

Full API documentation: .wiki/API.md

Licensing

The plugin uses an offline RSA-2048-SHA256 license system with a perpetual + update gating model:

  • Full mode: No restrictions. Requires a valid license key with active updates.
  • Trial mode: debug=true forced, 30 min tracking limit, 1 h cooldown.

Update Gating

Each plugin build embeds a build timestamp. If the license's update period (exp) has ended before the build date, the plugin runs in trial mode:

License exp = 2027-03-16 (1 year from purchase)

Plugin v1.2 (built 2026-05) → exp > build → FULL ✅ forever
Plugin v2.0 (built 2027-06) → exp < build → TRIAL ⚠️ renewal needed

License Renewal

  1. Sign in to the License Portal
  2. Expired licenses show a red Expired badge
  3. Click Renew to generate a new key with a fresh 1-year update period
  4. Update capacitor.config.ts with the new key

See .wiki/Licensing.md for full details.

Testing

npm test              # Web (Vitest) — 164 tests
npm run test:android  # Android (JUnit + MockK) — 343 tests
npm run test:ios      # iOS (XCTest via SPM) — 267 tests

Total: 774 tests across 3 platforms (+ 116 in test app = 890 total).

See .wiki/Testing.md for details.

Development

npm run build          # TypeScript → dist/
npm run test:watch     # Vitest watch mode
npm run verify         # Build + test both native platforms
npm run verify:ios     # Pod install + xcodebuild
npm run verify:android # Gradle clean build test

Debug Sounds

When debug: true and debugSounds: true, the plugin plays system sounds on key events:

| Event | iOS | Android | |-------|-----|---------| | Start tracking | Begin Recording (1054) | TONE_PROP_BEEP | | Stop tracking | End Recording (1055) | TONE_PROP_BEEP2 | | Location fix | Tink (1052) | TONE_PROP_ACK | | Heartbeat | Tock (1057) | TONE_SUP_CONFIRM | | HTTP error | SMS Alert (1073) | TONE_SUP_ERROR | | HTTP success | — (silent) | — (silent) | | Geofence add | Key Press Click (1116) | TONE_PROP_ACK | | Geofence event | Key Press Delete (1117) | TONE_SUP_CONFIRM |

Web platform does not play sounds — debug events are available via onDebug listener and console.debug.

Known Limitations

  • No motion/activity detection

See .wiki/Known-Limitations.md for full list and production roadmap.

License

ELv2 — all rights reserved.