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

@andynursa/checkpoint-react-native

v0.1.0

Published

Checkpoint location / geofence / ingest SDK for React Native (iOS + Android). A thin bridge over the @andynursa/checkpoint-capacitor native cores; all detection logic runs server-side.

Readme

@checkpoint/react-native

A thin React Native bridge over the two device-verified Checkpoint native cores (@checkpoint/capacitor's iOS GeofenceManager + Android Play Services / foreground-service layer). It re-implements no geofencing logic — that's server-side. The wrapper's only jobs are (a) expose the native API in JS/TS and (b) wire the platform permissions / background modes / manifest entries the cores require. This mirrors how HyperTrack ships its RN SDK over its native cores.

JS  →  Checkpoint.init({ publishableKey })
       NativeGeofence.configure({ … }); NativeGeofence.addFence({ … })
        │
        ▼  (native module — same name "CheckpointGeofence" on both platforms)
iOS  GeofenceManager.shared   ── or ──  Android GeofenceStore + Play Services
        │                                          │
        ▼                                          ▼
   region wake → POST /v1/ingest from URLSession / OkHttp  (NEVER a JS fetch)

Status — not published. This package and the native core it binds to (@checkpoint/capacitor) both live on unmerged branches. The npm names and the native dependency coordinates below are intended names. See Dependency on the native core.

Architecture: classic bridge, hosted on New Arch

Both native modules are classic bridge modules (iOS RCTEventEmitter, Android ReactContextBaseJavaModule) rather than Swift/Kotlin TurboModules.

  • A TurboModule codegen spec (src/NativeCheckpointGeofence.ts) IS shipped, so the JS resolves the module through TurboModuleRegistry on the New Architecture and through NativeModules on the classic one — same registry name ("CheckpointGeofence"), one resolution path.
  • The native implementations are classic-bridge because RN's interop layer hosts a legacy module unchanged under bridgeless mode, so one Swift file + one Java file serve both architectures. Every method is a trivial forward to the engine; there is no synchronous per-frame hot path that would justify the extra Objective-C++ / JNI codegen shim a fully-native TurboModule needs.

Install (intended)

yarn add @checkpoint/react-native
cd ios && pod install

Autolinking (react-native.config.js + the RN gradle/CocoaPods plugins) wires the iOS pod and the Android package — no MainApplication or Podfile edits.

Usage (mirrors the universal SDK contract)

import { Checkpoint, NativeGeofence } from "@checkpoint/react-native";

// 1. Bootstrap the transport. baseUrl + anonKey are REQUIRED (no baked defaults —
//    a published SDK must not ship a platform ref). publishableKey is safe in a binary.
Checkpoint.init({
  publishableKey: "pk_live_…",
  baseUrl: "https://<project>.supabase.co",
  anonKey: "<anon>",
});

// 2. Persist creds + subject natively so a BACKGROUND relaunch can POST without JS.
await NativeGeofence.configure({
  baseUrl: "https://<project>.supabase.co",
  anonKey: "<anon>",
  publishableKey: "pk_live_…",
  subjectExternalId: "nurse-123",   // YOUR id for the subject
  trackingMode: "geofence",          // geofence (default) | always | off
});

// 3. Permissions (see the platform ladder below).
await NativeGeofence.requestAlwaysAuthorization();
await NativeGeofence.requestNotificationAuthorization();
await NativeGeofence.requestBatteryExemption(); // Android only; no-op on iOS

// 4. Register the perimeter ring → native OS geofence (wakes a force-quit app).
await NativeGeofence.addFence({
  id: "facility-1", latitude: 40.0, longitude: -111.0, radius: 200,
});

// 5. React to crossings while JS is alive (the native layer already POSTed the wake
//    ping regardless). Detection — arrivals/exits/dwell — is server-side.
const sub = await NativeGeofence.addListener("regionEvent", (e) => {
  console.log(e.type, e.regionId, e.latitude, e.longitude, e.timestamp);
});
// later: sub.remove();

// Tracking mode via the ergonomic facade (identical to @checkpoint/capacitor):
await Checkpoint.setTrackingMode("always");
const { mode, streaming } = await Checkpoint.getTrackingMode();

The public surface (Checkpoint, NativeGeofence, TrackingMode, RegionEvent, NativeDiagnostics) is byte-identical to @checkpoint/capacitor — that cross-wrapper uniformity is the whole point.

Cross-wrapper listener idiom

The types and wire values are uniform across all four wrappers; the call syntax for subscribing to region events is idiomatic per platform (this is the one place the "uniform API" claim is scoped to types, not literal call syntax):

| Wrapper | Subscribe | Unsubscribe | |---|---|---| | Capacitor | addListener('regionEvent', cb)Promise<handle> | handle.remove() | | React Native | addListener('regionEvent', cb)Promise<{ remove }> | sub.remove() | | Expo | addListener('regionEvent', cb) (re-exports RN) | sub.remove() | | Flutter | addRegionEventListener(cb)CheckpointListenerHandle | await handle.remove() | | .NET MAUI | RegionEvent += handler; (C# event) | RegionEvent -= handler; |

The event name ("regionEvent"), payload (RegionEvent), and TrackingMode wire values are identical everywhere. The shared conformance fixture (test/conformance.spec.ts, mirrored in each wrapper) asserts that.

Platform configuration

iOS — Info.plist

<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to confirm arrival at your shift facility.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Background location lets us record arrival/departure even when the app is closed.</string>
<key>UIBackgroundModes</key>
<array><string>location</string></array>

Android — permission ladder (driven from JS via PermissionsAndroid)

The library manifest declares the permissions + the core's receivers/services (folded in by manifest-merge from the core AAR). The runtime grant order is your app's responsibility and must be staged — Android 10+ forbids bundling background with foreground:

  1. ACCESS_FINE_LOCATION (foreground) — PermissionsAndroid.request(...).
  2. Then ACCESS_BACKGROUND_LOCATION ("Allow all the time") — a SEPARATE request after foreground is granted. A geofence armed foreground-only never wakes a killed app.
  3. POST_NOTIFICATIONS (API 33+).
  4. Call NativeGeofence.requestAlwaysAuthorization() after the background grant so the module re-registers fences for the killed-app path.
  5. NativeGeofence.requestBatteryExemption() on aggressive OEMs (Samsung One UI especially) — an optimized app is force-stopped and its receivers disabled.

See the Checkpoint docs: docs/guides/whitelisting.md, docs/guides/store-submission.md, docs/guides/mock-locations.md.

Dependency on the native core

This wrapper binds to the same native cores as @checkpoint/capacitor:

  • iOSios/CheckpointGeofence.swift does import CheckpointCapacitor and drives GeofenceManager.shared. Intended pod dependency: CheckpointCapacitor.
  • AndroidCheckpointGeofenceModule.java calls com.checkpoint.capacitor.GeofenceStore / ContinuousLocationService / GeofencingClient. Intended Maven dependency: com.checkpoint:checkpoint-android-core.

Neither coordinate is published yet. The core lives in @checkpoint/capacitor on the unmerged but device-verified branch feat/sdk-extraction-capacitor (an iOS Pod + an Android Capacitor module project, not yet standalone artifacts). Until they're published, an integrator vendors the core via local path:

# ios/Podfile
pod 'CheckpointCapacitor', :path => '../node_modules/@checkpoint/capacitor'
// settings.gradle — include the core module
include ':checkpoint-capacitor'
project(':checkpoint-capacitor').projectDir =
    new File('../node_modules/@checkpoint/capacitor/android')

Core access-control prerequisite (a real, tracked gap)

Binding from a separate module requires two mechanical, no-logic changes to the core (they do not alter behavior):

  • iOS: GeofenceManager's engine methods (configure, addFence, clearFences, requestAlwaysAuthorization, requestNotificationAuthorization, applyTrackingMode, currentMode, isStreaming, monitoredCount, diagnostics, authorizationStatusString, the onRegionEvent callback) and the TrackingMode enum are currently internal. They must be widened to public (only shared + reviveForBackgroundLaunch are public today).
  • Android: GeofenceStore's methods and the services' start/stop/RUNNING are package-private (com.checkpoint.capacitor). They must be widened to public or this module must move into that package.

Known gaps / uncertainties

  • Android regionEvent is native-only in the current core. The Android broadcast receiver POSTs + fires a notification but does not emit a JS event; the Capacitor core has no notifyListeners/RCTDeviceEventEmitter on that path. So addListener("regionEvent") is wired for contract symmetry but only fires on Android once the core adds a JS-bridge broadcast. iOS emits via the engine's onRegionEvent and is live. (This is a faithful mirror of the core's current behavior, not a wrapper bug.)
  • The device REST hot-path is re-exported, not duplicated. mintDeviceToken, getTrackingDirective, and setDeviceTrackingMode are framework-agnostic pure TypeScript in @checkpoint/capacitor's src/api.ts. This wrapper re-exports the real implementation (export { … } from "@checkpoint/capacitor") rather than forking it, so the wire contract (3-key directive RPC body, anon-key bearer on the RPCs, publishable-key bearer on mint) is byte-identical to the core. Checkpoint.init delegates to the core's transport so those re-exports read the same creds. @checkpoint/capacitor is an unpublished peer (branch feat/sdk-extraction-capacitor); src/checkpoint-capacitor-shim.d.ts is an ambient declaration so this package typechecks standalone, and it resolves to the real module once the core is published. (Like the Expo wrapper's react-native shim, this ambient declare module is a pre-publish stand-in only.)
  • Not device-verified. Only TypeScript typechecks here. The native cores are device-verified; this thin bridge over them is not yet — see the device checklist.
  • Expo: use a development build (config plugin TBD — the Expo wrapper is a separate package). This will not run in Expo Go.