@wayq/beekon-rn 0.0.9 → 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/BeekonRn.podspec +2 -2
- package/CHANGELOG.md +29 -7
- package/LICENSE.txt +3 -3
- package/README.md +84 -325
- package/android/build.gradle +2 -2
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +28 -1
- package/ios/BeekonRn.mm +5 -0
- package/ios/BeekonRn.swift +37 -1
- package/ios/Frameworks/BeekonKit.xcframework/LICENSE.txt +3 -3
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Info.plist +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +254 -233
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +2 -2
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Info.plist +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +254 -233
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +2 -2
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +254 -233
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +2 -2
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +1 -1
- package/lib/module/NativeBeekonRn.js +6 -0
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +13 -1
- package/lib/module/beekon.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/mappers.js +12 -0
- package/lib/module/internal/mappers.js.map +1 -1
- package/lib/module/types/permission.js +2 -0
- package/lib/module/types/permission.js.map +1 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts +19 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +10 -0
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/mappers.d.ts +3 -1
- package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
- package/lib/typescript/src/types/permission.d.ts +42 -0
- package/lib/typescript/src/types/permission.d.ts.map +1 -0
- package/package.json +5 -5
- package/scripts/fetch-beekonkit.sh +4 -4
- package/src/NativeBeekonRn.ts +21 -0
- package/src/beekon.ts +14 -0
- package/src/index.tsx +5 -0
- package/src/internal/mappers.ts +26 -0
- package/src/types/permission.ts +48 -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/
|
|
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
|
-
# `
|
|
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,28 @@ 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.0] - 2026-06-15
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Moved to the `beekonlabs` GitHub org.** The Android native dependency
|
|
14
|
+
coordinate is now `io.github.beekonlabs:beekon` (was `io.github.wayqteam:beekon`),
|
|
15
|
+
and the iOS xcframework is fetched from `github.com/beekonlabs/beekon-ios-binary`.
|
|
16
|
+
This is internal to the module — the npm package name (`@wayq/beekon-rn`) is
|
|
17
|
+
unchanged and no consumer code changes are required.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`getPermissionStatus()` — read-only permission query.** Resolves the current
|
|
22
|
+
location grant as a `PermissionStatus` (`level`: `notDetermined` / `denied` /
|
|
23
|
+
`restricted` / `foreground` / `background`; nullable `accuracy`: `full` /
|
|
24
|
+
`reduced`) for pre-start checks, without ever prompting. Adds the
|
|
25
|
+
`PermissionLevel` / `PermissionAccuracy` types and `isAuthorized` /
|
|
26
|
+
`canTrackInBackground`. Beekon still never *requests* permission — the app owns
|
|
27
|
+
that; during tracking, loss surfaces on `onState`. On Android, "not yet asked"
|
|
28
|
+
and "denied" both report `notDetermined`; `restricted` is iOS-only. Requires
|
|
29
|
+
the native SDKs ≥ the release that adds the API.
|
|
30
|
+
|
|
9
31
|
## [0.0.9] - 2026-06-14
|
|
10
32
|
|
|
11
33
|
### Added
|
|
@@ -34,7 +56,7 @@ Beekon is pre-1.0 — releases may contain breaking changes.
|
|
|
34
56
|
discriminator (`'cloud'` | `'selfManaged'`) is now required. Tracking params,
|
|
35
57
|
`sync`, and `licenseKey` live only on the `selfManaged` arm.
|
|
36
58
|
|
|
37
|
-
[beekon#20]: https://github.com/
|
|
59
|
+
[beekon#20]: https://github.com/beekonlabs/beekon/issues/20
|
|
38
60
|
|
|
39
61
|
## [0.0.8]
|
|
40
62
|
|
|
@@ -43,7 +65,7 @@ No binding API changes.
|
|
|
43
65
|
|
|
44
66
|
### Changed
|
|
45
67
|
|
|
46
|
-
- Native pins bumped to `0.0.8` (Maven `io.github.
|
|
68
|
+
- Native pins bumped to `0.0.8` (Maven `io.github.beekonlabs:beekon`, `BeekonKit`
|
|
47
69
|
xcframework). 0.0.8 embeds the production ES256 license verification keyset,
|
|
48
70
|
so genuine `license-format-v1` tokens resolve to `licensed` / `evaluation`
|
|
49
71
|
on-device, and hardens wire/license conformance. The license surface remains a
|
|
@@ -70,7 +92,7 @@ Built against the native **0.0.7** API; requires native ≥ 0.0.7 at runtime.
|
|
|
70
92
|
degrades, or delays the SDK; Android and iOS may legally diverge for the
|
|
71
93
|
same app.
|
|
72
94
|
- The binding identifies itself to the verifier as the `rn` product.
|
|
73
|
-
- Native pins bumped to `0.0.7` (Maven `io.github.
|
|
95
|
+
- Native pins bumped to `0.0.7` (Maven `io.github.beekonlabs:beekon`, `BeekonKit`
|
|
74
96
|
xcframework).
|
|
75
97
|
|
|
76
98
|
## [0.0.6]
|
|
@@ -110,7 +132,7 @@ Built against the native **0.0.6** API; requires native ≥ 0.0.6 at runtime.
|
|
|
110
132
|
- `resumeIfNeeded()` now calls the guarded native `Beekon.resumeIfNeeded()` on
|
|
111
133
|
Android (previously `Beekon.start()`): it re-adopts a previously-active,
|
|
112
134
|
non-user-stopped session only, mirroring iOS.
|
|
113
|
-
- Native pins bumped to `0.0.6` (Maven `io.github.
|
|
135
|
+
- Native pins bumped to `0.0.6` (Maven `io.github.beekonlabs:beekon`, iOS
|
|
114
136
|
`BeekonKit.xcframework`).
|
|
115
137
|
|
|
116
138
|
## [0.0.5]
|
|
@@ -140,7 +162,7 @@ release. The TypeScript surface is a faithful 1:1 mirror of the native `Beekon`
|
|
|
140
162
|
`minDistanceBetweenLocationsMeters`.
|
|
141
163
|
- `getLocations(from: Date, to: Date)` replaces the prior history call.
|
|
142
164
|
- Notification config is now `androidNotification` (Android-only; iOS ignores it).
|
|
143
|
-
- Native pins bumped to `0.0.5` (Maven `io.github.
|
|
165
|
+
- Native pins bumped to `0.0.5` (Maven `io.github.beekonlabs:beekon`, SwiftPM
|
|
144
166
|
`beekon-ios-binary`).
|
|
145
167
|
|
|
146
168
|
### Removed
|
|
@@ -151,7 +173,7 @@ release. The TypeScript surface is a faithful 1:1 mirror of the native `Beekon`
|
|
|
151
173
|
## [0.0.3]
|
|
152
174
|
|
|
153
175
|
First synchronized release across all four registries (Maven Central,
|
|
154
|
-
`
|
|
176
|
+
`beekonlabs/beekon-ios-binary` xcframework, pub.dev, npm). `Location`
|
|
155
177
|
accuracy/speed/bearing/altitude are nullable to faithfully represent providers
|
|
156
178
|
that don't deliver them.
|
|
157
179
|
|
|
@@ -162,7 +184,7 @@ Initial release.
|
|
|
162
184
|
### Added
|
|
163
185
|
|
|
164
186
|
- React Native (New Architecture / TurboModule) binding for the native Beekon
|
|
165
|
-
location SDKs (Android `io.github.
|
|
187
|
+
location SDKs (Android `io.github.beekonlabs:beekon`, iOS `BeekonKit`
|
|
166
188
|
xcframework).
|
|
167
189
|
- Public API mirroring the native surface: `Beekon.configure / start / stop /
|
|
168
190
|
getLocations`, plus `onState` / `onLocation` subscriptions.
|
package/LICENSE.txt
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Beekon SDK
|
|
2
|
-
Copyright (c) 2026
|
|
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
|
|
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
|
|
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
|
-
|
|
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/
|
|
21
|
+
<a href="https://github.com/beekonlabs/beekon"><strong>GitHub</strong></a>
|
|
23
22
|
</p>
|
|
24
23
|
|
|
25
24
|
---
|
|
26
25
|
|
|
27
|
-
Capture
|
|
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
|
|
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
33
|
- Geofencing with enter and exit events
|
|
35
34
|
- Optional batched server sync with automatic retry
|
|
36
|
-
-
|
|
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,81 @@ 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
|
-
##
|
|
47
|
+
## Install
|
|
50
48
|
|
|
51
49
|
```sh
|
|
52
50
|
npm install @wayq/beekon-rn
|
|
53
51
|
```
|
|
54
52
|
|
|
55
|
-
**iOS** — install
|
|
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
|
-
|
|
58
|
-
|
|
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
|
+
// One-shot fix, independent of tracking (null on timeout)
|
|
111
|
+
const fix = await Beekon.getCurrentLocation();
|
|
112
|
+
|
|
113
|
+
// Pre-start permission check (the app still owns the request)
|
|
114
|
+
const status = await Beekon.getPermissionStatus();
|
|
115
|
+
|
|
116
|
+
// Diagnostics — runtime level, live tail, NDJSON export for bug reports
|
|
117
|
+
await Beekon.setLogLevel('debug');
|
|
118
|
+
const logPath = await Beekon.exportLog();
|
|
59
119
|
```
|
|
60
120
|
|
|
61
|
-
**
|
|
121
|
+
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
122
|
|
|
63
123
|
## Permissions and setup
|
|
64
124
|
|
|
@@ -78,7 +138,7 @@ In `android/app/src/main/AndroidManifest.xml`:
|
|
|
78
138
|
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
|
79
139
|
```
|
|
80
140
|
|
|
81
|
-
While tracking, Android shows a persistent notification (required by the system). Set its title and text with the `notification` option.
|
|
141
|
+
While tracking, Android shows a persistent notification (required by the system). Set its title and text with the `notification` config option.
|
|
82
142
|
|
|
83
143
|
### iOS
|
|
84
144
|
|
|
@@ -103,7 +163,7 @@ In `Info.plist`:
|
|
|
103
163
|
<string>Explain why your app detects activity.</string>
|
|
104
164
|
```
|
|
105
165
|
|
|
106
|
-
Register Beekon's background task once during launch (required for background sync and to resume tracking after
|
|
166
|
+
Register Beekon's background task once during launch (required for background sync and to resume tracking after termination). In `AppDelegate.swift`:
|
|
107
167
|
|
|
108
168
|
```swift
|
|
109
169
|
import BeekonRn
|
|
@@ -118,325 +178,24 @@ func application(
|
|
|
118
178
|
}
|
|
119
179
|
```
|
|
120
180
|
|
|
121
|
-
##
|
|
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):
|
|
257
|
-
|
|
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.
|
|
268
|
-
|
|
269
|
-
## Configuration
|
|
270
|
-
|
|
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.
|
|
181
|
+
## API at a glance
|
|
286
182
|
|
|
287
|
-
|
|
183
|
+
Methods: `configure` · `start` · `stop` · `resumeIfNeeded` · `getCurrentLocation` · `getLocations` · `deleteLocations` · `pendingUploadCount` · `sync` · `setExtras` · `addGeofences` · `removeGeofences` · `listGeofences` · `getPermissionStatus` · `licenseStatus` · `getLog` · `exportLog` · `clearLog` · `setLogLevel` · `log`.
|
|
288
184
|
|
|
289
|
-
|
|
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.
|
|
185
|
+
Subscriptions (each returns an unsubscribe function): `onState` · `onLocation` · `onGeofenceEvent` · `onSyncStatus` · `onAuthTokens` · `onLicenseStatus` · `onLog`.
|
|
324
186
|
|
|
325
|
-
|
|
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`.
|
|
187
|
+
Every method, type, and event is documented at **[docs.getbeekon.com](https://docs.getbeekon.com)** and typed in the package.
|
|
433
188
|
|
|
434
189
|
## Notes
|
|
435
190
|
|
|
436
|
-
- **Retention.** Fixes are kept
|
|
437
|
-
- **Background relaunch.** Call `resumeIfNeeded()` early in
|
|
191
|
+
- **Retention.** Fixes are kept for up to 7 days, or the most recent 100,000 fixes, whichever comes first.
|
|
192
|
+
- **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
193
|
- **Android background limits.** Some manufacturers aggressively stop background services; if tracking halts unexpectedly, see [dontkillmyapp.com](https://dontkillmyapp.com).
|
|
439
194
|
|
|
195
|
+
## Beekon Cloud
|
|
196
|
+
|
|
197
|
+
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/).
|
|
198
|
+
|
|
440
199
|
## Example
|
|
441
200
|
|
|
442
201
|
A complete, runnable example is in the [`example/`](./example) directory.
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
buildscript {
|
|
2
2
|
ext.BeekonRn = [
|
|
3
3
|
kotlinVersion: "2.1.20",
|
|
4
|
-
// minSdk 26 is required by the native Beekon AAR (io.github.
|
|
4
|
+
// minSdk 26 is required by the native Beekon AAR (io.github.beekonlabs:beekon).
|
|
5
5
|
minSdkVersion: 26,
|
|
6
6
|
compileSdkVersion: 36,
|
|
7
7
|
targetSdkVersion: 36
|
|
@@ -79,7 +79,7 @@ dependencies {
|
|
|
79
79
|
// the SDK reaches v1 stability. 0.0.7 is the release that ships the license
|
|
80
80
|
// API (setWrapperInfo / BeekonConfig.licenseKey / Beekon.licenseStatus);
|
|
81
81
|
// 0.0.8 embeds the production ES256 verification keyset.
|
|
82
|
-
implementation "io.github.
|
|
82
|
+
implementation "io.github.beekonlabs:beekon:0.1.0"
|
|
83
83
|
// Kotlin coroutines — required for collecting Beekon's StateFlow/SharedFlow.
|
|
84
84
|
// Beekon already depends on coroutines transitively, but declaring it here
|
|
85
85
|
// makes the dependency intent explicit.
|