@wayq/beekon-rn 0.0.6 → 0.0.7

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 (46) hide show
  1. package/BeekonRn.podspec +1 -1
  2. package/CHANGELOG.md +24 -0
  3. package/android/build.gradle +3 -2
  4. package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +51 -0
  5. package/ios/BeekonRn.mm +10 -0
  6. package/ios/BeekonRn.swift +87 -2
  7. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
  8. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +1179 -28
  9. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  10. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +34 -1
  11. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
  12. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +1179 -28
  13. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  14. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +34 -1
  15. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +1179 -28
  16. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  17. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +34 -1
  18. package/lib/module/NativeBeekonRn.js +7 -0
  19. package/lib/module/NativeBeekonRn.js.map +1 -1
  20. package/lib/module/beekon.js +35 -1
  21. package/lib/module/beekon.js.map +1 -1
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/module/internal/mappers.js +48 -1
  24. package/lib/module/internal/mappers.js.map +1 -1
  25. package/lib/module/types/license.js +2 -0
  26. package/lib/module/types/license.js.map +1 -0
  27. package/lib/typescript/src/NativeBeekonRn.d.ts +36 -0
  28. package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
  29. package/lib/typescript/src/beekon.d.ts +15 -0
  30. package/lib/typescript/src/beekon.d.ts.map +1 -1
  31. package/lib/typescript/src/index.d.ts +1 -0
  32. package/lib/typescript/src/index.d.ts.map +1 -1
  33. package/lib/typescript/src/internal/mappers.d.ts +8 -1
  34. package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
  35. package/lib/typescript/src/types/config.d.ts +13 -0
  36. package/lib/typescript/src/types/config.d.ts.map +1 -1
  37. package/lib/typescript/src/types/license.d.ts +50 -0
  38. package/lib/typescript/src/types/license.d.ts.map +1 -0
  39. package/package.json +6 -2
  40. package/scripts/fetch-beekonkit.sh +4 -4
  41. package/src/NativeBeekonRn.ts +38 -0
  42. package/src/beekon.ts +38 -0
  43. package/src/index.tsx +1 -0
  44. package/src/internal/mappers.ts +50 -0
  45. package/src/types/config.ts +13 -0
  46. package/src/types/license.ts +47 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wayq/beekon-rn",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "React Native binding for the Beekon location SDK (Android + iOS).",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -38,7 +38,8 @@
38
38
  "fetch-beekonkit": "bash scripts/fetch-beekonkit.sh",
39
39
  "prepare": "yarn fetch-beekonkit && bob build",
40
40
  "typecheck": "tsc",
41
- "lint": "eslint \"**/*.{js,ts,tsx}\""
41
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
42
+ "test": "jest"
42
43
  },
