@wayq/beekon-rn 0.0.9 → 0.1.2

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.
Files changed (53) hide show
  1. package/BeekonRn.podspec +2 -2
  2. package/CHANGELOG.md +70 -7
  3. package/LICENSE.txt +3 -3
  4. package/README.md +111 -326
  5. package/android/build.gradle +2 -2
  6. package/android/src/main/AndroidManifest.xml +10 -0
  7. package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +132 -1
  8. package/ios/BeekonRn.mm +5 -0
  9. package/ios/BeekonRn.swift +96 -10
  10. package/ios/Frameworks/BeekonKit.xcframework/LICENSE.txt +3 -3
  11. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
  12. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Info.plist +0 -0
  13. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +6218 -3034
  14. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  15. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +89 -5
  16. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
  17. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Info.plist +0 -0
  18. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +6218 -3034
  19. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  20. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +89 -5
  21. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +6218 -3034
  22. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  23. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +89 -5
  24. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +1 -1
  25. package/lib/module/NativeBeekonRn.js +14 -0
  26. package/lib/module/NativeBeekonRn.js.map +1 -1
  27. package/lib/module/beekon.js +26 -1
  28. package/lib/module/beekon.js.map +1 -1
  29. package/lib/module/index.js.map +1 -1
  30. package/lib/module/internal/mappers.js +95 -2
  31. package/lib/module/internal/mappers.js.map +1 -1
  32. package/lib/module/types/permission.js +2 -0
  33. package/lib/module/types/permission.js.map +1 -0
  34. package/lib/typescript/src/NativeBeekonRn.d.ts +39 -0
  35. package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
  36. package/lib/typescript/src/beekon.d.ts +20 -0
  37. package/lib/typescript/src/beekon.d.ts.map +1 -1
  38. package/lib/typescript/src/index.d.ts +2 -1
  39. package/lib/typescript/src/index.d.ts.map +1 -1
  40. package/lib/typescript/src/internal/mappers.d.ts +4 -1
  41. package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
  42. package/lib/typescript/src/types/geofence.d.ts +37 -0
  43. package/lib/typescript/src/types/geofence.d.ts.map +1 -1
  44. package/lib/typescript/src/types/permission.d.ts +78 -0
  45. package/lib/typescript/src/types/permission.d.ts.map +1 -0
  46. package/package.json +5 -5
  47. package/scripts/fetch-beekonkit.sh +6 -6
  48. package/src/NativeBeekonRn.ts +45 -0
  49. package/src/beekon.ts +33 -0
  50. package/src/index.tsx +12 -0
  51. package/src/internal/mappers.ts +140 -0
  52. package/src/types/geofence.ts +41 -0
  53. package/src/types/permission.ts +91 -0
package/BeekonRn.podspec CHANGED
@@ -15,7 +15,7 @@ Pod::Spec.new do |s|
15
15
  # 13–16 fallback paths). Kept at 17.0 to avoid any consumer regression; do not
16
16
  # lower below React Native's own minimum.
17
17
  s.platforms = { :ios => "17.0" }
18
- s.source = { :git => "https://github.com/wayqteam/beekon-rn.git", :tag => "v#{s.version}" }
18
+ s.source = { :git => "https://github.com/beekonlabs/beekon-rn.git", :tag => "v#{s.version}" }
19
19
 
20
20
  s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
21
21
  # Headers are only for the auto-generated `BeekonRn-Swift.h` interop — keep
@@ -23,7 +23,7 @@ Pod::Spec.new do |s|
23
23
  s.private_header_files = "ios/**/*.h"
24
24
 
25
25
  # Native Beekon SDK as a vendored xcframework. The zip is fetched from
26
- # `wayqteam/beekon-ios-binary`'s GitHub Release at this version into
26
+ # `beekonlabs/beekon-ios-binary`'s GitHub Release at this version into
27
27
  # `ios/Frameworks/` by `scripts/fetch-beekonkit.sh`, run via `yarn prepare`,
28
28
  # and bundled in the npm tarball at publish time. SHA256 verified before
29
29
  # extraction.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,69 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  Beekon is pre-1.0 — releases may contain breaking changes.
8
8
 
