@wayq/beekon-rn 0.0.8 → 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 +3 -3
- package/CHANGELOG.md +58 -6
- package/LICENSE.txt +3 -3
- package/README.md +85 -264
- package/android/build.gradle +2 -2
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +133 -8
- package/ios/BeekonRn.mm +40 -0
- package/ios/BeekonRn.swift +176 -8
- package/ios/Frameworks/BeekonKit.xcframework/Info.plist +5 -5
- 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/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +5839 -3482
- 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 +93 -16
- 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/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +5839 -3482
- 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 +93 -16
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +5839 -3482
- 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 +93 -16
- package/lib/module/NativeBeekonRn.js +6 -0
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +109 -1
- package/lib/module/beekon.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/licenseNudge.js +64 -0
- package/lib/module/internal/licenseNudge.js.map +1 -0
- package/lib/module/internal/mappers.js +68 -8
- package/lib/module/internal/mappers.js.map +1 -1
- package/lib/module/types/config.js +73 -1
- package/lib/module/types/config.js.map +1 -1
- package/lib/module/types/log.js +4 -0
- package/lib/module/types/log.js.map +1 -0
- package/lib/module/types/permission.js +2 -0
- package/lib/module/types/permission.js.map +1 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts +66 -2
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +56 -1
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/licenseNudge.d.ts +38 -0
- package/lib/typescript/src/internal/licenseNudge.d.ts.map +1 -0
- package/lib/typescript/src/internal/mappers.d.ts +5 -1
- package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
- package/lib/typescript/src/types/config.d.ts +90 -7
- package/lib/typescript/src/types/config.d.ts.map +1 -1
- package/lib/typescript/src/types/enums.d.ts +8 -0
- package/lib/typescript/src/types/enums.d.ts.map +1 -1
- package/lib/typescript/src/types/log.d.ts +22 -0
- package/lib/typescript/src/types/log.d.ts.map +1 -0
- package/lib/typescript/src/types/permission.d.ts +42 -0
- package/lib/typescript/src/types/permission.d.ts.map +1 -0
- package/lib/typescript/src/types/state.d.ts +4 -1
- package/lib/typescript/src/types/state.d.ts.map +1 -1
- package/package.json +5 -5
- package/scripts/fetch-beekonkit.sh +6 -6
- package/src/NativeBeekonRn.ts +70 -2
- package/src/beekon.ts +114 -1
- package/src/index.tsx +12 -1
- package/src/internal/licenseNudge.ts +80 -0
- package/src/internal/mappers.ts +93 -7
- package/src/types/config.ts +99 -7
- package/src/types/enums.ts +9 -0
- package/src/types/log.ts +22 -0
- package/src/types/permission.ts +48 -0
- package/src/types/state.ts +4 -0
|
@@ -25,8 +25,11 @@ import `in`.wayq.beekon.Location
|
|
|
25
25
|
import `in`.wayq.beekon.LocationQuality
|
|
26
26
|
import `in`.wayq.beekon.LocationTrigger
|
|
27
27
|
import `in`.wayq.beekon.LocationUnavailableReason
|
|
28
|
+
import `in`.wayq.beekon.LogEntry
|
|
29
|
+
import `in`.wayq.beekon.LogLevel
|
|
28
30
|
import `in`.wayq.beekon.MotionState
|
|
29
31
|
import `in`.wayq.beekon.NotificationConfig
|
|
32
|
+
import `in`.wayq.beekon.PermissionStatus
|
|
30
33
|
import `in`.wayq.beekon.StationaryMode
|
|
31
34
|
import `in`.wayq.beekon.StopReason
|
|
32
35
|
import `in`.wayq.beekon.SyncConfig
|
|
@@ -64,6 +67,7 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
64
67
|
scope.launch { Beekon.syncStatus.collect { emitOnSyncStatus(syncStatusToWire(it)) } }
|
|
65
68
|
scope.launch { Beekon.authChanges.collect { emitOnAuthTokens(tokenRefreshToWire(it)) } }
|
|
66
69
|
scope.launch { Beekon.licenseStatus.collect { emitOnLicenseStatus(licenseStatusToWire(it)) } }
|
|
70
|
+
scope.launch { Beekon.logs.collect { emitOnLog(logEntryToWire(it)) } }
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
// ---------------------------------------------------------------------------
|
|
@@ -232,6 +236,60 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
232
236
|
promise.resolve(licenseStatusToWire(Beekon.licenseStatus.value))
|
|
233
237
|
}
|
|
234
238
|
|
|
239
|
+
override fun getPermissionStatus(promise: Promise) {
|
|
240
|
+
// Synchronous, non-throwing read on the native SDK.
|
|
241
|
+
promise.resolve(permissionStatusToWire(Beekon.getPermissionStatus()))
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Diagnostic logs (spec diagnostics/log-format-v1)
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
override fun getLog(fromMs: Double, toMs: Double, promise: Promise) {
|
|
249
|
+
scope.launch {
|
|
250
|
+
try {
|
|
251
|
+
val from = Instant.ofEpochMilli(fromMs.toLong())
|
|
252
|
+
val to = Instant.ofEpochMilli(toMs.toLong())
|
|
253
|
+
val arr: WritableArray = Arguments.createArray()
|
|
254
|
+
for (e in Beekon.getLog(from, to)) arr.pushMap(logEntryToWire(e))
|
|
255
|
+
promise.resolve(arr)
|
|
256
|
+
} catch (t: Throwable) {
|
|
257
|
+
promise.reject(errorCode(t), t.message ?: "getLog failed", t)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
override fun exportLog(promise: Promise) {
|
|
263
|
+
scope.launch {
|
|
264
|
+
try {
|
|
265
|
+
promise.resolve(Beekon.exportLog())
|
|
266
|
+
} catch (t: Throwable) {
|
|
267
|
+
promise.reject(errorCode(t), t.message ?: "exportLog failed", t)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
override fun clearLog(promise: Promise) {
|
|
273
|
+
scope.launch {
|
|
274
|
+
try {
|
|
275
|
+
Beekon.clearLog()
|
|
276
|
+
promise.resolve(null)
|
|
277
|
+
} catch (t: Throwable) {
|
|
278
|
+
promise.reject(errorCode(t), t.message ?: "clearLog failed", t)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
override fun setLogLevel(level: String, promise: Promise) {
|
|
284
|
+
Beekon.setLogLevel(toLogLevel(level))
|
|
285
|
+
promise.resolve(null)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
override fun log(level: String, message: String, promise: Promise) {
|
|
289
|
+
Beekon.log(toLogLevel(level), message)
|
|
290
|
+
promise.resolve(null)
|
|
291
|
+
}
|
|
292
|
+
|
|
235
293
|
override fun invalidate() {
|
|
236
294
|
super.invalidate()
|
|
237
295
|
scope.cancel()
|
|
@@ -241,20 +299,47 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
241
299
|
// Mappers: wire (ReadableMap/Array) → Kotlin
|
|
242
300
|
// ---------------------------------------------------------------------------
|
|
243
301
|
|
|
302
|
+
// GATED on beekon native 0.0.9 (sealed BeekonConfig) — does not compile against
|
|
303
|
+
// the pinned 0.0.8 dep (the flat BeekonConfig(...) constructor). Switches on the
|
|
304
|
+
// wire `mode` (cloud-mode-v1 §2) to build the matching sealed arm. A cloud
|
|
305
|
+
// configuration with an empty/invalid projectKey throws
|
|
306
|
+
// BeekonException.InvalidConfiguration, which the configure() catch turns into a
|
|
307
|
+
// promise rejection via errorCode().
|
|
244
308
|
private fun wireToConfig(map: ReadableMap): BeekonConfig {
|
|
245
|
-
val sync =
|
|
246
|
-
if (map.hasKey("sync") && !map.isNull("sync")) {
|
|
247
|
-
map.getMap("sync")?.let { wireToSyncConfig(it) }
|
|
248
|
-
} else {
|
|
249
|
-
null
|
|
250
|
-
}
|
|
251
309
|
val notification =
|
|
252
310
|
if (map.hasKey("notification") && !map.isNull("notification")) {
|
|
253
311
|
map.getMap("notification")?.let { wireToNotification(it) }
|
|
254
312
|
} else {
|
|
255
313
|
null
|
|
256
314
|
}
|
|
257
|
-
|
|
315
|
+
|
|
316
|
+
if (optString(map, "mode") == "cloud") {
|
|
317
|
+
val projectKey = optString(map, "projectKey") ?: ""
|
|
318
|
+
val endpoint = optString(map, "endpoint")
|
|
319
|
+
// Omitted endpoint → fall back to the constructor's baked-in default.
|
|
320
|
+
return if (endpoint != null) {
|
|
321
|
+
BeekonConfig.Cloud(
|
|
322
|
+
projectKey = projectKey,
|
|
323
|
+
endpoint = endpoint,
|
|
324
|
+
notification = notification ?: NotificationConfig(),
|
|
325
|
+
logLevel = toLogLevel(optString(map, "logLevel")),
|
|
326
|
+
)
|
|
327
|
+
} else {
|
|
328
|
+
BeekonConfig.Cloud(
|
|
329
|
+
projectKey = projectKey,
|
|
330
|
+
notification = notification ?: NotificationConfig(),
|
|
331
|
+
logLevel = toLogLevel(optString(map, "logLevel")),
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
val sync =
|
|
337
|
+
if (map.hasKey("sync") && !map.isNull("sync")) {
|
|
338
|
+
map.getMap("sync")?.let { wireToSyncConfig(it) }
|
|
339
|
+
} else {
|
|
340
|
+
null
|
|
341
|
+
}
|
|
342
|
+
return BeekonConfig.SelfManaged(
|
|
258
343
|
minTimeBetweenLocationsSeconds =
|
|
259
344
|
map.getDouble("minTimeBetweenLocationsSeconds").toLong(),
|
|
260
345
|
minDistanceBetweenLocationsMeters =
|
|
@@ -268,6 +353,7 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
268
353
|
// Passed through verbatim; absent/null means unset — the SDK falls through
|
|
269
354
|
// to the manifest meta-data, then evaluation (license-format-v1 §9).
|
|
270
355
|
licenseKey = optString(map, "licenseKey"),
|
|
356
|
+
logLevel = toLogLevel(optString(map, "logLevel")),
|
|
271
357
|
)
|
|
272
358
|
}
|
|
273
359
|
|
|
@@ -283,6 +369,7 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
283
369
|
headers = entriesToMap(map.getArray("headers")),
|
|
284
370
|
intervalSeconds = map.getDouble("intervalSeconds").toLong(),
|
|
285
371
|
batchSize = map.getDouble("batchSize").toInt(),
|
|
372
|
+
syncThreshold = if (map.hasKey("syncThreshold")) map.getDouble("syncThreshold").toInt() else 0,
|
|
286
373
|
auth = auth,
|
|
287
374
|
)
|
|
288
375
|
}
|
|
@@ -432,6 +519,27 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
432
519
|
return m
|
|
433
520
|
}
|
|
434
521
|
|
|
522
|
+
private fun permissionStatusToWire(p: PermissionStatus): WritableMap {
|
|
523
|
+
val m = Arguments.createMap()
|
|
524
|
+
m.putString("level", permissionLevelToWire(p.level))
|
|
525
|
+
// '' is the wire encoding of null (not granted).
|
|
526
|
+
m.putString("accuracy", p.accuracy?.let { permissionAccuracyToWire(it) } ?: "")
|
|
527
|
+
return m
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private fun permissionLevelToWire(l: PermissionStatus.Level): String = when (l) {
|
|
531
|
+
PermissionStatus.Level.NotDetermined -> "notDetermined"
|
|
532
|
+
PermissionStatus.Level.Denied -> "denied"
|
|
533
|
+
PermissionStatus.Level.Restricted -> "restricted"
|
|
534
|
+
PermissionStatus.Level.Foreground -> "foreground"
|
|
535
|
+
PermissionStatus.Level.Background -> "background"
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private fun permissionAccuracyToWire(a: PermissionStatus.Accuracy): String = when (a) {
|
|
539
|
+
PermissionStatus.Accuracy.Full -> "full"
|
|
540
|
+
PermissionStatus.Accuracy.Reduced -> "reduced"
|
|
541
|
+
}
|
|
542
|
+
|
|
435
543
|
private fun syncStatusToWire(s: SyncStatus): WritableMap {
|
|
436
544
|
val m = Arguments.createMap()
|
|
437
545
|
when (s) {
|
|
@@ -484,6 +592,16 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
484
592
|
return m
|
|
485
593
|
}
|
|
486
594
|
|
|
595
|
+
private fun logEntryToWire(e: LogEntry): WritableMap {
|
|
596
|
+
val m = Arguments.createMap()
|
|
597
|
+
m.putString("id", e.id)
|
|
598
|
+
m.putDouble("timestampMs", e.timestamp.toEpochMilli().toDouble())
|
|
599
|
+
m.putString("level", e.level.wire)
|
|
600
|
+
m.putString("category", e.category)
|
|
601
|
+
m.putString("message", e.message)
|
|
602
|
+
return m
|
|
603
|
+
}
|
|
604
|
+
|
|
487
605
|
private fun putNullableDouble(map: WritableMap, key: String, value: Double?) {
|
|
488
606
|
if (value == null) map.putNull(key) else map.putDouble(key, value)
|
|
489
607
|
}
|
|
@@ -514,11 +632,18 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
514
632
|
else -> AuthBodyFormat.Form
|
|
515
633
|
}
|
|
516
634
|
|
|
635
|
+
// Wire token (lowercase) -> native LogLevel; unknown/absent defaults to Info.
|
|
636
|
+
private fun toLogLevel(s: String?): LogLevel =
|
|
637
|
+
LogLevel.fromWire(s ?: "") ?: LogLevel.Info
|
|
638
|
+
|
|
639
|
+
// GATED on beekon native 0.0.9 (StopReason.CloudModeUnavailable) — the new arm
|
|
640
|
+
// does not exist on the pinned 0.0.8 dep, so this `when` is non-exhaustive there.
|
|
517
641
|
private fun stopReasonToWire(r: StopReason): String = when (r) {
|
|
518
642
|
StopReason.User -> "user"
|
|
519
643
|
StopReason.PermissionDenied -> "permissionDenied"
|
|
520
644
|
StopReason.LocationServicesDisabled -> "locationServicesDisabled"
|
|
521
645
|
StopReason.LocationUnavailable -> "locationUnavailable"
|
|
646
|
+
StopReason.CloudModeUnavailable -> "cloudModeUnavailable"
|
|
522
647
|
StopReason.System -> "system"
|
|
523
648
|
}
|
|
524
649
|
|
|
@@ -580,6 +705,6 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
580
705
|
// Reported to the native verifier via setWrapperInfo (diagnostics only — the
|
|
581
706
|
// verifier consumes only the product). Keep in sync with package.json
|
|
582
707
|
// "version" on release.
|
|
583
|
-
private const val WRAPPER_VERSION = "0.0
|
|
708
|
+
private const val WRAPPER_VERSION = "0.1.0"
|
|
584
709
|
}
|
|
585
710
|
}
|
package/ios/BeekonRn.mm
CHANGED
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
}
|
|
36
36
|
onLicenseStatus:^(NSDictionary *_Nonnull ls) {
|
|
37
37
|
[weakSelf emitOnLicenseStatus:ls];
|
|
38
|
+
}
|
|
39
|
+
onLog:^(NSDictionary *_Nonnull e) {
|
|
40
|
+
[weakSelf emitOnLog:e];
|
|
38
41
|
}];
|
|
39
42
|
}
|
|
40
43
|
return self;
|
|
@@ -147,6 +150,43 @@
|
|
|
147
150
|
[_impl licenseStatusWithResolver:resolve rejecter:reject];
|
|
148
151
|
}
|
|
149
152
|
|
|
153
|
+
- (void)getPermissionStatus:(RCTPromiseResolveBlock)resolve
|
|
154
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
155
|
+
[_impl getPermissionStatusWithResolver:resolve rejecter:reject];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// MARK: - Diagnostic logs
|
|
159
|
+
|
|
160
|
+
- (void)getLog:(double)fromMs
|
|
161
|
+
toMs:(double)toMs
|
|
162
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
163
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
164
|
+
[_impl getLogFromMs:fromMs toMs:toMs resolver:resolve rejecter:reject];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
- (void)exportLog:(RCTPromiseResolveBlock)resolve
|
|
168
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
169
|
+
[_impl exportLogWithResolver:resolve rejecter:reject];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
- (void)clearLog:(RCTPromiseResolveBlock)resolve
|
|
173
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
174
|
+
[_impl clearLogWithResolver:resolve rejecter:reject];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
- (void)setLogLevel:(NSString *)level
|
|
178
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
179
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
180
|
+
[_impl setLogLevel:level resolver:resolve rejecter:reject];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
- (void)log:(NSString *)level
|
|
184
|
+
message:(NSString *)message
|
|
185
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
186
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
187
|
+
[_impl logWithLevel:level message:message resolver:resolve rejecter:reject];
|
|
188
|
+
}
|
|
189
|
+
|
|
150
190
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
151
191
|
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
152
192
|
return std::make_shared<facebook::react::NativeBeekonRnSpecJSI>(params);
|
package/ios/BeekonRn.swift
CHANGED
|
@@ -24,6 +24,7 @@ import BeekonKit
|
|
|
24
24
|
private let onSyncStatusCb: (NSDictionary) -> Void
|
|
25
25
|
private let onAuthTokensCb: (NSDictionary) -> Void
|
|
26
26
|
private let onLicenseStatusCb: (NSDictionary) -> Void
|
|
27
|
+
private let onLogCb: (NSDictionary) -> Void
|
|
27
28
|
|
|
28
29
|
private var stateTask: Task<Void, Never>?
|
|
29
30
|
private var locationsTask: Task<Void, Never>?
|
|
@@ -31,11 +32,12 @@ import BeekonKit
|
|
|
31
32
|
private var syncStatusTask: Task<Void, Never>?
|
|
32
33
|
private var authTokensTask: Task<Void, Never>?
|
|
33
34
|
private var licenseStatusTask: Task<Void, Never>?
|
|
35
|
+
private var logsTask: Task<Void, Never>?
|
|
34
36
|
|
|
35
37
|
// Reported to the native verifier via setWrapperInfo (diagnostics only — the
|
|
36
38
|
// verifier consumes only the product). Keep in sync with package.json
|
|
37
39
|
// "version" on release.
|
|
38
|
-
private static let wrapperVersion = "0.0
|
|
40
|
+
private static let wrapperVersion = "0.1.0"
|
|
39
41
|
|
|
40
42
|
@objc public init(
|
|
41
43
|
onState: @escaping (NSDictionary) -> Void,
|
|
@@ -43,7 +45,8 @@ import BeekonKit
|
|
|
43
45
|
onGeofenceEvent: @escaping (NSDictionary) -> Void,
|
|
44
46
|
onSyncStatus: @escaping (NSDictionary) -> Void,
|
|
45
47
|
onAuthTokens: @escaping (NSDictionary) -> Void,
|
|
46
|
-
onLicenseStatus: @escaping (NSDictionary) -> Void
|
|
48
|
+
onLicenseStatus: @escaping (NSDictionary) -> Void,
|
|
49
|
+
onLog: @escaping (NSDictionary) -> Void
|
|
47
50
|
) {
|
|
48
51
|
// Identify this wrapper to the native verifier (license-format-v1 §11) before
|
|
49
52
|
// any configure() can run — a direct native call (nonisolated static),
|
|
@@ -55,6 +58,7 @@ import BeekonKit
|
|
|
55
58
|
self.onSyncStatusCb = onSyncStatus
|
|
56
59
|
self.onAuthTokensCb = onAuthTokens
|
|
57
60
|
self.onLicenseStatusCb = onLicenseStatus
|
|
61
|
+
self.onLogCb = onLog
|
|
58
62
|
super.init()
|
|
59
63
|
self.stateTask = Task { [weak self] in
|
|
60
64
|
guard let self = self else { return }
|
|
@@ -92,6 +96,12 @@ import BeekonKit
|
|
|
92
96
|
self.onLicenseStatusCb(self.licenseStatusToWire(status))
|
|
93
97
|
}
|
|
94
98
|
}
|
|
99
|
+
self.logsTask = Task { [weak self] in
|
|
100
|
+
guard let self = self else { return }
|
|
101
|
+
for await entry in await Beekon.shared.logs {
|
|
102
|
+
self.onLogCb(self.logEntryToWire(entry))
|
|
103
|
+
}
|
|
104
|
+
}
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
@objc public func invalidate() {
|
|
@@ -101,12 +111,14 @@ import BeekonKit
|
|
|
101
111
|
syncStatusTask?.cancel()
|
|
102
112
|
authTokensTask?.cancel()
|
|
103
113
|
licenseStatusTask?.cancel()
|
|
114
|
+
logsTask?.cancel()
|
|
104
115
|
stateTask = nil
|
|
105
116
|
locationsTask = nil
|
|
106
117
|
geofenceEventsTask = nil
|
|
107
118
|
syncStatusTask = nil
|
|
108
119
|
authTokensTask = nil
|
|
109
120
|
licenseStatusTask = nil
|
|
121
|
+
logsTask = nil
|
|
110
122
|
}
|
|
111
123
|
|
|
112
124
|
/// Register Beekon's background-refresh task and install cold-launch hooks.
|
|
@@ -123,11 +135,22 @@ import BeekonKit
|
|
|
123
135
|
@objc public func configure(
|
|
124
136
|
_ config: NSDictionary,
|
|
125
137
|
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
126
|
-
rejecter
|
|
138
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
127
139
|
) {
|
|
140
|
+
// GATED on beekon native 0.0.9 (sealed BeekonConfig) — does not compile
|
|
141
|
+
// against the pinned 0.0.8 dep. `wireToConfig` now throws: the cloud arm
|
|
142
|
+
// (`try BeekonConfig.cloud(...)`) raises BeekonError.invalidConfiguration on
|
|
143
|
+
// an empty/invalid projectKey, which we surface via the existing reject path.
|
|
144
|
+
//
|
|
128
145
|
// Convert the non-Sendable NSDictionary to a Sendable BeekonConfig before
|
|
129
146
|
// crossing into the Task closure.
|
|
130
|
-
let cfg
|
|
147
|
+
let cfg: BeekonConfig
|
|
148
|
+
do {
|
|
149
|
+
cfg = try wireToConfig(config)
|
|
150
|
+
} catch {
|
|
151
|
+
reject(errorCode(error), error.localizedDescription, error)
|
|
152
|
+
return
|
|
153
|
+
}
|
|
131
154
|
Task {
|
|
132
155
|
await Beekon.shared.configure(cfg)
|
|
133
156
|
resolve(nil)
|
|
@@ -322,9 +345,104 @@ import BeekonKit
|
|
|
322
345
|
}
|
|
323
346
|
}
|
|
324
347
|
|
|
348
|
+
@objc public func getPermissionStatusWithResolver(
|
|
349
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
350
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
351
|
+
) {
|
|
352
|
+
// `getPermissionStatus()` is `nonisolated` + synchronous — no Task/await.
|
|
353
|
+
resolve(permissionStatusToWire(Beekon.shared.getPermissionStatus()))
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// MARK: - Diagnostic logs (spec diagnostics/log-format-v1)
|
|
357
|
+
|
|
358
|
+
@objc public func getLogFromMs(
|
|
359
|
+
_ fromMs: Double,
|
|
360
|
+
toMs: Double,
|
|
361
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
362
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
363
|
+
) {
|
|
364
|
+
Task { [weak self] in
|
|
365
|
+
guard let self = self else { return }
|
|
366
|
+
do {
|
|
367
|
+
let from = Date(timeIntervalSince1970: fromMs / 1000.0)
|
|
368
|
+
let to = Date(timeIntervalSince1970: toMs / 1000.0)
|
|
369
|
+
let entries = try await Beekon.shared.getLog(from: from, to: to)
|
|
370
|
+
resolve(entries.map { self.logEntryToWire($0) })
|
|
371
|
+
} catch {
|
|
372
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
@objc public func exportLogWithResolver(
|
|
378
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
379
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
380
|
+
) {
|
|
381
|
+
Task { [weak self] in
|
|
382
|
+
guard let self = self else { return }
|
|
383
|
+
do {
|
|
384
|
+
resolve(try await Beekon.shared.exportLog())
|
|
385
|
+
} catch {
|
|
386
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@objc public func clearLogWithResolver(
|
|
392
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
393
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
394
|
+
) {
|
|
395
|
+
Task { [weak self] in
|
|
396
|
+
guard let self = self else { return }
|
|
397
|
+
do {
|
|
398
|
+
try await Beekon.shared.clearLog()
|
|
399
|
+
resolve(nil)
|
|
400
|
+
} catch {
|
|
401
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@objc public func setLogLevel(
|
|
407
|
+
_ level: String,
|
|
408
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
409
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
410
|
+
) {
|
|
411
|
+
// setLogLevel is nonisolated on the actor — callable synchronously.
|
|
412
|
+
Beekon.shared.setLogLevel(logLevelFromWire(level))
|
|
413
|
+
resolve(nil)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
@objc public func logWithLevel(
|
|
417
|
+
_ level: String,
|
|
418
|
+
message: String,
|
|
419
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
420
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
421
|
+
) {
|
|
422
|
+
Beekon.shared.log(logLevelFromWire(level), message)
|
|
423
|
+
resolve(nil)
|
|
424
|
+
}
|
|
425
|
+
|
|
325
426
|
// MARK: - Mappers: wire (NSDictionary/NSArray) → Beekon
|
|
326
427
|
|
|
327
|
-
|
|
428
|
+
// GATED on beekon native 0.0.9 (sealed BeekonConfig) — does not compile against
|
|
429
|
+
// the pinned 0.0.8 dep (the flat BeekonConfig(...) init). Switches on the wire
|
|
430
|
+
// `mode` (cloud-mode-v1 §2) to build the matching sealed arm. The cloud factory
|
|
431
|
+
// is throwing (`try BeekonConfig.cloud(...)`): an empty/invalid projectKey
|
|
432
|
+
// raises BeekonError.invalidConfiguration, propagated to configure()'s reject.
|
|
433
|
+
// iOS has NO `notification`, so the cloud arm omits it.
|
|
434
|
+
private func wireToConfig(_ d: NSDictionary) throws -> BeekonConfig {
|
|
435
|
+
if d["mode"] as? String == "cloud" {
|
|
436
|
+
let projectKey = (d["projectKey"] as? String) ?? ""
|
|
437
|
+
// Omitted endpoint → the baked-in Beekon Cloud default.
|
|
438
|
+
let endpoint = (d["endpoint"] as? String) ?? "https://api.getbeekon.com"
|
|
439
|
+
return try BeekonConfig.cloud(
|
|
440
|
+
projectKey: projectKey,
|
|
441
|
+
endpoint: endpoint,
|
|
442
|
+
logLevel: logLevelFromWire(d["logLevel"] as? String)
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
|
|
328
446
|
let minTime =
|
|
329
447
|
(d["minTimeBetweenLocationsSeconds"] as? NSNumber)?.doubleValue ?? 30
|
|
330
448
|
let minDist =
|
|
@@ -343,6 +461,7 @@ import BeekonKit
|
|
|
343
461
|
headers: entriesToDict((s["headers"] as? NSArray) ?? []),
|
|
344
462
|
intervalSeconds: (s["intervalSeconds"] as? NSNumber)?.doubleValue ?? 300,
|
|
345
463
|
batchSize: (s["batchSize"] as? NSNumber)?.intValue ?? 100,
|
|
464
|
+
syncThreshold: (s["syncThreshold"] as? NSNumber)?.intValue ?? 0,
|
|
346
465
|
auth: auth
|
|
347
466
|
)
|
|
348
467
|
} else {
|
|
@@ -352,8 +471,9 @@ import BeekonKit
|
|
|
352
471
|
}
|
|
353
472
|
}
|
|
354
473
|
|
|
355
|
-
// `notification` is Android-only — iOS ignores it
|
|
356
|
-
|
|
474
|
+
// `notification` is Android-only — iOS ignores it (the selfManaged factory
|
|
475
|
+
// takes no notification parameter on iOS).
|
|
476
|
+
return BeekonConfig.selfManaged(
|
|
357
477
|
minTimeBetweenLocationsSeconds: minTime,
|
|
358
478
|
minDistanceBetweenLocationsMeters: minDist,
|
|
359
479
|
accuracyMode: accuracyModeFromWire(d["accuracyMode"] as? String),
|
|
@@ -363,7 +483,8 @@ import BeekonKit
|
|
|
363
483
|
sync: sync,
|
|
364
484
|
// Passed through verbatim; nil/blank means unset — the SDK falls through to
|
|
365
485
|
// the Info.plist value, then evaluation (license-format-v1 §9).
|
|
366
|
-
licenseKey: d["licenseKey"] as? String
|
|
486
|
+
licenseKey: d["licenseKey"] as? String,
|
|
487
|
+
logLevel: logLevelFromWire(d["logLevel"] as? String)
|
|
367
488
|
)
|
|
368
489
|
}
|
|
369
490
|
|
|
@@ -471,6 +592,44 @@ import BeekonKit
|
|
|
471
592
|
]
|
|
472
593
|
}
|
|
473
594
|
|
|
595
|
+
private func logEntryToWire(_ e: LogEntry) -> NSDictionary {
|
|
596
|
+
return [
|
|
597
|
+
"id": e.id,
|
|
598
|
+
"timestampMs": e.timestamp.timeIntervalSince1970 * 1000.0,
|
|
599
|
+
"level": e.level.rawValue,
|
|
600
|
+
"category": e.category,
|
|
601
|
+
"message": e.message,
|
|
602
|
+
]
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
private func permissionStatusToWire(_ p: PermissionStatus) -> NSDictionary {
|
|
606
|
+
return [
|
|
607
|
+
"level": permissionLevelToWire(p.level),
|
|
608
|
+
// "" is the wire encoding of null (not granted).
|
|
609
|
+
"accuracy": p.accuracy.map { permissionAccuracyToWire($0) } ?? "",
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private func permissionLevelToWire(_ l: PermissionStatus.Level) -> String {
|
|
614
|
+
switch l {
|
|
615
|
+
case .notDetermined: return "notDetermined"
|
|
616
|
+
case .denied: return "denied"
|
|
617
|
+
case .restricted: return "restricted"
|
|
618
|
+
case .foreground: return "foreground"
|
|
619
|
+
case .background: return "background"
|
|
620
|
+
// BeekonKit enums are non-frozen (library-evolution binary); handle unknowns.
|
|
621
|
+
@unknown default: return "notDetermined"
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private func permissionAccuracyToWire(_ a: PermissionStatus.Accuracy) -> String {
|
|
626
|
+
switch a {
|
|
627
|
+
case .full: return "full"
|
|
628
|
+
case .reduced: return "reduced"
|
|
629
|
+
@unknown default: return "full"
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
474
633
|
private func stateToWire(_ s: BeekonState) -> NSDictionary {
|
|
475
634
|
switch s {
|
|
476
635
|
case .idle:
|
|
@@ -590,12 +749,21 @@ import BeekonKit
|
|
|
590
749
|
}
|
|
591
750
|
}
|
|
592
751
|
|
|
752
|
+
// Wire token (LogLevel.rawValue, lowercase) -> LogLevel; unknown defaults to .info.
|
|
753
|
+
private func logLevelFromWire(_ s: String?) -> LogLevel {
|
|
754
|
+
LogLevel(rawValue: s ?? "") ?? .info
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// GATED on beekon native 0.0.9 (StopReason.cloudModeUnavailable) — the new case
|
|
758
|
+
// does not exist on the pinned 0.0.8 dep. (Non-frozen enum, so the existing
|
|
759
|
+
// `@unknown default` already keeps this compiling either way.)
|
|
593
760
|
private func stopReasonToWire(_ r: StopReason) -> String {
|
|
594
761
|
switch r {
|
|
595
762
|
case .user: return "user"
|
|
596
763
|
case .permissionDenied: return "permissionDenied"
|
|
597
764
|
case .locationServicesDisabled: return "locationServicesDisabled"
|
|
598
765
|
case .locationUnavailable: return "locationUnavailable"
|
|
766
|
+
case .cloudModeUnavailable: return "cloudModeUnavailable"
|
|
599
767
|
case .system: return "system"
|
|
600
768
|
@unknown default: return "system"
|
|
601
769
|
}
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>BeekonKit.framework/BeekonKit</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>BeekonKit.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
-
<string>x86_64</string>
|
|
18
17
|
</array>
|
|
19
18
|
<key>SupportedPlatform</key>
|
|
20
19
|
<string>ios</string>
|
|
21
|
-
<key>SupportedPlatformVariant</key>
|
|
22
|
-
<string>simulator</string>
|
|
23
20
|
</dict>
|
|
24
21
|
<dict>
|
|
25
22
|
<key>BinaryPath</key>
|
|
26
23
|
<string>BeekonKit.framework/BeekonKit</string>
|
|
27
24
|
<key>LibraryIdentifier</key>
|
|
28
|
-
<string>ios-
|
|
25
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
29
26
|
<key>LibraryPath</key>
|
|
30
27
|
<string>BeekonKit.framework</string>
|
|
31
28
|
<key>SupportedArchitectures</key>
|
|
32
29
|
<array>
|
|
33
30
|
<string>arm64</string>
|
|
31
|
+
<string>x86_64</string>
|
|
34
32
|
</array>
|
|
35
33
|
<key>SupportedPlatform</key>
|
|
36
34
|
<string>ios</string>
|
|
35
|
+
<key>SupportedPlatformVariant</key>
|
|
36
|
+
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
|
@@ -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.
|