43
44
  "keywords": [
44
45
  "react-native",
@@ -75,12 +76,15 @@
75
76
  "@eslint/js": "^10.0.1",
76
77
  "@react-native/babel-preset": "0.85.0",
77
78
  "@react-native/eslint-config": "0.85.0",
79
+ "@types/jest": "^29.5.14",
78
80
  "@types/react": "^19.2.0",
81
+ "babel-jest": "^29.7.0",
79
82
  "del-cli": "^7.0.0",
80
83
  "eslint": "^9.39.4",
81
84
  "eslint-config-prettier": "^10.1.8",
82
85
  "eslint-plugin-ft-flow": "^3.0.11",
83
86
  "eslint-plugin-prettier": "^5.5.5",
87
+ "jest": "^29.7.0",
84
88
  "prettier": "^3.8.1",
85
89
  "react": "19.2.3",
86
90
  "react-native": "0.85.0",
@@ -13,12 +13,12 @@
13
13
 
14
14
  set -euo pipefail
15
15
 
16
- VERSION="0.0.6"
16
+ VERSION="0.0.7"
17
17
  URL="https://github.com/wayqteam/beekon-ios-binary/releases/download/v${VERSION}/BeekonKit.xcframework.zip"
18
- # SHA256 of the v0.0.6 BeekonKit.xcframework.zip. Matches the SwiftPM
19
- # `binaryTarget` checksum in beekon-ios-binary's Package.swift at tag v0.0.6
18
+ # SHA256 of the v0.0.7 BeekonKit.xcframework.zip. Matches the SwiftPM
19
+ # `binaryTarget` checksum in beekon-ios-binary's Package.swift at tag v0.0.7
20
20
  # (SwiftPM's compute-checksum is the SHA256 of the zip).
21
- EXPECTED_SHA="eb0cd87c869cdbce5374569efa2d47f0f7e6c01ce18461c0653ef701163cf343"
21
+ EXPECTED_SHA="48803b540f063f609230f44a0f5b7faca125847db9f4628d0bf5d31d351a762b"
22
22
 
23
23
  ROOT="$(cd "$(dirname "$0")/.." && pwd)"
24
24
  DEST_DIR="${ROOT}/ios/Frameworks"
@@ -84,6 +84,13 @@ export type WireConfig = {
84
84
  sync?: WireSyncConfig;
85
85
  /** Android-only; the iOS native module ignores it. */
86
86
  notification?: WireNotificationConfig;
87
+ /**
88
+ * License token (license-format-v1 §9). Omitted/`undefined` means unset — the
89
+ * native SDK falls through to the manifest / Info.plist. The wrapper passes it
90
+ * through verbatim (no trimming or fallback). `configure` crosses as an untyped
91
+ * object, so this field documents the shape rather than altering codegen.
92
+ */
93
+ licenseKey?: string;
87
94
  };
88
95
 
89
96
  export type WireLocation = {
@@ -153,6 +160,29 @@ export type WireAuthTokens = {
153
160
  epoch: number;
154
161
  };
155
162
 
163
+ /**
164
+ * Flat wire form of the current platform's native license status
165
+ * (license-format-v1 §8). `status` is the wire-stable identifier; `tier` and
166
+ * `entitlements` are populated only when `status === 'licensed'` (null / empty
167
+ * otherwise), and `reason` only when `status === 'invalid'`.
168
+ */
169
+ export type WireLicenseStatus = {
170
+ /**
171
+ * One of: 'notDetermined' | 'licensed' | 'evaluation' | 'expired' |
172
+ * 'updateEntitlementLapsed' | 'invalid'.
173
+ */
174
+ status: string;
175
+ /** The opaque pricing tier; `null` unless `status === 'licensed'`. */
176
+ tier: string | null;
177
+ /** Opaque feature-gate strings; empty unless `status === 'licensed'`. */
178
+ entitlements: string[];
179
+ /**
180
+ * `null` unless `status === 'invalid'`. One of: 'malformed' | 'unknownKey' |
181
+ * 'badSignature' | 'appIdMismatch' | 'productMismatch' | 'unsupportedVersion'.
182
+ */
183
+ reason: string | null;
184
+ };
185
+
156
186
  export interface Spec extends TurboModule {
157
187
  /**
158
188
  * The config crosses as an untyped object (`ReadableMap` on Android,
@@ -195,11 +225,19 @@ export interface Spec extends TurboModule {
195
225
  removeGeofences(ids: string[]): Promise<void>;
196
226
  listGeofences(): Promise<WireGeofence[]>;
197
227
 
228
+ /**
229
+ * The current platform's native license status (license-format-v1 §8). Each
230
+ * native SDK validates independently; this reflects only the platform you are
231
+ * running on — no cross-platform merging.
232
+ */
233
+ licenseStatus(): Promise<WireLicenseStatus>;
234
+
198
235
  readonly onState: CodegenTypes.EventEmitter<WireState>;
199
236
  readonly onLocation: CodegenTypes.EventEmitter<WireLocation>;
200
237
  readonly onGeofenceEvent: CodegenTypes.EventEmitter<WireGeofenceEvent>;
201
238
  readonly onSyncStatus: CodegenTypes.EventEmitter<WireSyncStatus>;
202
239
  readonly onAuthTokens: CodegenTypes.EventEmitter<WireAuthTokens>;
240
+ readonly onLicenseStatus: CodegenTypes.EventEmitter<WireLicenseStatus>;
203
241
  }
204
242
 
205
243
  export default TurboModuleRegistry.getEnforcing<Spec>('BeekonRn');
package/src/beekon.ts CHANGED
@@ -3,6 +3,7 @@ import type { AuthTokens } from './types/auth';
3
3
  import type { BeekonConfig } from './types/config';
4
4
  import type { AccuracyMode } from './types/enums';
5
5
  import type { BeekonGeofence, GeofenceEvent } from './types/geofence';
6
+ import type { LicenseStatus } from './types/license';
6
7
  import type { Location } from './types/location';
7
8
  import type { BeekonState } from './types/state';
8
9
  import type { SyncStatus } from './types/sync';
@@ -14,6 +15,7 @@ import {
14
15
  wireToAuthTokens,
15
16
  wireToGeofence,
16
17
  wireToGeofenceEvent,
18
+ wireToLicenseStatus,
17
19
  wireToLocation,
18
20
  wireToState,
19
21
  wireToSyncStatus,
@@ -39,9 +41,11 @@ class EventHub {
39
41
  private readonly geofenceListeners = new Set<Listener<GeofenceEvent>>();
40
42
  private readonly syncListeners = new Set<Listener<SyncStatus>>();
41
43
  private readonly authTokenListeners = new Set<Listener<AuthTokens>>();
44
+ private readonly licenseListeners = new Set<Listener<LicenseStatus>>();
42
45
  private lastState: BeekonState | undefined;
43
46
  private lastSyncStatus: SyncStatus | undefined;
44
47
  private lastAuthTokens: AuthTokens | undefined;
48
+ private lastLicenseStatus: LicenseStatus | undefined;
45
49
 
46
50
  /** Idempotent: opens the native subscriptions the first time it is called. */
47
51
  ensureSubscribed(): void {
@@ -70,6 +74,11 @@ class EventHub {
70
74
  this.lastAuthTokens = t;
71
75
  this.authTokenListeners.forEach((cb) => cb(t));
72
76
  });
77
+ NativeBeekon.onLicenseStatus((w) => {
78
+ const s = wireToLicenseStatus(w);
79
+ this.lastLicenseStatus = s;
80
+ this.licenseListeners.forEach((cb) => cb(s));
81
+ });
73
82
  }
74
83
 
75
84
  onState(cb: Listener<BeekonState>): () => void {
@@ -114,6 +123,15 @@ class EventHub {
114
123
  this.authTokenListeners.delete(cb);
115
124
  };
116
125
  }
126
+
127
+ onLicenseStatus(cb: Listener<LicenseStatus>): () => void {
128
+ this.ensureSubscribed();
129
+ this.licenseListeners.add(cb);
130
+ if (this.lastLicenseStatus !== undefined) cb(this.lastLicenseStatus);
131
+ return () => {
132
+ this.licenseListeners.delete(cb);
133
+ };
134
+ }
117
135
  }
118
136
 
119
137
  /**
@@ -293,6 +311,16 @@ class BeekonImpl {
293
311
  return wires.map(wireToGeofence);
294
312
  }
295
313
 
314
+ /**
315
+ * The current platform's license status (license-format-v1 §8). Android and
316
+ * iOS validate independently — this reflects the platform you are running on,
317
+ * with no cross-platform merging. Purely observational: a license never blocks,
318
+ * degrades, or delays the SDK. For live transitions use {@link onLicenseStatus}.
319
+ */
320
+ async licenseStatus(): Promise<LicenseStatus> {
321
+ return wireToLicenseStatus(await NativeBeekon.licenseStatus());
322
+ }
323
+
296
324
  /**
297
325
  * Subscribe to tracking-state transitions (`idle` / `tracking` /
298
326
  * `stopped(reason)`). The current state is delivered to new subscribers
@@ -337,6 +365,16 @@ class BeekonImpl {
337
365
  onAuthTokens(cb: (t: AuthTokens) => void): () => void {
338
366
  return this.hub.onAuthTokens(cb);
339
367
  }
368
+
369
+ /**
370
+ * Subscribe to license-status transitions for the current platform
371
+ * (license-format-v1 §8). The current status is delivered to new subscribers
372
+ * immediately (replay-1), then again on each transition (the first validation
373
+ * is lazy). Purely observational. Returns an unsubscribe function.
374
+ */
375
+ onLicenseStatus(cb: (s: LicenseStatus) => void): () => void {
376
+ return this.hub.onLicenseStatus(cb);
377
+ }
340
378
  }
341
379
 
342
380
  export const Beekon = new BeekonImpl();
package/src/index.tsx CHANGED
@@ -24,4 +24,5 @@ export type {
24
24
  } from './types/geofence';
25
25
  export type { BeekonState, StopReason } from './types/state';
26
26
  export type { SyncStatus, SyncFailure } from './types/sync';
27
+ export type { LicenseStatus, LicenseInvalidReason } from './types/license';
27
28
  export { BeekonError, type BeekonErrorKind } from './types/error';
@@ -15,6 +15,7 @@ import type {
15
15
  GeofenceEvent,
16
16
  Transition,
17
17
  } from '../types/geofence';
18
+ import type { LicenseInvalidReason, LicenseStatus } from '../types/license';
18
19
  import type { Location } from '../types/location';
19
20
  import type { BeekonState, StopReason } from '../types/state';
20
21
  import type { SyncFailure, SyncStatus } from '../types/sync';
@@ -26,6 +27,7 @@ import type {
26
27
  WireGeofence,
27
28
  WireGeofenceEvent,
28
29
  WireKeyValue,
30
+ WireLicenseStatus,
29
31
  WireLocation,
30
32
  WireState,
31
33
  WireSyncStatus,
@@ -90,6 +92,10 @@ export function configToWire(config: BeekonConfig): WireConfig {
90
92
  smallIcon: config.notification.smallIcon,
91
93
  }
92
94
  : undefined,
95
+ // Passed through verbatim — `undefined` is omitted and the native SDK falls
96
+ // through to manifest/Info.plist. No wrapper-side trimming or fallback
97
+ // (blank/whitespace handling lives in the native SDK; spec §9).
98
+ licenseKey: config.licenseKey,
93
99
  };
94
100
  }