9
+ ## [0.1.2] - 2026-06-28
10
+
11
+ ### Added
12
+
13
+ - **Per-geofence local notifications.** A `BeekonGeofence` may now carry an
14
+ optional `notification` (`GeofenceNotification`) that the SDK renders natively
15
+ at crossing time — offline and even when the app is killed. It takes a
16
+ required `delivery` (`'local'` is the only implemented mode; `'cloud'` is
17
+ reserved) plus optional `onEnter` / `onExit` `NotificationContent` (`title`
18
+ and `body` required; optional `importance` defaulting to `'high'`, `deepLink`,
19
+ and a flat `data` string map). `onEnter` requires `notifyOnEntry` and `onExit`
20
+ requires `notifyOnExit`; in self-managed mode `delivery` must be `'local'`.
21
+ Adds the `GeofenceNotification`, `NotificationContent`, `NotificationDelivery`,
22
+ and `NotificationImportance` types. Requires the native SDKs ≥ 0.1.2.
23
+ - **`getRequiredPermissions()` — config-aware permission doctor.** Resolves the
24
+ OS permissions the *current configuration* needs as a
25
+ `PermissionRequirement[]`, each marked `satisfied` against the live grant —
26
+ the companion to the location-only `getPermissionStatus()` snapshot. Each
27
+ entry carries `permission` (`BeekonPermission`: `location` /
28
+ `backgroundLocation` / `activityRecognition` / `notifications`), `importance`
29
+ (`required` / `recommended`), `satisfied`, and a human-readable `rationale`,
30
+ reporting activity-recognition and, on Android, notifications too. Read-only;
31
+ never prompts — Beekon still never *requests* permission. Call after
32
+ `configure()`; in cloud mode it returns the conservative list. Adds the
33
+ `PermissionRequirement` and `PermissionImportance` types. Requires the native
34
+ SDKs ≥ 0.1.2.
35
+
36
+ ## [0.1.1] - 2026-06-23
37
+
38
+ ### Changed
39
+
40
+ - **Native SDK pins bumped to 0.1.1** — Android Maven coordinate and iOS
41
+ xcframework (`fetch-beekonkit.sh` `VERSION` + `EXPECTED_SHA`) now track
42
+ BeekonKit / `beekon` 0.1.1 (device control plane, build-time license gate,
43
+ and related native fixes since 0.1.0).
44
+
45
+ ### Added
46
+
47
+ - **Build-gate product id** — native registration now declares the React Native
48
+ wrapper product id for build-time license enforcement (build-gate-v1).
49
+
50
+ ## [0.1.0] - 2026-06-15
51
+
52
+ ### Changed
53
+
54
+ - **Moved to the `beekonlabs` GitHub org.** The Android native dependency
55
+ coordinate is now `io.github.beekonlabs:beekon` (was `io.github.wayqteam:beekon`),
56
+ and the iOS xcframework is fetched from `github.com/beekonlabs/beekon-ios-binary`.
57
+ This is internal to the module — the npm package name (`@wayq/beekon-rn`) is
58
+ unchanged and no consumer code changes are required.
59
+
60
+ ### Added
61
+
62
+ - **`getPermissionStatus()` — read-only permission query.** Resolves the current
63
+ location grant as a `PermissionStatus` (`level`: `notDetermined` / `denied` /
64
+ `restricted` / `foreground` / `background`; nullable `accuracy`: `full` /
65
+ `reduced`) for pre-start checks, without ever prompting. Adds the
66
+ `PermissionLevel` / `PermissionAccuracy` types and `isAuthorized` /
67
+ `canTrackInBackground`. Beekon still never *requests* permission — the app owns
68
+ that; during tracking, loss surfaces on `onState`. On Android, "not yet asked"
69
+ and "denied" both report `notDetermined`; `restricted` is iOS-only. Requires
70
+ the native SDKs ≥ the release that adds the API.
71
+
9
72
  ## [0.0.9] - 2026-06-14
10
73
 
11
74
  ### Added
@@ -34,7 +97,7 @@ Beekon is pre-1.0 — releases may contain breaking changes.
34
97
  discriminator (`'cloud'` | `'selfManaged'`) is now required. Tracking params,
35
98
  `sync`, and `licenseKey` live only on the `selfManaged` arm.
36
99
 
