@wayq/beekon-rn 0.0.8 → 0.0.9
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 +1 -1
- package/CHANGELOG.md +30 -0
- package/README.md +67 -5
- package/android/build.gradle +1 -1
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +106 -8
- package/ios/BeekonRn.mm +35 -0
- package/ios/BeekonRn.swift +140 -8
- package/ios/Frameworks/BeekonKit.xcframework/Info.plist +5 -5
- 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 +6036 -3700
- 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 +95 -18
- 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 +6036 -3700
- 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 +95 -18
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +6036 -3700
- 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 +95 -18
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +1 -1
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +97 -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 +56 -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/typescript/src/NativeBeekonRn.d.ts +47 -2
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +46 -1
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -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 +3 -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/state.d.ts +4 -1
- package/lib/typescript/src/types/state.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/fetch-beekonkit.sh +4 -4
- package/src/NativeBeekonRn.ts +49 -2
- package/src/beekon.ts +100 -1
- package/src/index.tsx +7 -1
- package/src/internal/licenseNudge.ts +80 -0
- package/src/internal/mappers.ts +67 -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/state.ts +4 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LogLevel } from './enums';
|
|
2
|
+
/**
|
|
3
|
+
* A single diagnostic log record from the Beekon SDK.
|
|
4
|
+
*
|
|
5
|
+
* Delivered live on {@link Beekon.onLog} and read back from the persisted ring
|
|
6
|
+
* buffer via {@link Beekon.getLog}. Entries survive process death — the point of
|
|
7
|
+
* the buffer is post-hoc field diagnosis. Mirrors the native `LogEntry`; see
|
|
8
|
+
* `spec/diagnostics/log-format-v1`.
|
|
9
|
+
*/
|
|
10
|
+
export type LogEntry = {
|
|
11
|
+
/** UUIDv7 — orders and dedups entries. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** When the entry was recorded. */
|
|
14
|
+
timestamp: Date;
|
|
15
|
+
/** Severity. Always one of `'error'`…`'verbose'`. */
|
|
16
|
+
level: LogLevel;
|
|
17
|
+
/** Originating subsystem (e.g. `'location'`, `'sync'`); host breadcrumbs use `'app'`. */
|
|
18
|
+
category: string;
|
|
19
|
+
/** Human-readable, already-redacted text. */
|
|
20
|
+
message: string;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../../../src/types/log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,mCAAmC;IACnC,SAAS,EAAE,IAAI,CAAC;IAChB,qDAAqD;IACrD,KAAK,EAAE,QAAQ,CAAC;IAChB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
|
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
* (user-fixable in Settings).
|
|
9
9
|
* - `'locationUnavailable'` — no usable location backend on this device (e.g.
|
|
10
10
|
* Google Play Services absent/outdated on Android). Not user-fixable.
|
|
11
|
+
* - `'cloudModeUnavailable'` — the SDK was configured for cloud mode but the
|
|
12
|
+
* running build cannot operate it (cloud-mode-v1). Not user-fixable; switch to
|
|
13
|
+
* a self-managed config or a cloud-capable SDK build.
|
|
11
14
|
* - `'system'` — the OS terminated tracking (e.g. foreground-service kill,
|
|
12
15
|
* or an unrecoverable internal error).
|
|
13
16
|
*/
|
|
14
|
-
export type StopReason = 'user' | 'permissionDenied' | 'locationServicesDisabled' | 'locationUnavailable' | 'system';
|
|
17
|
+
export type StopReason = 'user' | 'permissionDenied' | 'locationServicesDisabled' | 'locationUnavailable' | 'cloudModeUnavailable' | 'system';
|
|
15
18
|
/**
|
|
16
19
|
* Tracking state. Mirrors the native `BeekonState` sealed type on both
|
|
17
20
|
* platforms. Use the `kind` field as a discriminator.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../src/types/state.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../src/types/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,kBAAkB,GAClB,0BAA0B,GAC1B,qBAAqB,GACrB,sBAAsB,GACtB,QAAQ,CAAC;AAEb;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
set -euo pipefail
|
|
15
15
|
|
|
16
|
-
VERSION="0.0.
|
|
16
|
+
VERSION="0.0.9"
|
|
17
17
|
URL="https://github.com/wayqteam/beekon-ios-binary/releases/download/v${VERSION}/BeekonKit.xcframework.zip"
|
|
18
|
-
# SHA256 of the v0.0.
|
|
19
|
-
# `binaryTarget` checksum in beekon-ios-binary's Package.swift at tag v0.0.
|
|
18
|
+
# SHA256 of the v0.0.9 BeekonKit.xcframework.zip. Matches the SwiftPM
|
|
19
|
+
# `binaryTarget` checksum in beekon-ios-binary's Package.swift at tag v0.0.9
|
|
20
20
|
# (SwiftPM's compute-checksum is the SHA256 of the zip).
|
|
21
|
-
EXPECTED_SHA="
|
|
21
|
+
EXPECTED_SHA="6f5e75bd90ef67c2e6466336edce4c3a33b5d44aa592fb5a6fa2b46eeab005e7"
|
|
22
22
|
|
|
23
23
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
24
24
|
DEST_DIR="${ROOT}/ios/Frameworks"
|
package/src/NativeBeekonRn.ts
CHANGED
|
@@ -59,6 +59,7 @@ export type WireSyncConfig = {
|
|
|
59
59
|
headers: WireKeyValue[];
|
|
60
60
|
intervalSeconds: number;
|
|
61
61
|
batchSize: number;
|
|
62
|
+
syncThreshold: number;
|
|
62
63
|
/** Token-refresh recipe; omitted keeps static-header auth. */
|
|
63
64
|
auth?: WireAuthConfig;
|
|
64
65
|
};
|
|
@@ -71,7 +72,27 @@ export type WireNotificationConfig = {
|
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
export type WireConfig = {
|
|
74
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Configuration-ownership discriminator (cloud-mode-v1 §2). One of: 'cloud' |
|
|
77
|
+
* 'selfManaged'. The native side switches on this to build the matching arm of
|
|
78
|
+
* the sealed `BeekonConfig`.
|
|
79
|
+
*/
|
|
80
|
+
mode: string;
|
|
81
|
+
/**
|
|
82
|
+
* Cloud arm only: the opaque project credential (`bkproj_…`). Omitted/empty for
|
|
83
|
+
* the self-managed arm; the native cloud arm trims it and rejects an empty key.
|
|
84
|
+
*/
|
|
85
|
+
projectKey?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Cloud arm only: base-URL override. Omitted means the native baked-in default
|
|
88
|
+
* (`https://api.getbeekon.com`). Ignored by the self-managed arm.
|
|
89
|
+
*/
|
|
90
|
+
endpoint?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Self-managed arm only (ignored by the cloud arm, which derives tracking
|
|
93
|
+
* config from the server). All fields required at the wire level — the TS
|
|
94
|
+
* facade applies defaults.
|
|
95
|
+
*/
|
|
75
96
|
minTimeBetweenLocationsSeconds: number;
|
|
76
97
|
minDistanceBetweenLocationsMeters: number;
|
|
77
98
|
/** One of: 'high' | 'balanced' | 'low'. */
|
|
@@ -91,6 +112,20 @@ export type WireConfig = {
|
|
|
91
112
|
* object, so this field documents the shape rather than altering codegen.
|
|
92
113
|
*/
|
|
93
114
|
licenseKey?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Diagnostic log verbosity threshold (spec `diagnostics/log-format-v1` §1).
|
|
117
|
+
* One of: 'off' | 'error' | 'warn' | 'info' | 'debug' | 'verbose'.
|
|
118
|
+
*/
|
|
119
|
+
logLevel: string;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type WireLogEntry = {
|
|
123
|
+
id: string;
|
|
124
|
+
timestampMs: number;
|
|
125
|
+
/** One of: 'error' | 'warn' | 'info' | 'debug' | 'verbose'. */
|
|
126
|
+
level: string;
|
|
127
|
+
category: string;
|
|
128
|
+
message: string;
|
|
94
129
|
};
|
|
95
130
|
|
|
96
131
|
export type WireLocation = {
|
|
@@ -137,7 +172,7 @@ export type WireState = {
|
|
|
137
172
|
/**
|
|
138
173
|
* Only populated when `type === 'stopped'`. One of: 'user' |
|
|
139
174
|
* 'permissionDenied' | 'locationServicesDisabled' | 'locationUnavailable' |
|
|
140
|
-
* 'system'.
|
|
175
|
+
* 'cloudModeUnavailable' | 'system'.
|
|
141
176
|
*/
|
|
142
177
|
stopReason?: string;
|
|
143
178
|
};
|
|
@@ -232,12 +267,24 @@ export interface Spec extends TurboModule {
|
|
|
232
267
|
*/
|
|
233
268
|
licenseStatus(): Promise<WireLicenseStatus>;
|
|
234
269
|
|
|
270
|
+
/** Persisted diagnostic log entries in `[fromMs, toMs]`, oldest first. */
|
|
271
|
+
getLog(fromMs: number, toMs: number): Promise<WireLogEntry[]>;
|
|
272
|
+
/** Serialize the whole log buffer to an NDJSON file; resolves its path. */
|
|
273
|
+
exportLog(): Promise<string>;
|
|
274
|
+
/** Delete all persisted diagnostic log entries. */
|
|
275
|
+
clearLog(): Promise<void>;
|
|
276
|
+
/** Set the diagnostic log threshold at runtime. */
|
|
277
|
+
setLogLevel(level: string): Promise<void>;
|
|
278
|
+
/** Write a host-app breadcrumb (category `app`) into the log pipeline. */
|
|
279
|
+
log(level: string, message: string): Promise<void>;
|
|
280
|
+
|
|
235
281
|
readonly onState: CodegenTypes.EventEmitter<WireState>;
|
|
236
282
|
readonly onLocation: CodegenTypes.EventEmitter<WireLocation>;
|
|
237
283
|
readonly onGeofenceEvent: CodegenTypes.EventEmitter<WireGeofenceEvent>;
|
|
238
284
|
readonly onSyncStatus: CodegenTypes.EventEmitter<WireSyncStatus>;
|
|
239
285
|
readonly onAuthTokens: CodegenTypes.EventEmitter<WireAuthTokens>;
|
|
240
286
|
readonly onLicenseStatus: CodegenTypes.EventEmitter<WireLicenseStatus>;
|
|
287
|
+
readonly onLog: CodegenTypes.EventEmitter<WireLogEntry>;
|
|
241
288
|
}
|
|
242
289
|
|
|
243
290
|
export default TurboModuleRegistry.getEnforcing<Spec>('BeekonRn');
|
package/src/beekon.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import NativeBeekon from './NativeBeekonRn';
|
|
2
|
+
import { LicenseNudge } from './internal/licenseNudge';
|
|
2
3
|
import type { AuthTokens } from './types/auth';
|
|
3
4
|
import type { BeekonConfig } from './types/config';
|
|
4
|
-
import type { AccuracyMode } from './types/enums';
|
|
5
|
+
import type { AccuracyMode, LogLevel } from './types/enums';
|
|
5
6
|
import type { BeekonGeofence, GeofenceEvent } from './types/geofence';
|
|
6
7
|
import type { LicenseStatus } from './types/license';
|
|
7
8
|
import type { Location } from './types/location';
|
|
9
|
+
import type { LogEntry } from './types/log';
|
|
8
10
|
import type { BeekonState } from './types/state';
|
|
9
11
|
import type { SyncStatus } from './types/sync';
|
|
10
12
|
import {
|
|
@@ -17,6 +19,7 @@ import {
|
|
|
17
19
|
wireToGeofenceEvent,
|
|
18
20
|
wireToLicenseStatus,
|
|
19
21
|
wireToLocation,
|
|
22
|
+
wireToLogEntry,
|
|
20
23
|
wireToState,
|
|
21
24
|
wireToSyncStatus,
|
|
22
25
|
} from './internal/mappers';
|
|
@@ -42,6 +45,7 @@ class EventHub {
|
|
|
42
45
|
private readonly syncListeners = new Set<Listener<SyncStatus>>();
|
|
43
46
|
private readonly authTokenListeners = new Set<Listener<AuthTokens>>();
|
|
44
47
|
private readonly licenseListeners = new Set<Listener<LicenseStatus>>();
|
|
48
|
+
private readonly logListeners = new Set<Listener<LogEntry>>();
|
|
45
49
|
private lastState: BeekonState | undefined;
|
|
46
50
|
private lastSyncStatus: SyncStatus | undefined;
|
|
47
51
|
private lastAuthTokens: AuthTokens | undefined;
|
|
@@ -79,6 +83,10 @@ class EventHub {
|
|
|
79
83
|
this.lastLicenseStatus = s;
|
|
80
84
|
this.licenseListeners.forEach((cb) => cb(s));
|
|
81
85
|
});
|
|
86
|
+
NativeBeekon.onLog((w) => {
|
|
87
|
+
const e = wireToLogEntry(w);
|
|
88
|
+
this.logListeners.forEach((cb) => cb(e));
|
|
89
|
+
});
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
onState(cb: Listener<BeekonState>): () => void {
|
|
@@ -132,6 +140,14 @@ class EventHub {
|
|
|
132
140
|
this.licenseListeners.delete(cb);
|
|
133
141
|
};
|
|
134
142
|
}
|
|
143
|
+
|
|
144
|
+
onLog(cb: Listener<LogEntry>): () => void {
|
|
145
|
+
this.ensureSubscribed();
|
|
146
|
+
this.logListeners.add(cb);
|
|
147
|
+
return () => {
|
|
148
|
+
this.logListeners.delete(cb);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
135
151
|
}
|
|
136
152
|
|
|
137
153
|
/**
|
|
@@ -154,6 +170,20 @@ class EventHub {
|
|
|
154
170
|
*/
|
|
155
171
|
class BeekonImpl {
|
|
156
172
|
private readonly hub = new EventHub();
|
|
173
|
+
private readonly licenseNudge = new LicenseNudge();
|
|
174
|
+
private licenseNudgeAttached = false;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Echo the evaluation-mode nudge to the JS console once, on first lifecycle
|
|
178
|
+
* use — so it only activates for apps actually using the SDK. Metro shows JS
|
|
179
|
+
* console only, so this is how the native nudge reaches an RN dev. See
|
|
180
|
+
* {@link LicenseNudge}.
|
|
181
|
+
*/
|
|
182
|
+
private activateLicenseNudge(): void {
|
|
183
|
+
if (this.licenseNudgeAttached) return;
|
|
184
|
+
this.licenseNudgeAttached = true;
|
|
185
|
+
this.licenseNudge.attach((cb) => this.hub.onLicenseStatus(cb));
|
|
186
|
+
}
|
|
157
187
|
|
|
158
188
|
/**
|
|
159
189
|
* Set tracking parameters. Optional — `start()` falls back to the previously
|
|
@@ -163,6 +193,7 @@ class BeekonImpl {
|
|
|
163
193
|
*/
|
|
164
194
|
async configure(config: BeekonConfig): Promise<void> {
|
|
165
195
|
this.hub.ensureSubscribed();
|
|
196
|
+
this.activateLicenseNudge();
|
|
166
197
|
await NativeBeekon.configure(configToWire(config));
|
|
167
198
|
}
|
|
168
199
|
|
|
@@ -174,6 +205,7 @@ class BeekonImpl {
|
|
|
174
205
|
*/
|
|
175
206
|
async start(): Promise<void> {
|
|
176
207
|
this.hub.ensureSubscribed();
|
|
208
|
+
this.activateLicenseNudge();
|
|
177
209
|
await NativeBeekon.start();
|
|
178
210
|
}
|
|
179
211
|
|
|
@@ -194,6 +226,7 @@ class BeekonImpl {
|
|
|
194
226
|
*/
|
|
195
227
|
async resumeIfNeeded(): Promise<void> {
|
|
196
228
|
this.hub.ensureSubscribed();
|
|
229
|
+
this.activateLicenseNudge();
|
|
197
230
|
await NativeBeekon.resumeIfNeeded();
|
|
198
231
|
}
|
|
199
232
|
|
|
@@ -321,6 +354,63 @@ class BeekonImpl {
|
|
|
321
354
|
return wireToLicenseStatus(await NativeBeekon.licenseStatus());
|
|
322
355
|
}
|
|
323
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Read persisted diagnostic log entries in the inclusive range `[from, to]`,
|
|
359
|
+
* oldest first. Entries survive process death, so this recovers what the SDK
|
|
360
|
+
* did during a background session — the key to diagnosing field issues. Throws
|
|
361
|
+
* `BeekonError` with kind `'storage'` on a read failure. Requires native ≥ 0.0.9.
|
|
362
|
+
*/
|
|
363
|
+
async getLog(from: Date, to: Date): Promise<LogEntry[]> {
|
|
364
|
+
try {
|
|
365
|
+
const wires = await NativeBeekon.getLog(from.getTime(), to.getTime());
|
|
366
|
+
return wires.map(wireToLogEntry);
|
|
367
|
+
} catch (e) {
|
|
368
|
+
rethrowAsBeekonError(e);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Serialize the whole diagnostic log buffer to an NDJSON file and resolve its
|
|
374
|
+
* path. The app owns the file — attach it to a support email, share it, or
|
|
375
|
+
* upload it. Throws `BeekonError` with kind `'storage'` on a failure. Requires
|
|
376
|
+
* native ≥ 0.0.9.
|
|
377
|
+
*/
|
|
378
|
+
async exportLog(): Promise<string> {
|
|
379
|
+
try {
|
|
380
|
+
return await NativeBeekon.exportLog();
|
|
381
|
+
} catch (e) {
|
|
382
|
+
rethrowAsBeekonError(e);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** Delete all persisted diagnostic log entries. Requires native ≥ 0.0.9. */
|
|
387
|
+
async clearLog(): Promise<void> {
|
|
388
|
+
try {
|
|
389
|
+
await NativeBeekon.clearLog();
|
|
390
|
+
} catch (e) {
|
|
391
|
+
rethrowAsBeekonError(e);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Set the diagnostic log verbosity threshold at runtime — no restart needed.
|
|
397
|
+
* Raise to `'debug'`/`'verbose'` for field diagnosis, then lower again. A later
|
|
398
|
+
* `configure` re-applies `BeekonConfig.logLevel` and supersedes this. Requires
|
|
399
|
+
* native ≥ 0.0.9.
|
|
400
|
+
*/
|
|
401
|
+
async setLogLevel(level: LogLevel): Promise<void> {
|
|
402
|
+
await NativeBeekon.setLogLevel(level);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Write a host-app breadcrumb into the same pipeline as the SDK's own logs
|
|
407
|
+
* (category `app`), so app and SDK events interleave in one timeline. Requires
|
|
408
|
+
* native ≥ 0.0.9.
|
|
409
|
+
*/
|
|
410
|
+
async log(level: LogLevel, message: string): Promise<void> {
|
|
411
|
+
await NativeBeekon.log(level, message);
|
|
412
|
+
}
|
|
413
|
+
|
|
324
414
|
/**
|
|
325
415
|
* Subscribe to tracking-state transitions (`idle` / `tracking` /
|
|
326
416
|
* `stopped(reason)`). The current state is delivered to new subscribers
|
|
@@ -375,6 +465,15 @@ class BeekonImpl {
|
|
|
375
465
|
onLicenseStatus(cb: (s: LicenseStatus) => void): () => void {
|
|
376
466
|
return this.hub.onLicenseStatus(cb);
|
|
377
467
|
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Subscribe to diagnostic log entries as they are recorded — SDK subsystems
|
|
471
|
+
* plus host breadcrumbs from {@link log}. Broadcast (no replay); read history
|
|
472
|
+
* with {@link getLog}. Returns an unsubscribe function. Requires native ≥ 0.0.9.
|
|
473
|
+
*/
|
|
474
|
+
onLog(cb: (e: LogEntry) => void): () => void {
|
|
475
|
+
return this.hub.onLog(cb);
|
|
476
|
+
}
|
|
378
477
|
}
|
|
379
478
|
|
|
380
479
|
export const Beekon = new BeekonImpl();
|
package/src/index.tsx
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export { Beekon } from './beekon';
|
|
2
2
|
|
|
3
|
+
// `BeekonConfig` is a merged type + value (the `cloud` / `selfManaged`
|
|
4
|
+
// factories), so it is a value export — its type meaning travels with it.
|
|
5
|
+
export { BeekonConfig } from './types/config';
|
|
3
6
|
export type {
|
|
4
|
-
|
|
7
|
+
CloudConfig,
|
|
8
|
+
SelfManagedConfig,
|
|
5
9
|
SyncConfig,
|
|
6
10
|
NotificationConfig,
|
|
7
11
|
} from './types/config';
|
|
@@ -14,7 +18,9 @@ export type {
|
|
|
14
18
|
ActivityType,
|
|
15
19
|
AuthStrategy,
|
|
16
20
|
AuthBodyFormat,
|
|
21
|
+
LogLevel,
|
|
17
22
|
} from './types/enums';
|
|
23
|
+
export type { LogEntry } from './types/log';
|
|
18
24
|
export type { AuthConfig, AuthResponseMapping, AuthTokens } from './types/auth';
|
|
19
25
|
export type { Location } from './types/location';
|
|
20
26
|
export type {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { LicenseStatus } from '../types/license';
|
|
2
|
+
|
|
3
|
+
// The debug nudge: a prominent multi-line banner mirroring the native debug
|
|
4
|
+
// banner. Every line carries the prefix (greppability). Neutral by design — no
|
|
5
|
+
// "release is unaffected", no advertised opt-out — so it nudges toward a key.
|
|
6
|
+
const BANNER = [
|
|
7
|
+
'[Beekon][license] ════════════════════════════════════════════════════',
|
|
8
|
+
'[Beekon][license] Beekon is running without a license key.',
|
|
9
|
+
'[Beekon][license] Get one: https://getbeekon.com/pricing',
|
|
10
|
+
'[Beekon][license] Then add it via BeekonConfig.licenseKey.',
|
|
11
|
+
'[Beekon][license] ════════════════════════════════════════════════════',
|
|
12
|
+
].join('\n');
|
|
13
|
+
|
|
14
|
+
function isDev(): boolean {
|
|
15
|
+
const g = globalThis as { __DEV__?: boolean };
|
|
16
|
+
return g.__DEV__ === true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function acknowledged(): boolean {
|
|
20
|
+
const g = globalThis as { BEEKON_EVALUATION_MODE?: boolean };
|
|
21
|
+
return g.BEEKON_EVALUATION_MODE === true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function defaultLog(message: string): void {
|
|
25
|
+
// The dev-console nudge: Metro surfaces JS console output, not native logs.
|
|
26
|
+
console.warn(message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Echoes the evaluation-mode license nudge into the JS console.
|
|
31
|
+
*
|
|
32
|
+
* The native SDKs log the license notice to the platform stream (iOS unified
|
|
33
|
+
* logging, Android Logcat), but Metro shows only JS `console.*` — neither native
|
|
34
|
+
* stream reaches it, so a React Native developer never sees the "you're
|
|
35
|
+
* unlicensed" nudge in the terminal they actually watch. This mirrors the
|
|
36
|
+
* **evaluation** notice (no key supplied) onto `console.warn` so it surfaces in
|
|
37
|
+
* Metro, on **both** platforms.
|
|
38
|
+
*
|
|
39
|
+
* Debug-only (`__DEV__`): a release bundle never console-warns. Fires **at most
|
|
40
|
+
* once per JS context** on the first `evaluation` status, mirroring the native
|
|
41
|
+
* once-per-transition guarantee. Purely a log line — it never blocks, degrades,
|
|
42
|
+
* or delays the SDK (license-format-v1 policy anchor).
|
|
43
|
+
*
|
|
44
|
+
* Silence it without a key by setting `globalThis.BEEKON_EVALUATION_MODE = true`
|
|
45
|
+
* before the SDK initializes — the JS analog of the native `BeekonEvaluationMode`
|
|
46
|
+
* plist / `in.wayq.beekon.evaluationMode` manifest acknowledgment.
|
|
47
|
+
*/
|
|
48
|
+
export class LicenseNudge {
|
|
49
|
+
private emitted = false;
|
|
50
|
+
private unsubscribe: (() => void) | undefined;
|
|
51
|
+
private readonly log: (message: string) => void;
|
|
52
|
+
private readonly enabled: boolean;
|
|
53
|
+
|
|
54
|
+
constructor(options?: {
|
|
55
|
+
log?: (message: string) => void;
|
|
56
|
+
enabled?: boolean;
|
|
57
|
+
}) {
|
|
58
|
+
this.log = options?.log ?? defaultLog;
|
|
59
|
+
this.enabled = options?.enabled ?? (isDev() && !acknowledged());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Subscribe via [subscribe] — a replay-1 license-status subscription that
|
|
64
|
+
* returns an unsubscribe function — and emit the banner the first time the
|
|
65
|
+
* status is `evaluation`. A no-op when disabled; idempotent.
|
|
66
|
+
*/
|
|
67
|
+
attach(subscribe: (cb: (status: LicenseStatus) => void) => () => void): void {
|
|
68
|
+
if (!this.enabled || this.unsubscribe !== undefined) return;
|
|
69
|
+
this.unsubscribe = subscribe((status) => this.onStatus(status));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private onStatus(status: LicenseStatus): void {
|
|
73
|
+
if (this.emitted || status.status !== 'evaluation') return;
|
|
74
|
+
this.emitted = true;
|
|
75
|
+
this.log(BANNER);
|
|
76
|
+
// One-shot per process — drop the subscription once nudged.
|
|
77
|
+
this.unsubscribe?.();
|
|
78
|
+
this.unsubscribe = undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/internal/mappers.ts
CHANGED
|
@@ -7,9 +7,11 @@ import type {
|
|
|
7
7
|
AuthStrategy,
|
|
8
8
|
LocationQuality,
|
|
9
9
|
LocationTrigger,
|
|
10
|
+
LogLevel,
|
|
10
11
|
MotionState,
|
|
11
12
|
StationaryMode,
|
|
12
13
|
} from '../types/enums';
|
|
14
|
+
import type { LogEntry } from '../types/log';
|
|
13
15
|
import type {
|
|
14
16
|
BeekonGeofence,
|
|
15
17
|
GeofenceEvent,
|
|
@@ -29,6 +31,7 @@ import type {
|
|
|
29
31
|
WireKeyValue,
|
|
30
32
|
WireLicenseStatus,
|
|
31
33
|
WireLocation,
|
|
34
|
+
WireLogEntry,
|
|
32
35
|
WireState,
|
|
33
36
|
WireSyncStatus,
|
|
34
37
|
} from '../NativeBeekonRn';
|
|
@@ -44,6 +47,7 @@ const DEFAULTS = {
|
|
|
44
47
|
detectActivity: false,
|
|
45
48
|
syncIntervalSeconds: 300,
|
|
46
49
|
syncBatchSize: 100,
|
|
50
|
+
syncThreshold: 0,
|
|
47
51
|
authStrategy: 'bearer' as AuthStrategy,
|
|
48
52
|
authBodyFormat: 'form' as AuthBodyFormat,
|
|
49
53
|
authSkewMarginSeconds: 60,
|
|
@@ -51,6 +55,7 @@ const DEFAULTS = {
|
|
|
51
55
|
string,
|
|
52
56
|
string
|
|
53
57
|
>,
|
|
58
|
+
logLevel: 'info' as LogLevel,
|
|
54
59
|
};
|
|
55
60
|
|
|
56
61
|
// --- Public → wire ---------------------------------------------------------
|
|
@@ -62,9 +67,53 @@ export function recordToEntries(
|
|
|
62
67
|
return Object.keys(record).map((key) => ({ key, value: record[key]! }));
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
function notificationToWire(config: BeekonConfig): WireConfig['notification'] {
|
|
71
|
+
return config.notification
|
|
72
|
+
? {
|
|
73
|
+
title: config.notification.title,
|
|
74
|
+
text: config.notification.text,
|
|
75
|
+
smallIcon: config.notification.smallIcon,
|
|
76
|
+
}
|
|
77
|
+
: undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Switches on the sealed-config arm (cloud-mode-v1 §2). The wire form is flat
|
|
81
|
+
// and carries both arms' slots; only the active arm's fields are populated. The
|
|
82
|
+
// tracking-param slots are filled with defaults regardless so the wire struct
|
|
83
|
+
// stays fully-populated — the native cloud arm ignores them and derives config
|
|
84
|
+
// from the server.
|
|
65
85
|
export function configToWire(config: BeekonConfig): WireConfig {
|
|
86
|
+
const tracking = {
|
|
87
|
+
minTimeBetweenLocationsSeconds: DEFAULTS.minTimeBetweenLocationsSeconds,
|
|
88
|
+
minDistanceBetweenLocationsMeters:
|
|
89
|
+
DEFAULTS.minDistanceBetweenLocationsMeters,
|
|
90
|
+
accuracyMode: DEFAULTS.accuracyMode,
|
|
91
|
+
whenStationary: DEFAULTS.whenStationary,
|
|
92
|
+
stationaryRadiusMeters: DEFAULTS.stationaryRadiusMeters,
|
|
93
|
+
detectActivity: DEFAULTS.detectActivity,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (config.mode === 'cloud') {
|
|
97
|
+
return {
|
|
98
|
+
mode: 'cloud',
|
|
99
|
+
// Passed through verbatim; the native cloud arm trims and validates it.
|
|
100
|
+
projectKey: config.projectKey,
|
|
101
|
+
// Omitted means the native baked-in default (https://api.getbeekon.com).
|
|
102
|
+
endpoint: config.endpoint,
|
|
103
|
+
...tracking,
|
|
104
|
+
// Cloud config is server-owned: no local sync or license key.
|
|
105
|
+
sync: undefined,
|
|
106
|
+
notification: notificationToWire(config),
|
|
107
|
+
licenseKey: undefined,
|
|
108
|
+
logLevel: config.logLevel ?? DEFAULTS.logLevel,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
66
112
|
const sync = config.sync;
|
|
67
113
|
return {
|
|
114
|
+
mode: 'selfManaged',
|
|
115
|
+
projectKey: undefined,
|
|
116
|
+
endpoint: undefined,
|
|
68
117
|
minTimeBetweenLocationsSeconds:
|
|
69
118
|
config.minTimeBetweenLocationsSeconds ??
|
|
70
119
|
DEFAULTS.minTimeBetweenLocationsSeconds,
|
|
@@ -82,20 +131,16 @@ export function configToWire(config: BeekonConfig): WireConfig {
|
|
|
82
131
|
headers: recordToEntries(sync.headers),
|
|
83
132
|
intervalSeconds: sync.intervalSeconds ?? DEFAULTS.syncIntervalSeconds,
|
|
84
133
|
batchSize: sync.batchSize ?? DEFAULTS.syncBatchSize,
|
|
134
|
+
syncThreshold: sync.syncThreshold ?? DEFAULTS.syncThreshold,
|
|
85
135
|
auth: sync.auth ? authToWire(sync.auth) : undefined,
|
|
86
136
|
}
|
|
87
137
|
: undefined,
|
|
88
|
-
notification: config
|
|
89
|
-
? {
|
|
90
|
-
title: config.notification.title,
|
|
91
|
-
text: config.notification.text,
|
|
92
|
-
smallIcon: config.notification.smallIcon,
|
|
93
|
-
}
|
|
94
|
-
: undefined,
|
|
138
|
+
notification: notificationToWire(config),
|
|
95
139
|
// Passed through verbatim — `undefined` is omitted and the native SDK falls
|
|
96
140
|
// through to manifest/Info.plist. No wrapper-side trimming or fallback
|
|
97
141
|
// (blank/whitespace handling lives in the native SDK; spec §9).
|
|
98
142
|
licenseKey: config.licenseKey,
|
|
143
|
+
logLevel: config.logLevel ?? DEFAULTS.logLevel,
|
|
99
144
|
};
|
|
100
145
|
}
|
|
101
146
|
|
|
@@ -201,6 +246,20 @@ export function wireToGeofenceEvent(w: WireGeofenceEvent): GeofenceEvent {
|
|
|
201
246
|
};
|
|
202
247
|
}
|
|
203
248
|
|
|
249
|
+
export function wireToLogEntry(w: WireLogEntry): LogEntry {
|
|
250
|
+
return {
|
|
251
|
+
id: w.id,
|
|
252
|
+
timestamp: new Date(w.timestampMs),
|
|
253
|
+
level: oneOf<LogLevel>(
|
|
254
|
+
w.level,
|
|
255
|
+
['off', 'error', 'warn', 'info', 'debug', 'verbose'],
|
|
256
|
+
'info'
|
|
257
|
+
),
|
|
258
|
+
category: w.category,
|
|
259
|
+
message: w.message,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
204
263
|
export function wireToState(w: WireState): BeekonState {
|
|
205
264
|
switch (w.type) {
|
|
206
265
|
case 'idle':
|
|
@@ -290,6 +349,7 @@ function toStopReason(s: string | undefined): StopReason {
|
|
|
290
349
|
'permissionDenied',
|
|
291
350
|
'locationServicesDisabled',
|
|
292
351
|
'locationUnavailable',
|
|
352
|
+
'cloudModeUnavailable',
|
|
293
353
|
'system',
|
|
294
354
|
],
|
|
295
355
|
// The native side always populates the reason for a `stopped` state; an
|