95
101
 
@@ -232,6 +238,50 @@ export function wireToAuthTokens(w: WireAuthTokens): AuthTokens {
232
238
  };
233
239
  }
234
240
 
241
+ /**
242
+ * Decode the wire-stable license status into the public discriminated union. The
243
+ * native side already decided the status (the wrapper performs no validation) —
244
+ * this only re-shapes the flat wire form and validates the enum strings.
245
+ */
246
+ export function wireToLicenseStatus(w: WireLicenseStatus): LicenseStatus {
247
+ switch (w.status) {
248
+ case 'licensed':
249
+ return {
250
+ status: 'licensed',
251
+ tier: w.tier ?? '',
252
+ entitlements: w.entitlements ?? [],
253
+ };
254
+ case 'invalid':
255
+ return {
256
+ status: 'invalid',
257
+ reason: oneOf<LicenseInvalidReason>(
258
+ w.reason,
259
+ [
260
+ 'malformed',
261
+ 'unknownKey',
262
+ 'badSignature',
263
+ 'appIdMismatch',
264
+ 'productMismatch',
265
+ 'unsupportedVersion',
266
+ ],
267
+ 'malformed'
268
+ ),
269
+ };
270
+ case 'evaluation':
271
+ return { status: 'evaluation' };
272
+ case 'expired':
273
+ return { status: 'expired' };
274
+ case 'updateEntitlementLapsed':
275
+ return { status: 'updateEntitlementLapsed' };
276
+ case 'notDetermined':
277
+ return { status: 'notDetermined' };
278
+ default:
279
+ // Forward-compat: an unknown status string means a native build newer than
280
+ // this wrapper — surface as notDetermined rather than throwing.
281
+ return { status: 'notDetermined' };
282
+ }
283
+ }
284
+
235
285
  function toStopReason(s: string | undefined): StopReason {
236
286
  return oneOf<StopReason>(
237
287
  s,
@@ -78,4 +78,17 @@ export type BeekonConfig = {
78
78
  sync?: SyncConfig;
79
79
  /** Android-only notification overrides. Ignored on iOS. */
80
80
  notification?: NotificationConfig;
81
+ /**
82
+ * Beekon license token (a `license-format-v1` JWS). The **highest-priority**
83
+ * supply channel (spec §9): it overrides the platform manifest value
84
+ * (`in.wayq.beekon.license` meta-data on Android, `BeekonLicenseKey` in
85
+ * `Info.plist` on iOS). `undefined`, blank, or whitespace-only means unset —
86
+ * the SDK falls through to the manifest, then to evaluation mode.
87
+ *
88
+ * The license **never blocks** the SDK; observe the outcome via
89
+ * {@link Beekon.licenseStatus} / {@link Beekon.onLicenseStatus}. Safe to commit
90
+ * to source control: a token is app-id-bound and product-bound, so it is
91
+ * useless to anyone else. Default: unset.
92
+ */
93
+ licenseKey?: string;
81
94
  };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Why an invalid license token failed verification (license-format-v1 §8.1).
3
+ * These are the exact wire-stable identifiers — the string names *are* the
4
+ * contract, shared verbatim with both native SDKs.
5
+ *
6
+ * - `'malformed'` — unparseable: bad encoding, structure, header, or claim types.
7
+ * - `'unknownKey'` — signed with a key this SDK build does not embed (update the SDK).
8
+ * - `'badSignature'` — the ES256 signature does not verify.
9
+ * - `'appIdMismatch'` — the license does not cover this application id.
10
+ * - `'productMismatch'` — the license does not cover this framework (here, `rn`).
11
+ * - `'unsupportedVersion'` — the token's claim schema is newer than this SDK understands.
12
+ */
13
+ export type LicenseInvalidReason =
14
+ | 'malformed'
15
+ | 'unknownKey'
16
+ | 'badSignature'
17
+ | 'appIdMismatch'
18
+ | 'productMismatch'
19
+ | 'unsupportedVersion';
20
+
21
+ /**
22
+ * License verification status for the **current platform** (license-format-v1
23
+ * §8). Purely observational — no status ever blocks, degrades, or delays the
24
+ * SDK. Use the `status` field as a discriminator.
25
+ *
26
+ * - `'notDetermined'` — before the first (lazy) validation completes.
27
+ * - `'evaluation'` — no license key supplied through any channel; running free.
28
+ * - `'expired'` — the license's expiry has passed.
29
+ * - `'updateEntitlementLapsed'` — this SDK build is newer than the license's
30
+ * covered update window. Still fully functional; renew to cover newer releases.
31
+ * - `'licensed'` — a valid license covering this app, framework, and SDK release;
32
+ * carries the opaque `tier` and `entitlements` (empty when the token carried none).
33
+ * - `'invalid'` — verification failed; see {@link LicenseInvalidReason}.
34
+ *
35
+ * **Platform divergence is legal** (spec §8.2): Android and iOS validate
36
+ * independently, so for one app the two platforms may report different statuses
37
+ * (e.g. a license scoped to `["android"]` reads `licensed` on Android and
38
+ * `invalid` / `productMismatch` on iOS). This value reflects only the platform
39
+ * you are running on; the wrapper never merges across platforms.
40
+ */
41
+ export type LicenseStatus =
42
+ | { status: 'notDetermined' }
43
+ | { status: 'evaluation' }
44
+ | { status: 'expired' }
45
+ | { status: 'updateEntitlementLapsed' }
46
+ | { status: 'licensed'; tier: string; entitlements: string[] }
47
+ | { status: 'invalid'; reason: LicenseInvalidReason };