37
- [beekon#20]: https://github.com/wayqteam/beekon/issues/20
100
+ [beekon#20]: https://github.com/beekonlabs/beekon/issues/20
38
101
 
39
102
  ## [0.0.8]
40
103
 
@@ -43,7 +106,7 @@ No binding API changes.
43
106
 
44
107
  ### Changed
45
108
 
46
- - Native pins bumped to `0.0.8` (Maven `io.github.wayqteam:beekon`, `BeekonKit`
109
+ - Native pins bumped to `0.0.8` (Maven `io.github.beekonlabs:beekon`, `BeekonKit`
47
110
  xcframework). 0.0.8 embeds the production ES256 license verification keyset,
48
111
  so genuine `license-format-v1` tokens resolve to `licensed` / `evaluation`
49
112
  on-device, and hardens wire/license conformance. The license surface remains a
@@ -70,7 +133,7 @@ Built against the native **0.0.7** API; requires native ≥ 0.0.7 at runtime.
70
133
  degrades, or delays the SDK; Android and iOS may legally diverge for the
71
134
  same app.
72
135
  - The binding identifies itself to the verifier as the `rn` product.
73
- - Native pins bumped to `0.0.7` (Maven `io.github.wayqteam:beekon`, `BeekonKit`
136
+ - Native pins bumped to `0.0.7` (Maven `io.github.beekonlabs:beekon`, `BeekonKit`
74
137
  xcframework).
75
138
 
76
139
  ## [0.0.6]
@@ -110,7 +173,7 @@ Built against the native **0.0.6** API; requires native ≥ 0.0.6 at runtime.
110
173
  - `resumeIfNeeded()` now calls the guarded native `Beekon.resumeIfNeeded()` on
111
174
  Android (previously `Beekon.start()`): it re-adopts a previously-active,
112
175
  non-user-stopped session only, mirroring iOS.
113
- - Native pins bumped to `0.0.6` (Maven `io.github.wayqteam:beekon`, iOS
176
+ - Native pins bumped to `0.0.6` (Maven `io.github.beekonlabs:beekon`, iOS
114
177
  `BeekonKit.xcframework`).
115
178
 
116
179
  ## [0.0.5]
@@ -140,7 +203,7 @@ release. The TypeScript surface is a faithful 1:1 mirror of the native `Beekon`
140
203
  `minDistanceBetweenLocationsMeters`.
141
204
  - `getLocations(from: Date, to: Date)` replaces the prior history call.
142
205
  - Notification config is now `androidNotification` (Android-only; iOS ignores it).
143
- - Native pins bumped to `0.0.5` (Maven `io.github.wayqteam:beekon`, SwiftPM
206
+ - Native pins bumped to `0.0.5` (Maven `io.github.beekonlabs:beekon`, SwiftPM
144
207
  `beekon-ios-binary`).
145
208
 
146
209
  ### Removed
@@ -151,7 +214,7 @@ release. The TypeScript surface is a faithful 1:1 mirror of the native `Beekon`
151
214
  ## [0.0.3]
152
215
 
153
216
  First synchronized release across all four registries (Maven Central,
154
- `wayqteam/beekon-ios-binary` xcframework, pub.dev, npm). `Location`
217
+ `beekonlabs/beekon-ios-binary` xcframework, pub.dev, npm). `Location`
155
218
  accuracy/speed/bearing/altitude are nullable to faithfully represent providers
156
219
  that don't deliver them.
157
220
 
@@ -162,7 +225,7 @@ Initial release.
162
225
  ### Added
163
226
 
164
227
  - React Native (New Architecture / TurboModule) binding for the native Beekon
165
- location SDKs (Android `io.github.wayqteam:beekon`, iOS `BeekonKit`
228
+ location SDKs (Android `io.github.beekonlabs:beekon`, iOS `BeekonKit`
166
229
  xcframework).
167
230
  - Public API mirroring the native surface: `Beekon.configure / start / stop /
168
231
  getLocations`, plus `onState` / `onLocation` subscriptions.
package/LICENSE.txt CHANGED
@@ -1,9 +1,9 @@
1
1
  Beekon SDK
2
- Copyright (c) 2026 wayqteam. All rights reserved.
2
+ Copyright (c) 2026 beekonlabs. All rights reserved.
3
3
 
4
4
  This software is licensed for evaluation use only. No production deployment,
5
5
  redistribution, sublicensing, or commercial use is permitted without a separate
6
- written agreement with wayqteam.
6
+ written agreement with beekonlabs.
7
7
 
8
8
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9
9
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
@@ -11,4 +11,4 @@ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
11
11
  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING
12
12
  FROM USE OF THE SOFTWARE.
13
13
 
14
- For licensing inquiries: contact wayqteam.
14
+ For licensing inquiries: contact beekonlabs.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  Background location tracking for React Native.<br/>
9
- Native location stack on each platform — tracking survives backgrounding and termination.
9
+ A native location stack on each platform — tracking survives backgrounding and termination.
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -17,24 +17,22 @@
17
17
 
18
18
  <p align="center">
19
19
  <a href="https://docs.getbeekon.com"><strong>Docs</strong></a> ·
20
- <a href="https://console.getbeekon.com"><strong>Console</strong></a> ·
21
20
  <a href="https://getbeekon.com"><strong>getbeekon.com</strong></a> ·
22
- <a href="https://github.com/wayqteam/beekon"><strong>Umbrella</strong></a>
21
+ <a href="https://github.com/beekonlabs/beekon"><strong>GitHub</strong></a>
23
22
  </p>
24
23
 
25
24
  ---
26
25
 
27
- Capture GPS in the foreground and background, keep a queryable history on the device, define geofences, and optionally sync to your own server. The JavaScript API is fully typed.
26
+ Capture location in the foreground and background, keep a queryable history on the device, define geofences, and optionally upload to a backend **you** control. Nothing leaves the device unless you set a `sync` endpoint. The JavaScript API is fully typed.
28
27
 
29
28
  ## Features
30
29
 
31
30
  - Foreground and background tracking that survives app termination
32
- - Battery-aware capture: time and distance filtering, accuracy presets, and automatic pausing while the device is stationary
31
+ - Battery-aware capture: time/distance filtering, accuracy presets, auto-pause while stationary
33
32
  - On-device history you can query at any time
34
- - Geofencing with enter and exit events
33
+ - Geofencing with enter and exit events, plus optional per-geofence local notifications that fire offline and even when the app is killed
35
34
  - Optional batched server sync with automatic retry
36
- - Beekon Cloud mode: point at a project with a `bkproj_` key and let the server own tracking config, geofences, and license
37
- - Diagnostic logging: query/export an on-device log buffer and stream live entries for field debugging
35
+ - Diagnostic logging: query/export an on-device buffer, stream live entries
38
36
  - Activity detection (walking, running, cycling, automotive)
39
37
  - Built for the React Native New Architecture
40
38
 
@@ -46,19 +44,107 @@ Capture GPS in the foreground and background, keep a queryable history on the de
46
44
  | iOS | 17.0 |
47
45
  | Android | 8.0 (API level 26) |
48
46
 
49
- ## Installation
47
+ ## Install
50
48
 
51
49
  ```sh
52
50
  npm install @wayq/beekon-rn
53
51
  ```
54
52
 
55
- **iOS** — install pods:
53
+ **iOS** — `cd ios && pod install`. **Android** — no extra steps; the module links automatically. See [Setup](#permissions-and-setup) for the OS config.
56
54
 
57
- ```sh
58
- cd ios && pod install
55
+ ## Quick start
56
+
57
+ ```ts
58
+ import { Beekon } from '@wayq/beekon-rn';
59
+
60
+ const offState = Beekon.onState((state) => console.log('state:', state.kind));
61
+ const offLocation = Beekon.onLocation((loc) =>
62
+ console.log(loc.latitude, loc.longitude, loc.timestamp),
63
+ );
64
+
65
+ // Configure is optional. Request OS permissions before starting (see Setup).
66
+ await Beekon.configure({
67
+ mode: 'selfManaged',
68
+ minTimeBetweenLocationsSeconds: 30,
69
+ minDistanceBetweenLocationsMeters: 100,
70
+ });
71
+
72
+ await Beekon.start();
73
+ ```
74
+
75
+ `start()` and `stop()` never throw. Observe `onState` to learn whether tracking began and why it stopped (`'user' | 'permissionDenied' | 'locationServicesDisabled' | 'locationUnavailable' | 'system'`).
76
+
77
+ ## Send to your backend
78
+
79
+ Add a `sync` config and Beekon uploads fixes and geofence events to your backend — batched, retried, and removed from the device once your server accepts them. Without a `sync` config, all data stays on the device.
80
+
81
+ ```ts
82
+ await Beekon.configure({
83
+ mode: 'selfManaged',
84
+ sync: {
85
+ url: 'https://api.example.com/locations',
86
+ headers: { Authorization: 'Bearer <token>' },
87
+ // intervalSeconds defaults to 300, batchSize to 100
88
+ },
89
+ });
90
+
91
+ Beekon.setExtras({ userId: 'abc123' }); // attached to every upload
92
+ Beekon.onSyncStatus((status) => console.log('sync:', status.kind));
93
+ ```
94
+
95
+ Your server receives a small JSON envelope and acknowledges it with a status code. **[Build your ingest endpoint](https://docs.getbeekon.com/backend/ingest/)** is a complete, working example. For rotating bearer tokens, set `sync.auth` and the SDK attaches and **natively refreshes** the token — proactively before expiry, reactively on a `401`/`403` — even in the background. See [Auth & token refresh](https://docs.getbeekon.com/backend/auth/).
96
+
97
+ ## More
98
+
99
+ ```ts
100
+ // History (stored on the device, survives restarts)
101
+ const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
102
+ const fixes = await Beekon.getLocations(since, new Date());
103
+
104
+ // Geofences — enter/exit events that keep firing across launches
105
+ await Beekon.addGeofences([
106
+ { id: 'office', latitude: 37.331, longitude: -122.031, radiusMeters: 150 },
107
+ ]);
108
+ const offGeofence = Beekon.onGeofenceEvent((e) => console.log(e.geofenceId, e.type));
109
+
110
+ // Per-geofence local notification — rendered natively at crossing time,
111
+ // offline and even when the app is killed. Add `notification` to a geofence:
112
+ await Beekon.addGeofences([
113
+ {
114
+ id: 'home',
115
+ latitude: 37.331,
116
+ longitude: -122.031,
117
+ radiusMeters: 150,
118
+ notification: {
119
+ delivery: 'local', // 'local' is the only implemented mode ('cloud' reserved)
120
+ onEnter: { title: 'Welcome home', body: 'You arrived', deepLink: 'myapp://home' },
121
+ onExit: { title: 'On the move', body: 'You left home', importance: 'default' },
122
+ },
123
+ },
124
+ ]);
125
+
126
+ // One-shot fix, independent of tracking (null on timeout)
127
+ const fix = await Beekon.getCurrentLocation();
128
+
129
+ // Pre-start permission check (the app still owns the request)
130
+ const status = await Beekon.getPermissionStatus();
131
+
132
+ // Config-aware "permission doctor" — the OS permissions the CURRENT config
133
+ // needs, each marked `satisfied` against the live grant (never prompts).
134
+ // Call after configure(). Reports activity-recognition and, on Android,
135
+ // notifications — not just location.
136
+ const required = await Beekon.getRequiredPermissions();
137
+ for (const r of required) {
138
+ // r.permission, r.importance ('required' | 'recommended'), r.satisfied, r.rationale
139
+ if (!r.satisfied) console.log(`${r.permission} (${r.importance}): ${r.rationale}`);
140
+ }
141
+
142
+ // Diagnostics — runtime level, live tail, NDJSON export for bug reports
143
+ await Beekon.setLogLevel('debug');
144
+ const logPath = await Beekon.exportLog();
59
145
  ```
60
146
 
61
- **Android** — no additional steps; the module links automatically.
147
+ A **license key** is optional and purely observational `configure({ mode: 'selfManaged', licenseKey: '<JWS>' })`, then read `licenseStatus()`; without one, Beekon runs in evaluation mode. The package ships full TypeScript types, so methods, config, and events are typed in your editor. Complete API and guides: **[docs.getbeekon.com](https://docs.getbeekon.com)**.
62
148
 
63
149
  ## Permissions and setup
64
150
 
@@ -78,7 +164,7 @@ In `android/app/src/main/AndroidManifest.xml`:
78
164
  <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
79
165
  ```
80
166
 
81
- While tracking, Android shows a persistent notification (required by the system). Set its title and text with the `notification` option.
167
+ While tracking, Android shows a persistent notification (required by the system). Set its title and text with the `notification` config option.
82
168
 
83
169
  ### iOS
84
170
 
@@ -103,7 +189,7 @@ In `Info.plist`:
103
189
  <string>Explain why your app detects activity.</string>
104
190
  ```
105
191
 
106
- Register Beekon's background task once during launch (required for background sync and to resume tracking after the app is terminated). In `AppDelegate.swift`:
192
+ Register Beekon's background task once during launch (required for background sync and to resume tracking after termination). In `AppDelegate.swift`:
107
193
 
108
194
  ```swift
109
195
  import BeekonRn
@@ -118,325 +204,24 @@ func application(
118
204
  }
119
205
  ```
120
206
 
121
- ## Usage
122
-
123
- ### Start tracking
124
-
125
- ```ts
126
- import { Beekon } from '@wayq/beekon-rn';
127
-
128
- const offState = Beekon.onState((state) => {
129
- console.log('state:', state.kind); // 'idle' | 'tracking' | 'stopped'
130
- });
131
-
132
- const offLocation = Beekon.onLocation((location) => {
133
- console.log(location.latitude, location.longitude, location.timestamp);
134
- });
135
-
136
- // Configure is optional. Request OS permissions before starting.
137
- await Beekon.configure({
138
- mode: 'selfManaged',
139
- minTimeBetweenLocationsSeconds: 30,
140
- minDistanceBetweenLocationsMeters: 100,
141
- });
142
-
143
- await Beekon.start();
144
-
145
- // Later:
146
- await Beekon.stop();
147
- offState();
148
- offLocation();
149
- ```
150
-
151
- `start()` and `stop()` never throw. Observe `onState` to learn whether tracking began and why it stopped:
152
-
153
- ```ts
154
- Beekon.onState((state) => {
155
- if (state.kind === 'stopped') {
156
- // 'user' | 'permissionDenied' | 'locationServicesDisabled'
157
- // | 'locationUnavailable' | 'cloudModeUnavailable' | 'system'
158
- console.log('stopped:', state.reason);
159
- }
160
- });
161
- ```
162
-
163
- ### Read history
164
-
165
- Recorded fixes are stored on the device and remain available even after the app is closed.
166
-
167
- ```ts
168
- const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
169
- const fixes = await Beekon.getLocations(since, new Date());
170
-
171
- await Beekon.deleteLocations(); // delete everything
172
- await Beekon.deleteLocations(since); // delete up to a cutoff
173
- ```
174
-
175
- ### Geofences
176
-
177
- ```ts
178
- await Beekon.addGeofences([
179
- { id: 'office', latitude: 37.331, longitude: -122.031, radiusMeters: 150 },
180
- ]);
181
-
182
- const offGeofence = Beekon.onGeofenceEvent((event) => {
183
- console.log(event.geofenceId, event.type); // 'enter' | 'exit'
184
- });
185
-
186
- await Beekon.listGeofences();
187
- await Beekon.removeGeofences(['office']);
188
- ```
189
-
190
- ### Server sync
191
-
192
- Add a `sync` config to upload recorded fixes and geofence events to your backend. Uploads are batched, retried automatically, and removed from the device once your server accepts them. Without a `sync` config, all data stays on the device.
193
-
194
- ```ts
195
- await Beekon.configure({
196
- mode: 'selfManaged',
197
- sync: {
198
- url: 'https://api.example.com/locations',
199
- headers: { Authorization: 'Bearer <token>' },
200
- // intervalSeconds defaults to 300, batchSize to 100
201
- },
202
- });
203
-
204
- Beekon.setExtras({ userId: 'abc123' }); // attached to every upload
205
-
206
- Beekon.onSyncStatus((status) => {
207
- console.log('sync:', status.kind); // 'idle' | 'pending' | 'failed'
208
- });
209
- ```
210
-
211
- #### Token refresh
212
-
213
- Set `sync.auth` (an `AuthConfig`) to let the SDK attach and **natively** refresh the upload access token — proactively before it expires and reactively on a `401`/`403`, then retry the upload. This runs in the background and survives a cold launch with no host involvement. Without `auth`, a `401`/`403` pauses sync and surfaces `SyncFailure 'auth'`; re-seed by calling `configure()` again to resume.
214
-
215
- The supplied tokens are a **seed**: the SDK owns and rotates the live token set in secure storage (Keychain / encrypted prefs) after first persist. Subscribe with `onAuthTokens` to mirror rotations into your own session store.
216
-
217
- ```ts
218
- await Beekon.configure({
219
- mode: 'selfManaged',
220
- sync: {
221
- url: 'https://api.example.com/locations',
222
- auth: {
223
- accessToken: '<initial access token>',
224
- refreshToken: '<initial refresh token>',
225
- expiresAt: new Date(Date.now() + 3600_000),
226
- refreshUrl: 'https://auth.example.com/oauth/token',
227
- refreshPayload: {
228
- grant_type: 'refresh_token',
229
- refresh_token: '{refreshToken}', // substituted with the current token
230
- },
231
- // strategy defaults to 'bearer', refreshBodyFormat to 'form',
232
- // skewMarginSeconds to 60. responseMapping defaults to common-name detection.
233
- },
234
- },
235
- });
236
-
237
- const offAuth = Beekon.onAuthTokens((tokens) => {
238
- // Persist the rotated set into your own session store.
239
- console.log('rotated:', tokens.accessToken, tokens.expiresAt, tokens.epoch);
240
- });
241
- ```
242
-
243
- The refresh recipe is persisted in plaintext to survive a cold launch — **do not** put static client secrets in `refreshHeaders` or `refreshPayload`. Requires the native SDK ≥ 0.0.6; with an older native binary the recipe is ignored and only static `sync.headers` apply.
244
-
245
- ### License
246
-
247
- Supply a Beekon license token (a `license-format-v1` JWS) with `licenseKey`. It is the highest-priority channel, overriding the platform manifest value (`in.wayq.beekon.license` meta-data on Android, `BeekonLicenseKey` in `Info.plist` on iOS); `undefined`, blank, or whitespace-only means unset. A token is app-id- and product-bound, so it is safe to commit to source control.
248
-
249
- ```ts
250
- await Beekon.configure({
251
- mode: 'selfManaged',
252
- licenseKey: '<license-format-v1 JWS>',
253
- });
254
- ```
255
-
256
- The license is **purely observational** — no status ever blocks, degrades, or delays the SDK. Read the current platform's status once with `licenseStatus()`, or subscribe with `onLicenseStatus` (the latest status is delivered immediately):
207
+ ## API at a glance
257
208
 
258
- ```ts
259
- const status = await Beekon.licenseStatus();
260
- console.log('license:', status.status); // 'evaluation' | 'licensed' | ...
261
-
262
- const offLicense = Beekon.onLicenseStatus((s) => {
263
- if (s.status === 'licensed') console.log('tier:', s.tier);
264
- });
265
- ```
266
-
267
- Android and iOS validate independently and may legally report different statuses for the same app (for example a license scoped to `["android"]` reads `licensed` on Android and `invalid` / `productMismatch` on iOS). The value reflects only the platform you are running on.
209
+ Methods: `configure` · `start` · `stop` · `resumeIfNeeded` · `getCurrentLocation` · `getLocations` · `deleteLocations` · `pendingUploadCount` · `sync` · `setExtras` · `addGeofences` · `removeGeofences` · `listGeofences` · `getPermissionStatus` · `getRequiredPermissions` · `licenseStatus` · `getLog` · `exportLog` · `clearLog` · `setLogLevel` · `log`.
268
210
 
269
- ## Configuration
211
+ Subscriptions (each returns an unsubscribe function): `onState` · `onLocation` · `onGeofenceEvent` · `onSyncStatus` · `onAuthTokens` · `onLicenseStatus` · `onLog`.
270
212
 
271
- `BeekonConfig` is a sealed two-arm union pass `mode: 'selfManaged'` (the options below, all optional) or `mode: 'cloud'` (`{ projectKey, endpoint?, notification?, logLevel? }`, where the server owns tracking/geofences/license). Self-managed options (defaults shown):
272
-
273
- | Option | Default | Description |
274
- |---|---|---|
275
- | `minTimeBetweenLocationsSeconds` | `30` | Minimum seconds between recorded fixes. `0` disables the time filter. |
276
- | `minDistanceBetweenLocationsMeters` | `100` | Minimum metres between recorded fixes. `0` disables the distance filter. |
277
- | `accuracyMode` | `'balanced'` | `'high'` \| `'balanced'` \| `'low'`. Trades accuracy against battery. |
278
- | `whenStationary` | `'pause'` | `'keepTracking'` \| `'pause'` \| `'pauseWithCheckIns'`. |
279
- | `stationaryRadiusMeters` | `5` | Distance the device must move to count as moving again. |
280
- | `detectActivity` | `false` | Detect physical activity. Requires the motion permission. |
281
- | `sync` | – | Server upload settings: `{ url, headers?, intervalSeconds?, batchSize?, syncThreshold?, auth? }`. See [Token refresh](#token-refresh). |
282
- | `notification` | – | Android-only tracking notification: `{ title?, text?, smallIcon? }`. `smallIcon` is a drawable/mipmap resource name. |
283
- | `licenseKey` | – | Beekon license token (`license-format-v1` JWS). Highest-priority supply channel; overrides the platform manifest value. See [License](#license). |
284
-
285
- A fix is recorded only when **both** the time and distance filters are satisfied.
286
-
287
- ## API
288
-
289
- | Method | Description |
290
- |---|---|
291
- | `configure(config)` | Apply settings. Optional; may be called while tracking. |
292
- | `start()` | Begin tracking. |
293
- | `stop()` | Stop tracking. |
294
- | `resumeIfNeeded()` | Resume a session that was active before the app closed. |
295
- | `getCurrentLocation(options?)` | One-shot current fix, independent of tracking. Resolves `null` on timeout. `options`: `{ timeoutMs?, accuracy? }`. |
296
- | `getLocations(from, to)` | Recorded fixes within a date range. |
297
- | `deleteLocations(before?)` | Delete fixes (all if `before` is omitted); returns the count removed. |
298
- | `pendingUploadCount()` | Number of fixes not yet uploaded. |
299
- | `sync()` | Request an immediate upload. |
300
- | `setExtras(extras)` | Custom fields included with every upload. |
301
- | `addGeofences(geofences)` | Register circular regions. |
302
- | `removeGeofences(ids)` | Unregister geofences by id. |
303
- | `listGeofences()` | Currently registered geofences. |
304
- | `licenseStatus()` | Current platform's license status (a `LicenseStatus`). Observational only. See [License](#license). |
305
- | `getLog(from, to)` | Persisted diagnostic log entries in a date range (oldest first). |
306
- | `exportLog()` | Serialize the log buffer to an NDJSON file; resolves the file path. |
307
- | `clearLog()` | Delete all persisted diagnostic log entries. |
308
- | `setLogLevel(level)` | Change the diagnostic log verbosity threshold at runtime. |
309
- | `log(level, message)` | Write a host-app breadcrumb into the SDK log pipeline (category `app`). |
310
-
311
- Subscriptions — each returns an unsubscribe function:
312
-
313
- | Subscription | Delivers |
314
- |---|---|
315
- | `onState(cb)` | Tracking state changes. The current state is delivered immediately. |
316
- | `onLocation(cb)` | Each newly recorded fix. |
317
- | `onGeofenceEvent(cb)` | Geofence enter and exit events. |
318
- | `onSyncStatus(cb)` | Upload status. The current status is delivered immediately. |
319
- | `onAuthTokens(cb)` | Token rotations from native refresh (see [Token refresh](#token-refresh)). The latest rotation is delivered immediately. |
320
- | `onLicenseStatus(cb)` | License status changes (see [License](#license)). The current status is delivered immediately. |
321
- | `onLog(cb)` | Diagnostic log entries as they are recorded (broadcast, no replay). |
322
-
323
- `getLocations`, `deleteLocations`, `pendingUploadCount`, and `addGeofences` reject with a `BeekonError` on failure — check `error.kind` (`'storage'` or `'invalidGeofence'`). `getCurrentLocation` rejects with `error.kind` `'permissionDenied'`, `'locationServicesDisabled'`, or `'locationUnavailable'` on a precondition failure. Other methods resolve normally; tracking-lifecycle problems are reported through `onState` rather than thrown.
324
-
325
- ## Types
326
-
327
- ```ts
328
- type Location = {
329
- id: string;
330
- latitude: number;
331
- longitude: number;
332
- timestamp: Date;
333
- accuracy: number | null; // metres
334
- speed: number | null; // m/s
335
- bearing: number | null; // degrees from true north
336
- altitude: number | null; // metres
337
- quality: 'ok' | 'lowAccuracy' | 'implausibleSpeed';
338
- trigger: 'interval' | 'motion' | 'checkIn' | 'geofence' | 'manual';
339
- motion: 'moving' | 'stationary' | 'unknown';
340
- activity:
341
- | 'stationary' | 'walking' | 'running'
342
- | 'cycling' | 'automotive' | 'unknown' | null;
343
- isMock: boolean;
344
- };
345
-
346
- type BeekonState =
347
- | { kind: 'idle' }
348
- | { kind: 'tracking' }
349
- | {
350
- kind: 'stopped';
351
- reason:
352
- | 'user' | 'permissionDenied' | 'locationServicesDisabled'
353
- | 'locationUnavailable' | 'cloudModeUnavailable' | 'system';
354
- };
355
-
356
- type SyncStatus =
357
- | { kind: 'idle' }
358
- | { kind: 'pending' }
359
- | { kind: 'failed'; reason: 'auth' | 'rejected' };
360
-
361
- type BeekonGeofence = {
362
- id: string;
363
- latitude: number;
364
- longitude: number;
365
- radiusMeters: number;
366
- notifyOnEntry?: boolean; // default true
367
- notifyOnExit?: boolean; // default true
368
- };
369
-
370
- type GeofenceEvent = {
371
- id: string;
372
- geofenceId: string;
373
- type: 'enter' | 'exit';
374
- timestamp: Date;
375
- };
376
-
377
- // Set on `SyncConfig.auth` to enable native token refresh. The token fields are
378
- // a seed — the SDK rotates them after first persist.
379
- type AuthConfig = {
380
- accessToken?: string;
381
- refreshToken?: string;
382
- expiresAt?: Date;
383
- strategy?: 'bearer' | 'raw'; // default 'bearer'
384
- refreshUrl?: string; // omit to disable refresh
385
- refreshPayload?: Record<string, string>; // '{refreshToken}' substituted
386
- refreshHeaders?: Record<string, string>; // '{accessToken}' substituted
387
- refreshBodyFormat?: 'form' | 'json'; // default 'form'
388
- responseMapping?: {
389
- accessToken?: string;
390
- refreshToken?: string;
391
- expiresIn?: string;
392
- expiresAt?: string;
393
- };
394
- skewMarginSeconds?: number; // default 60
395
- seedEpoch?: number;
396
- };
397
-
398
- // Delivered by `onAuthTokens` after each native rotation. Sensitive — in-process
399
- // only, never logged.
400
- type AuthTokens = {
401
- accessToken: string;
402
- refreshToken: string | null;
403
- expiresAt: Date | null;
404
- epoch: number; // monotonic generation, bumped on each refresh
405
- };
406
-
407
- // Returned by `licenseStatus()` / delivered by `onLicenseStatus`. Observational
408
- // only — no status ever blocks the SDK. Use `status` as the discriminator.
409
- type LicenseStatus =
410
- | { status: 'notDetermined' }
411
- | { status: 'evaluation' }
412
- | { status: 'expired' }
413
- | { status: 'updateEntitlementLapsed' }
414
- | { status: 'licensed'; tier: string; entitlements: string[] }
415
- | { status: 'invalid'; reason: LicenseInvalidReason };
416
-
417
- type LicenseInvalidReason =
418
- | 'malformed' | 'unknownKey' | 'badSignature'
419
- | 'appIdMismatch' | 'productMismatch' | 'unsupportedVersion';
420
-
421
- type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug' | 'verbose';
422
-
423
- type LogEntry = {
424
- id: string; // UUIDv7
425
- timestamp: Date;
426
- level: LogLevel;
427
- category: string; // e.g. 'location', 'sync'; host breadcrumbs use 'app'
428
- message: string;
429
- };
430
- ```
431
-
432
- Optional numeric fields on `Location` are `null` when the device did not report them — never `0`.
213
+ Every method, type, and event is documented at **[docs.getbeekon.com](https://docs.getbeekon.com)** and typed in the package.
433
214
 
434
215
  ## Notes
435
216
 
436
- - **Retention.** Fixes are kept on the device for up to 7 days, or the most recent 100,000 fixes, whichever comes first.
437
- - **Background relaunch.** Call `resumeIfNeeded()` early in your app to restore tracking after the system terminates it. On Android, also resume from your `Application.onCreate` (see the example app).
217
+ - **Retention.** Fixes are kept for up to 7 days, or the most recent 100,000 fixes, whichever comes first.
218
+ - **Background relaunch.** Call `resumeIfNeeded()` early in startup to restore tracking after the system terminates the app. On Android, also resume from `Application.onCreate` (see the example app).
438
219
  - **Android background limits.** Some manufacturers aggressively stop background services; if tracking halts unexpectedly, see [dontkillmyapp.com](https://dontkillmyapp.com).
439
220
 
221
+ ## Beekon Cloud
222
+
223
+ Prefer managed ingest, a console, and server-owned config instead of running your own backend? **Beekon Cloud** is in private beta — see the [docs](https://docs.getbeekon.com/cloud/).
224
+
440
225
  ## Example
441
226
 
442
227
  A complete, runnable example is in the [`example/`](./example) directory.