react-native-test-adx 0.1.1 → 0.1.3
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/README.md +33 -19
- package/lib/module/AdaxonSdk.js +20 -9
- package/lib/module/components/AdaxonProvider.js +6 -3
- package/lib/module/config/ApiConstants.js +1 -1
- package/lib/module/config/StorageKeys.js +1 -1
- package/lib/module/core/AdaxonClient.js +42 -6
- package/lib/module/platform/network.js +20 -0
- package/lib/module/platform/reactNative.js +94 -39
- package/lib/module/services/device/DeviceInfoService.js +59 -10
- package/lib/module/services/event/EventBuilder.js +1 -2
- package/lib/module/services/event/EventService.js +0 -7
- package/lib/module/services/network/NetworkService.js +82 -0
- package/lib/typescript/src/AdaxonSdk.d.ts +2 -1
- package/lib/typescript/src/config/ApiConstants.d.ts +1 -1
- package/lib/typescript/src/core/AdaxonClient.d.ts +7 -1
- package/lib/typescript/src/platform/network.d.ts +1 -0
- package/lib/typescript/src/platform/reactNative.d.ts +2 -1
- package/lib/typescript/src/platform/types.d.ts +5 -0
- package/lib/typescript/src/services/device/DeviceInfoService.d.ts +11 -1
- package/lib/typescript/src/services/event/EventBuilder.d.ts +0 -2
- package/lib/typescript/src/services/event/EventService.d.ts +1 -2
- package/lib/typescript/src/services/network/NetworkService.d.ts +17 -0
- package/package.json +1 -1
- package/lib/module/storage/AnonymousIdService.js +0 -31
- package/lib/typescript/src/storage/AnonymousIdService.d.ts +0 -11
package/README.md
CHANGED
|
@@ -36,12 +36,6 @@ import { AdaxonProvider, useAdaxon } from 'react-native-test-adx';
|
|
|
36
36
|
|
|
37
37
|
Both packages expose the same API. Only the npm name differs.
|
|
38
38
|
|
|
39
|
-
Recommended storage helper:
|
|
40
|
-
|
|
41
|
-
```sh
|
|
42
|
-
npx expo install expo-secure-store
|
|
43
|
-
```
|
|
44
|
-
|
|
45
39
|
## Quick start (React Native)
|
|
46
40
|
|
|
47
41
|
Wrap the app in `AdaxonProvider`. Screens use `useAdaxon()`.
|
|
@@ -56,12 +50,12 @@ The SDK owns the tracker host and API paths (`/event/tracking`, `/api/mts/recomm
|
|
|
56
50
|
|---|---|---|
|
|
57
51
|
| `appId` | **Yes** | None. MTS app id (`adx_…`). |
|
|
58
52
|
| `sdkKey` | **Yes** | None. Sent as the `x-sdk-key` header. |
|
|
59
|
-
| `storage` | No | In-memory
|
|
53
|
+
| `storage` | No | In-memory event queue and anonymous id. Offline events and the generated id are lost on app restart. |
|
|
60
54
|
| `timeoutMs` | No | `10000` |
|
|
61
55
|
| `testMode` | No | `false` (`sdk_version` gets a `-test` suffix when true) |
|
|
62
56
|
| `externalUserId` | No | Unset |
|
|
63
57
|
|
|
64
|
-
|
|
58
|
+
The SDK detects the device platform and collects device/app fields automatically. Do **not** pass a tracker URL — requests go to the SDK `BASE_URL`.
|
|
65
59
|
|
|
66
60
|
```tsx
|
|
67
61
|
import {
|
|
@@ -70,7 +64,6 @@ import {
|
|
|
70
64
|
useAdaxon,
|
|
71
65
|
type RecommendationOffer,
|
|
72
66
|
} from 'react-native-adaxon';
|
|
73
|
-
import * as SecureStore from 'expo-secure-store';
|
|
74
67
|
import { Button, Text, View } from 'react-native';
|
|
75
68
|
|
|
76
69
|
function App() {
|
|
@@ -80,11 +73,6 @@ function App() {
|
|
|
80
73
|
appId: 'adx_your_app_id',
|
|
81
74
|
sdkKey: 'your_sdk_key',
|
|
82
75
|
testMode: __DEV__,
|
|
83
|
-
storage: {
|
|
84
|
-
getItem: (key) => SecureStore.getItemAsync(key),
|
|
85
|
-
setItem: (key, value) => SecureStore.setItemAsync(key, value),
|
|
86
|
-
removeItem: (key) => SecureStore.deleteItemAsync(key),
|
|
87
|
-
},
|
|
88
76
|
}}
|
|
89
77
|
>
|
|
90
78
|
<HomeScreen />
|
|
@@ -156,6 +144,21 @@ function HomeScreen() {
|
|
|
156
144
|
|
|
157
145
|
Do **not** pass device or app fields. The SDK attaches `device` and `app` automatically on every event.
|
|
158
146
|
|
|
147
|
+
### Anonymous id
|
|
148
|
+
|
|
149
|
+
`user.anonymous_id` is generated by the SDK. It reads a unique id from React Native `NativeModules` when the host exposes one, otherwise it generates a UUID. The value is cached for the process and written to the configured `storage` adapter so the same id is reused across app launches. No third-party device-info package is required.
|
|
150
|
+
|
|
151
|
+
### Storage
|
|
152
|
+
|
|
153
|
+
`storage` is optional. It persists the **event queue** (offline / pending events) and the **anonymous id**. Only `appId` and `sdkKey` are required.
|
|
154
|
+
|
|
155
|
+
| What you pass | What happens |
|
|
156
|
+
|---|---|
|
|
157
|
+
| No `storage` | The queue and anonymous id stay in memory. Tracking still works. Both are lost if the app closes. |
|
|
158
|
+
| A storage adapter | Queued events and the anonymous id survive restart. |
|
|
159
|
+
|
|
160
|
+
The adapter is only `getItem` / `setItem` / `removeItem`. You do not implement the queue.
|
|
161
|
+
|
|
159
162
|
### Non-component boot
|
|
160
163
|
|
|
161
164
|
`Adaxon.initialize()` is still available if you are not inside a React tree. Prefer the provider in React Native apps.
|
|
@@ -218,16 +221,27 @@ POST /event/tracking
|
|
|
218
221
|
|
|
219
222
|
Rules:
|
|
220
223
|
|
|
221
|
-
- Pending events are stored through the `storage` adapter, so they survive app restart.
|
|
224
|
+
- Pending events are stored through the `storage` adapter when you pass one, so they survive app restart. If you omit `storage`, they stay in memory only.
|
|
222
225
|
- Events are processed in creation order (FIFO).
|
|
223
226
|
- Each request contains at most **100** events. A queue of 250 is sent as 100 + 100 + 50.
|
|
224
227
|
- Events are removed only after the API confirms success (`success: true`). On network, HTTP, or unknown errors they stay in the queue.
|
|
225
228
|
- Only one processor runs at a time for the same app. A second `processPendingEvents()` call returns `already_processing`.
|
|
226
229
|
- Large queues are read 100 events at a time. The SDK does not load the whole queue into memory.
|
|
227
|
-
- On `Adaxon.initialize()` / `AdaxonProvider` mount, pending events start flushing in the background without blocking startup.
|
|
230
|
+
- On `Adaxon.initialize()` / `AdaxonProvider` mount, pending events start flushing in the background without blocking startup when the network is available.
|
|
228
231
|
- Pass `removeItem` on the storage adapter so successful events can be deleted. If it is omitted, the SDK writes an empty value instead.
|
|
229
232
|
|
|
230
|
-
|
|
233
|
+
## Network loss
|
|
234
|
+
|
|
235
|
+
The SDK watches connectivity through the host app's NetInfo / Expo Network listener. It does **not** poll.
|
|
236
|
+
|
|
237
|
+
- Online: `track` / `trackBatch` send immediately, same as before.
|
|
238
|
+
- Offline: events are stored in the event queue and no API request is made.
|
|
239
|
+
- Offline → online: the batch processor is notified once and pending events are flushed. Duplicate `ONLINE` events do not start extra processors.
|
|
240
|
+
- `isNetworkAvailable()` reports the current state. `destroy()` (and Provider unmount) removes the network listener.
|
|
241
|
+
|
|
242
|
+
A missing or failing network module does not crash the app. Tracking continues as online until the device is known to be offline.
|
|
243
|
+
|
|
244
|
+
Offline events use the same `storage` adapter as above. Without it, they are held in memory and are lost if the app is closed before the network returns.
|
|
231
245
|
|
|
232
246
|
## Automatic device information
|
|
233
247
|
|
|
@@ -253,7 +267,7 @@ Values come from React Native `Platform` (`src/platform/reactNative.ts`). They a
|
|
|
253
267
|
"app": {
|
|
254
268
|
"package_name": "com.example.app",
|
|
255
269
|
"app_version": "2.4.1",
|
|
256
|
-
"sdk_version": "0.1.
|
|
270
|
+
"sdk_version": "0.1.3"
|
|
257
271
|
}
|
|
258
272
|
}
|
|
259
273
|
```
|
|
@@ -289,7 +303,7 @@ MTS tracker
|
|
|
289
303
|
| `src/platform/` | Android/iOS fields. **Only `react-native` import** |
|
|
290
304
|
| `src/config/` | Tracker host, API paths, timeouts, storage key rules |
|
|
291
305
|
| `src/validation/` | Event name, property, and batch-size rules |
|
|
292
|
-
| `src/storage/` |
|
|
306
|
+
| `src/storage/` | Persistent event queue |
|
|
293
307
|
| `example/src/` | Demo app: `App.tsx`, `screens/`, `components/` |
|
|
294
308
|
|
|
295
309
|
Screens belong in the **host app**, not in the SDK.
|
package/lib/module/AdaxonSdk.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { AdaxonClient } from "./core/AdaxonClient.js";
|
|
4
4
|
import { MEDIATION_PROVIDER, OFFER_OPENED_EVENT } from "./config/ApiConstants.js";
|
|
5
5
|
import { AdaxonSdkError } from "./errors/AdaxonSdkError.js";
|
|
6
|
-
import { detectReactNativePlatform, reactNativeAppInfoReader, reactNativeDeviceReader, reactNativeNetworkInfoReader, reactNativeUrlOpener } from "./platform/reactNative.js";
|
|
6
|
+
import { detectReactNativePlatform, reactNativeAppInfoReader, reactNativeDeviceReader, reactNativeNetworkInfoReader, reactNativeNetworkStatusReader, reactNativeUrlOpener } from "./platform/reactNative.js";
|
|
7
7
|
import { buildAddToCartProperties, buildPurchaseProperties, normalizeEventValues } from "./services/event/normalizeProperties.js";
|
|
8
8
|
import { AdxEvent } from "./services/event/predefined.js";
|
|
9
9
|
export class AdaxonSdk {
|
|
@@ -18,18 +18,25 @@ export class AdaxonSdk {
|
|
|
18
18
|
}
|
|
19
19
|
async initialize(configuration, listener) {
|
|
20
20
|
try {
|
|
21
|
-
this.client
|
|
21
|
+
const previous = this.client;
|
|
22
|
+
const client = new AdaxonClient({
|
|
22
23
|
...configuration,
|
|
23
24
|
platform: detectReactNativePlatform(),
|
|
24
25
|
deviceInfoReader: reactNativeDeviceReader,
|
|
25
26
|
appInfoReader: reactNativeAppInfoReader,
|
|
26
|
-
networkInfoReader: reactNativeNetworkInfoReader
|
|
27
|
+
networkInfoReader: reactNativeNetworkInfoReader,
|
|
28
|
+
networkStatusReader: reactNativeNetworkStatusReader
|
|
27
29
|
});
|
|
30
|
+
await client.start();
|
|
31
|
+
previous?.destroy();
|
|
32
|
+
this.client = client;
|
|
28
33
|
this.testMode = Boolean(configuration.testMode);
|
|
29
34
|
listener?.onInitializationComplete();
|
|
30
|
-
|
|
35
|
+
if (client.isNetworkAvailable()) {
|
|
36
|
+
client.processPendingEvents().catch(() => undefined);
|
|
37
|
+
}
|
|
31
38
|
} catch (error) {
|
|
32
|
-
this.
|
|
39
|
+
this.destroy();
|
|
33
40
|
const message = AdaxonSdkError.messageOf(error, 'Initialization failed.');
|
|
34
41
|
const code = error instanceof AdaxonSdkError && error.code === 'INVALID_CONFIG' ? 'INVALID_CONFIG' : 'UNKNOWN';
|
|
35
42
|
listener?.onInitializationFailed(code, message);
|
|
@@ -107,14 +114,18 @@ export class AdaxonSdk {
|
|
|
107
114
|
setExternalUserId(externalUserId) {
|
|
108
115
|
this.requireClient().setExternalUserId(externalUserId);
|
|
109
116
|
}
|
|
117
|
+
isNetworkAvailable() {
|
|
118
|
+
return this.client?.isNetworkAvailable() ?? false;
|
|
119
|
+
}
|
|
120
|
+
destroy() {
|
|
121
|
+
this.client?.destroy();
|
|
122
|
+
this.client = null;
|
|
123
|
+
this.testMode = false;
|
|
124
|
+
}
|
|
110
125
|
requireClient() {
|
|
111
126
|
if (!this.client) {
|
|
112
127
|
throw new AdaxonSdkError('INVALID_CONFIG', 'Adaxon SDK is not initialized. Call Adaxon.initialize(...) first.');
|
|
113
128
|
}
|
|
114
129
|
return this.client;
|
|
115
130
|
}
|
|
116
|
-
reset() {
|
|
117
|
-
this.client = null;
|
|
118
|
-
this.testMode = false;
|
|
119
|
-
}
|
|
120
131
|
}
|
|
@@ -20,10 +20,12 @@ export function AdaxonProvider({
|
|
|
20
20
|
useEffect(() => {
|
|
21
21
|
let cancelled = false;
|
|
22
22
|
sdk.initialize(configurationRef.current, listenerRef.current).then(() => {
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (cancelled) {
|
|
24
|
+
sdk.destroy();
|
|
25
|
+
return;
|
|
26
26
|
}
|
|
27
|
+
setIsInitialized(true);
|
|
28
|
+
setInitializationError(null);
|
|
27
29
|
}).catch(error => {
|
|
28
30
|
if (!cancelled) {
|
|
29
31
|
setIsInitialized(false);
|
|
@@ -32,6 +34,7 @@ export function AdaxonProvider({
|
|
|
32
34
|
});
|
|
33
35
|
return () => {
|
|
34
36
|
cancelled = true;
|
|
37
|
+
sdk.destroy();
|
|
35
38
|
};
|
|
36
39
|
}, [sdk]);
|
|
37
40
|
const value = useMemo(() => ({
|
|
@@ -4,7 +4,7 @@ export const ANONYMOUS_ID_KEY_PREFIX = 'adaxon_sdk.anonymous_id.';
|
|
|
4
4
|
export const EVENT_QUEUE_META_KEY_PREFIX = 'adaxon_sdk.event_queue.meta.';
|
|
5
5
|
export const EVENT_QUEUE_ITEM_KEY_PREFIX = 'adaxon_sdk.event_queue.item.';
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// Queue adapters (and Keychain-backed stores) only accept alphanumerics
|
|
8
8
|
// plus `.`, `-` and `_`, so app ids are sanitized before use as a key.
|
|
9
9
|
const UNSAFE_STORAGE_KEY_CHARS = /[^A-Za-z0-9._-]/g;
|
|
10
10
|
export function sanitizeStorageId(appId) {
|
|
@@ -10,10 +10,11 @@ import { EventQueueService } from "../services/event/EventQueueService.js";
|
|
|
10
10
|
import { EventService } from "../services/event/EventService.js";
|
|
11
11
|
import { ApiClient } from "../services/network/ApiClient.js";
|
|
12
12
|
import { NetworkInfoService } from "../services/network/NetworkInfoService.js";
|
|
13
|
-
import {
|
|
13
|
+
import { NetworkService } from "../services/network/NetworkService.js";
|
|
14
14
|
import { EventQueueStorage } from "../storage/EventQueueStorage.js";
|
|
15
15
|
import { EventValidator } from "../validation/EventValidator.js";
|
|
16
16
|
export class AdaxonClient {
|
|
17
|
+
started = false;
|
|
17
18
|
constructor(config) {
|
|
18
19
|
const auth = new AuthenticationService(config);
|
|
19
20
|
this.validator = new EventValidator();
|
|
@@ -25,10 +26,9 @@ export class AdaxonClient {
|
|
|
25
26
|
const networkInfo = new NetworkInfoService(config.networkInfoReader);
|
|
26
27
|
this.builder = new EventBuilder({
|
|
27
28
|
appId: auth.appId,
|
|
28
|
-
deviceInfo: new DeviceInfoService(config.platform ?? 'web', config.deviceInfoReader),
|
|
29
|
+
deviceInfo: new DeviceInfoService(config.platform ?? 'web', config.deviceInfoReader, config.storage, auth.appId),
|
|
29
30
|
appInfo: new AppInfoService(Boolean(config.testMode), config.appInfoReader),
|
|
30
31
|
networkInfo,
|
|
31
|
-
anonymousId: new AnonymousIdService(auth.appId, config.storage),
|
|
32
32
|
validator: this.validator,
|
|
33
33
|
externalUserId: config.externalUserId
|
|
34
34
|
});
|
|
@@ -42,12 +42,48 @@ export class AdaxonClient {
|
|
|
42
42
|
queue: this.eventQueue,
|
|
43
43
|
sendBatch: payloads => this.events.sendPayloads(payloads)
|
|
44
44
|
});
|
|
45
|
+
this.network = new NetworkService(config.networkStatusReader);
|
|
46
|
+
}
|
|
47
|
+
async start() {
|
|
48
|
+
if (this.started) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.started = true;
|
|
52
|
+
await this.network.initialize();
|
|
53
|
+
this.network.subscribe(() => {
|
|
54
|
+
this.batchProcessor.process().catch(() => undefined);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
destroy() {
|
|
58
|
+
this.network.destroy();
|
|
59
|
+
this.started = false;
|
|
60
|
+
}
|
|
61
|
+
isNetworkAvailable() {
|
|
62
|
+
return this.network.isAvailable();
|
|
45
63
|
}
|
|
46
64
|
track(eventName, properties = {}, options = {}) {
|
|
47
|
-
return this.
|
|
65
|
+
return this.trackBatch([{
|
|
66
|
+
eventName,
|
|
67
|
+
properties,
|
|
68
|
+
...options
|
|
69
|
+
}]);
|
|
48
70
|
}
|
|
49
|
-
trackBatch(events) {
|
|
50
|
-
|
|
71
|
+
async trackBatch(events) {
|
|
72
|
+
if (this.network.isAvailable()) {
|
|
73
|
+
return this.events.trackBatch(events);
|
|
74
|
+
}
|
|
75
|
+
this.validator.validateBatch(events);
|
|
76
|
+
const payloads = await Promise.all(events.map(event => this.builder.build(event)));
|
|
77
|
+
for (const payload of payloads) {
|
|
78
|
+
await this.eventQueue.add(payload);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
message: 'queued',
|
|
83
|
+
accepted: payloads.length,
|
|
84
|
+
received: payloads.length,
|
|
85
|
+
rejected: 0
|
|
86
|
+
};
|
|
51
87
|
}
|
|
52
88
|
async enqueueEvent(eventName, properties = {}, options = {}) {
|
|
53
89
|
const payload = await this.builder.build({
|
|
@@ -13,6 +13,26 @@ function publicIpAddress(value) {
|
|
|
13
13
|
const ip = usableIpAddress(value);
|
|
14
14
|
return ip && !isPrivateIpAddress(ip) ? ip : undefined;
|
|
15
15
|
}
|
|
16
|
+
export function isOnlineFromNativeState(state) {
|
|
17
|
+
const value = asNativeNetworkState(state);
|
|
18
|
+
if (!value) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (value.isConnected === false || value.isInternetReachable === false) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const type = nonEmptyString(value.type)?.toLowerCase();
|
|
25
|
+
if (type === 'none') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (value.isConnected === true) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (type && type !== 'unknown') {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
16
36
|
export function connectionTypeFromNativeState(state) {
|
|
17
37
|
const type = nonEmptyString(asNativeNetworkState(state)?.type)?.toLowerCase();
|
|
18
38
|
return type && CONNECTION_TYPES.has(type) ? type : undefined;
|
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { Linking, NativeModules, Platform } from 'react-native';
|
|
3
|
+
import { Linking, NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
|
4
4
|
import { nonEmptyString, setIfPresent } from "../utils/strings.js";
|
|
5
5
|
import { readLocaleFields } from "../utils/locale.js";
|
|
6
6
|
import { appFieldsFromNative } from "./app.js";
|
|
7
7
|
import { deviceFieldsFromAndroid } from "./android.js";
|
|
8
8
|
import { deviceFieldsFromIos } from "./ios.js";
|
|
9
|
-
import { connectionTypeFromNativeState, ipAddressFromNativeState, usableIpAddress } from "./network.js";
|
|
9
|
+
import { connectionTypeFromNativeState, ipAddressFromNativeState, isOnlineFromNativeState, usableIpAddress } from "./network.js";
|
|
10
|
+
function nativeModule(name) {
|
|
11
|
+
return (NativeModules ?? {})[name];
|
|
12
|
+
}
|
|
13
|
+
async function callNative(method, arg) {
|
|
14
|
+
if (typeof method !== 'function') {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return await method(arg);
|
|
19
|
+
} catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function safeUnsubscribe(remove) {
|
|
24
|
+
return () => {
|
|
25
|
+
try {
|
|
26
|
+
remove?.();
|
|
27
|
+
} catch {
|
|
28
|
+
// Native unsubscribe can throw after the module is torn down.
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
10
32
|
export function detectReactNativePlatform() {
|
|
11
33
|
return Platform.OS === 'ios' || Platform.OS === 'android' ? Platform.OS : 'web';
|
|
12
34
|
}
|
|
@@ -27,52 +49,85 @@ function collectFromReactNative(platform) {
|
|
|
27
49
|
Object.assign(device, readLocaleFields());
|
|
28
50
|
return device;
|
|
29
51
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
readAppInfo: () => appFieldsFromNative(readNativeAppConstants())
|
|
35
|
-
};
|
|
36
|
-
export const reactNativeNetworkInfoReader = {
|
|
37
|
-
readNetworkInfo: collectFromNativeNetwork
|
|
38
|
-
};
|
|
39
|
-
export const reactNativeUrlOpener = Linking;
|
|
52
|
+
async function readUniqueIdFromNative() {
|
|
53
|
+
const deviceInfo = nativeModule('RNDeviceInfo');
|
|
54
|
+
return nonEmptyString(await callNative(deviceInfo?.getUniqueId)) ?? '';
|
|
55
|
+
}
|
|
40
56
|
function readNativeAppConstants() {
|
|
41
|
-
const modules = NativeModules ?? {};
|
|
42
|
-
const expoApp = modules.ExpoApplication;
|
|
43
|
-
const expoConstants = modules.ExponentConstants;
|
|
44
|
-
const deviceInfo = modules.RNDeviceInfo;
|
|
45
57
|
return {
|
|
46
|
-
...
|
|
47
|
-
...
|
|
48
|
-
...
|
|
58
|
+
...nativeModule('RNDeviceInfo'),
|
|
59
|
+
...nativeModule('ExponentConstants'),
|
|
60
|
+
...nativeModule('ExpoApplication')
|
|
49
61
|
};
|
|
50
62
|
}
|
|
51
|
-
|
|
63
|
+
function nativeNetworkModules() {
|
|
64
|
+
return {
|
|
65
|
+
netInfo: nativeModule('RNCNetInfo'),
|
|
66
|
+
expoNetwork: nativeModule('ExpoNetwork')
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function readIsOnlineFromNative() {
|
|
70
|
+
const {
|
|
71
|
+
netInfo,
|
|
72
|
+
expoNetwork
|
|
73
|
+
} = nativeNetworkModules();
|
|
74
|
+
const fromNetInfo = isOnlineFromNativeState(await callNative(netInfo?.getCurrentState, null));
|
|
75
|
+
if (fromNetInfo !== null) {
|
|
76
|
+
return fromNetInfo;
|
|
77
|
+
}
|
|
78
|
+
return isOnlineFromNativeState(await callNative(expoNetwork?.getNetworkStateAsync));
|
|
79
|
+
}
|
|
80
|
+
function subscribeNativeNetworkStatus(listener) {
|
|
81
|
+
const {
|
|
82
|
+
netInfo,
|
|
83
|
+
expoNetwork
|
|
84
|
+
} = nativeNetworkModules();
|
|
85
|
+
const onState = state => {
|
|
86
|
+
listener(isOnlineFromNativeState(state));
|
|
87
|
+
};
|
|
52
88
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
89
|
+
if (netInfo) {
|
|
90
|
+
const subscription = new NativeEventEmitter(NativeModules.RNCNetInfo).addListener('netInfo.networkStatusDidChange', onState);
|
|
91
|
+
return safeUnsubscribe(() => subscription.remove());
|
|
92
|
+
}
|
|
93
|
+
if (expoNetwork && typeof expoNetwork.addListener === 'function') {
|
|
94
|
+
const subscription = expoNetwork.addListener('onNetworkStateChanged', onState);
|
|
95
|
+
return safeUnsubscribe(() => subscription?.remove?.());
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('[Adaxon] Failed to register the network listener', error);
|
|
56
99
|
}
|
|
100
|
+
return () => undefined;
|
|
57
101
|
}
|
|
58
102
|
async function collectFromNativeNetwork() {
|
|
59
103
|
const network = {};
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
const {
|
|
105
|
+
netInfo,
|
|
106
|
+
expoNetwork
|
|
107
|
+
} = nativeNetworkModules();
|
|
108
|
+
const state = await callNative(netInfo?.getCurrentState, null);
|
|
109
|
+
setIfPresent(network, 'connection_type', connectionTypeFromNativeState(state));
|
|
110
|
+
setIfPresent(network, 'ip_address', ipAddressFromNativeState(state));
|
|
111
|
+
if (!network.connection_type) {
|
|
112
|
+
setIfPresent(network, 'connection_type', connectionTypeFromNativeState(await callNative(expoNetwork?.getNetworkStateAsync)));
|
|
68
113
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
setIfPresent(network, 'connection_type', connectionTypeFromNativeState(await readOptionalNative(getNetworkState)));
|
|
72
|
-
}
|
|
73
|
-
const getIpAddress = expoNetwork?.getIpAddressAsync;
|
|
74
|
-
if (!network.ip_address && typeof getIpAddress === 'function') {
|
|
75
|
-
setIfPresent(network, 'ip_address', usableIpAddress(nonEmptyString(await readOptionalNative(getIpAddress))));
|
|
114
|
+
if (!network.ip_address) {
|
|
115
|
+
setIfPresent(network, 'ip_address', usableIpAddress(nonEmptyString(await callNative(expoNetwork?.getIpAddressAsync))));
|
|
76
116
|
}
|
|
77
117
|
return network;
|
|
78
|
-
}
|
|
118
|
+
}
|
|
119
|
+
export const reactNativeDeviceReader = {
|
|
120
|
+
readDeviceInfo: collectFromReactNative,
|
|
121
|
+
getUniqueId: readUniqueIdFromNative
|
|
122
|
+
};
|
|
123
|
+
export const reactNativeAppInfoReader = {
|
|
124
|
+
readAppInfo: () => appFieldsFromNative(readNativeAppConstants())
|
|
125
|
+
};
|
|
126
|
+
export const reactNativeNetworkInfoReader = {
|
|
127
|
+
readNetworkInfo: collectFromNativeNetwork
|
|
128
|
+
};
|
|
129
|
+
export const reactNativeNetworkStatusReader = {
|
|
130
|
+
getIsOnline: readIsOnlineFromNative,
|
|
131
|
+
subscribe: subscribeNativeNetworkStatus
|
|
132
|
+
};
|
|
133
|
+
export const reactNativeUrlOpener = Linking;
|
|
@@ -1,22 +1,71 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
import { ANONYMOUS_ID_KEY_PREFIX, sanitizeStorageId } from "../../config/StorageKeys.js";
|
|
4
|
+
import { nonEmptyString } from "../../utils/strings.js";
|
|
5
|
+
import { createUuid } from "../../utils/uuid.js";
|
|
3
6
|
export class DeviceInfoService {
|
|
4
|
-
constructor(platform, reader) {
|
|
7
|
+
constructor(platform, reader, storage, appId) {
|
|
5
8
|
this.platform = platform;
|
|
6
9
|
this.reader = reader;
|
|
10
|
+
this.storage = storage;
|
|
11
|
+
this.anonymousIdKey = storage && appId ? `${ANONYMOUS_ID_KEY_PREFIX}${sanitizeStorageId(appId)}` : undefined;
|
|
7
12
|
}
|
|
8
13
|
getDeviceInfo() {
|
|
9
14
|
if (!this.cached) {
|
|
10
|
-
|
|
11
|
-
this.cached = this.reader?.readDeviceInfo(this.platform) ?? {
|
|
12
|
-
platform: this.platform
|
|
13
|
-
};
|
|
14
|
-
} catch {
|
|
15
|
-
this.cached = {
|
|
16
|
-
platform: this.platform
|
|
17
|
-
};
|
|
18
|
-
}
|
|
15
|
+
this.cached = this.readDeviceInfo();
|
|
19
16
|
}
|
|
20
17
|
return this.cached;
|
|
21
18
|
}
|
|
19
|
+
getUniqueId() {
|
|
20
|
+
if (!this.uniqueIdPromise) {
|
|
21
|
+
this.uniqueIdPromise = this.resolveUniqueId();
|
|
22
|
+
}
|
|
23
|
+
return this.uniqueIdPromise;
|
|
24
|
+
}
|
|
25
|
+
readDeviceInfo() {
|
|
26
|
+
const fallback = {
|
|
27
|
+
platform: this.platform
|
|
28
|
+
};
|
|
29
|
+
try {
|
|
30
|
+
return this.reader?.readDeviceInfo(this.platform) ?? fallback;
|
|
31
|
+
} catch {
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async resolveUniqueId() {
|
|
36
|
+
const stored = await this.readStoredId();
|
|
37
|
+
if (stored) {
|
|
38
|
+
return stored;
|
|
39
|
+
}
|
|
40
|
+
const uniqueId = (await this.readNativeId()) ?? createUuid();
|
|
41
|
+
await this.writeStoredId(uniqueId);
|
|
42
|
+
return uniqueId;
|
|
43
|
+
}
|
|
44
|
+
async readNativeId() {
|
|
45
|
+
try {
|
|
46
|
+
return nonEmptyString(await this.reader?.getUniqueId?.());
|
|
47
|
+
} catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async readStoredId() {
|
|
52
|
+
if (!this.storage || !this.anonymousIdKey) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return nonEmptyString(await this.storage.getItem(this.anonymousIdKey));
|
|
57
|
+
} catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async writeStoredId(uniqueId) {
|
|
62
|
+
if (!this.storage || !this.anonymousIdKey) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
await this.storage.setItem(this.anonymousIdKey, uniqueId);
|
|
67
|
+
} catch {
|
|
68
|
+
// Persistence must not block tracking.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
22
71
|
}
|
|
@@ -15,14 +15,13 @@ export class EventBuilder {
|
|
|
15
15
|
const {
|
|
16
16
|
validator,
|
|
17
17
|
appId,
|
|
18
|
-
anonymousId,
|
|
19
18
|
deviceInfo,
|
|
20
19
|
appInfo,
|
|
21
20
|
networkInfo
|
|
22
21
|
} = this.config;
|
|
23
22
|
validator.validateTrackInput(input);
|
|
24
23
|
const user = {
|
|
25
|
-
anonymous_id: await
|
|
24
|
+
anonymous_id: await deviceInfo.getUniqueId()
|
|
26
25
|
};
|
|
27
26
|
setIfPresent(user, 'external_user_id', nonEmptyString(this.externalUserId));
|
|
28
27
|
return {
|
|
@@ -8,13 +8,6 @@ export class EventService {
|
|
|
8
8
|
this.builder = config.builder;
|
|
9
9
|
this.validator = config.validator;
|
|
10
10
|
}
|
|
11
|
-
track(eventName, properties = {}, options = {}) {
|
|
12
|
-
return this.trackBatch([{
|
|
13
|
-
eventName,
|
|
14
|
-
properties,
|
|
15
|
-
...options
|
|
16
|
-
}]);
|
|
17
|
-
}
|
|
18
11
|
async trackBatch(events) {
|
|
19
12
|
this.validator.validateBatch(events);
|
|
20
13
|
const payloads = await Promise.all(events.map(event => this.builder.build(event)));
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function toState(isOnline) {
|
|
4
|
+
if (isOnline === true) {
|
|
5
|
+
return 'ONLINE';
|
|
6
|
+
}
|
|
7
|
+
if (isOnline === false) {
|
|
8
|
+
return 'OFFLINE';
|
|
9
|
+
}
|
|
10
|
+
return 'UNKNOWN';
|
|
11
|
+
}
|
|
12
|
+
export class NetworkService {
|
|
13
|
+
state = 'UNKNOWN';
|
|
14
|
+
started = false;
|
|
15
|
+
listeners = new Set();
|
|
16
|
+
constructor(reader) {
|
|
17
|
+
this.reader = reader;
|
|
18
|
+
}
|
|
19
|
+
getState() {
|
|
20
|
+
return this.state;
|
|
21
|
+
}
|
|
22
|
+
isAvailable() {
|
|
23
|
+
return this.state !== 'OFFLINE';
|
|
24
|
+
}
|
|
25
|
+
async initialize() {
|
|
26
|
+
if (this.started) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.started = true;
|
|
30
|
+
if (!this.reader) {
|
|
31
|
+
this.applyState('ONLINE');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
this.applyState(toState(await this.reader.getIsOnline()));
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('[Adaxon] Network monitoring failed', error);
|
|
38
|
+
this.applyState('UNKNOWN');
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
this.unsubscribeReader = this.reader.subscribe(isOnline => {
|
|
42
|
+
this.applyState(toState(isOnline));
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('[Adaxon] Network monitoring failed', error);
|
|
46
|
+
this.applyState('UNKNOWN');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
subscribe(listener) {
|
|
50
|
+
this.listeners.add(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
this.listeners.delete(listener);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
destroy() {
|
|
56
|
+
try {
|
|
57
|
+
this.unsubscribeReader?.();
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('[Adaxon] Failed to remove the network listener', error);
|
|
60
|
+
}
|
|
61
|
+
this.unsubscribeReader = undefined;
|
|
62
|
+
this.started = false;
|
|
63
|
+
this.listeners.clear();
|
|
64
|
+
}
|
|
65
|
+
applyState(next) {
|
|
66
|
+
const previous = this.state;
|
|
67
|
+
if (previous === next) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.state = next;
|
|
71
|
+
if (previous !== 'OFFLINE' || next !== 'ONLINE') {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (const listener of [...this.listeners]) {
|
|
75
|
+
try {
|
|
76
|
+
listener();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error('[Adaxon] Network listener failed', error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -22,7 +22,8 @@ export declare class AdaxonSdk {
|
|
|
22
22
|
loadRecommendations(eventName: string, properties?: EventProperties, listener?: AdaxonRecommendationLoadListener): Promise<RecommendationOffer[]>;
|
|
23
23
|
showOffer(offer: RecommendationOffer, listener?: AdaxonOfferShowListener): Promise<void>;
|
|
24
24
|
setExternalUserId(externalUserId?: string): void;
|
|
25
|
+
isNetworkAvailable(): boolean;
|
|
26
|
+
destroy(): void;
|
|
25
27
|
private requireClient;
|
|
26
|
-
private reset;
|
|
27
28
|
}
|
|
28
29
|
//# sourceMappingURL=AdaxonSdk.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppInfoReader, DeviceInfoReader, NetworkInfoReader } from '../platform/types.js';
|
|
1
|
+
import type { AppInfoReader, DeviceInfoReader, NetworkInfoReader, NetworkStatusReader } from '../platform/types.js';
|
|
2
2
|
import type { AdaxonSdkConfig } from '../types/AdaxonConfig.js';
|
|
3
3
|
import type { EventIngestionResponse, RecommendationResponse } from '../types/ApiTypes.js';
|
|
4
4
|
import type { AdaxonPlatform, ClickAttribution, EventProperties, TrackEventInput, TrackEventOptions } from '../types/EventTypes.js';
|
|
@@ -8,6 +8,7 @@ interface AdaxonClientConfig extends AdaxonSdkConfig {
|
|
|
8
8
|
deviceInfoReader?: DeviceInfoReader;
|
|
9
9
|
appInfoReader?: AppInfoReader;
|
|
10
10
|
networkInfoReader?: NetworkInfoReader;
|
|
11
|
+
networkStatusReader?: NetworkStatusReader;
|
|
11
12
|
}
|
|
12
13
|
export declare class AdaxonClient {
|
|
13
14
|
private readonly events;
|
|
@@ -15,7 +16,12 @@ export declare class AdaxonClient {
|
|
|
15
16
|
private readonly validator;
|
|
16
17
|
private readonly eventQueue;
|
|
17
18
|
private readonly batchProcessor;
|
|
19
|
+
private readonly network;
|
|
20
|
+
private started;
|
|
18
21
|
constructor(config: AdaxonClientConfig);
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
isNetworkAvailable(): boolean;
|
|
19
25
|
track(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
20
26
|
trackBatch(events: readonly TrackEventInput[]): Promise<EventIngestionResponse>;
|
|
21
27
|
enqueueEvent(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function isOnlineFromNativeState(state: unknown): boolean | null;
|
|
1
2
|
export declare function connectionTypeFromNativeState(state: unknown): string | undefined;
|
|
2
3
|
export declare function ipAddressFromNativeState(state: unknown): string | undefined;
|
|
3
4
|
export declare function usableIpAddress(value: string | undefined): string | undefined;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { AdaxonPlatform } from '../types/EventTypes.js';
|
|
2
|
-
import type { AppInfoReader, DeviceInfoReader, NetworkInfoReader, UrlOpener } from './types.js';
|
|
2
|
+
import type { AppInfoReader, DeviceInfoReader, NetworkInfoReader, NetworkStatusReader, UrlOpener } from './types.js';
|
|
3
3
|
export declare function detectReactNativePlatform(): AdaxonPlatform;
|
|
4
4
|
export declare const reactNativeDeviceReader: DeviceInfoReader;
|
|
5
5
|
export declare const reactNativeAppInfoReader: AppInfoReader;
|
|
6
6
|
export declare const reactNativeNetworkInfoReader: NetworkInfoReader;
|
|
7
|
+
export declare const reactNativeNetworkStatusReader: NetworkStatusReader;
|
|
7
8
|
export declare const reactNativeUrlOpener: UrlOpener;
|
|
8
9
|
//# sourceMappingURL=reactNative.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AdaxonAppContext, AdaxonDeviceContext, AdaxonNetworkContext, AdaxonPlatform } from '../types/EventTypes.js';
|
|
2
2
|
export interface DeviceInfoReader {
|
|
3
3
|
readDeviceInfo(platform: AdaxonPlatform): AdaxonDeviceContext;
|
|
4
|
+
getUniqueId?(): Promise<string>;
|
|
4
5
|
}
|
|
5
6
|
export interface AppInfoReader {
|
|
6
7
|
readAppInfo(): AdaxonAppContext;
|
|
@@ -12,4 +13,8 @@ export interface UrlOpener {
|
|
|
12
13
|
canOpenURL(url: string): Promise<boolean>;
|
|
13
14
|
openURL(url: string): Promise<void>;
|
|
14
15
|
}
|
|
16
|
+
export interface NetworkStatusReader {
|
|
17
|
+
getIsOnline(): Promise<boolean | null>;
|
|
18
|
+
subscribe(listener: (isOnline: boolean | null) => void): () => void;
|
|
19
|
+
}
|
|
15
20
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type { DeviceInfoReader } from '../../platform/types.js';
|
|
2
|
+
import type { AdaxonStorageAdapter } from '../../types/AdaxonConfig.js';
|
|
2
3
|
import type { AdaxonDeviceContext, AdaxonPlatform } from '../../types/EventTypes.js';
|
|
3
4
|
export declare class DeviceInfoService {
|
|
4
5
|
private readonly platform;
|
|
5
6
|
private readonly reader?;
|
|
7
|
+
private readonly storage?;
|
|
6
8
|
private cached?;
|
|
7
|
-
|
|
9
|
+
private uniqueIdPromise?;
|
|
10
|
+
private readonly anonymousIdKey?;
|
|
11
|
+
constructor(platform: AdaxonPlatform, reader?: DeviceInfoReader | undefined, storage?: AdaxonStorageAdapter | undefined, appId?: string);
|
|
8
12
|
getDeviceInfo(): AdaxonDeviceContext;
|
|
13
|
+
getUniqueId(): Promise<string>;
|
|
14
|
+
private readDeviceInfo;
|
|
15
|
+
private resolveUniqueId;
|
|
16
|
+
private readNativeId;
|
|
17
|
+
private readStoredId;
|
|
18
|
+
private writeStoredId;
|
|
9
19
|
}
|
|
10
20
|
//# sourceMappingURL=DeviceInfoService.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AppInfoService } from '../app/AppInfoService.js';
|
|
2
2
|
import type { DeviceInfoService } from '../device/DeviceInfoService.js';
|
|
3
3
|
import type { NetworkInfoService } from '../network/NetworkInfoService.js';
|
|
4
|
-
import type { AnonymousIdService } from '../../storage/AnonymousIdService.js';
|
|
5
4
|
import type { EventPayload, TrackEventInput } from '../../types/EventTypes.js';
|
|
6
5
|
import type { EventValidator } from '../../validation/EventValidator.js';
|
|
7
6
|
interface EventBuilderConfig {
|
|
@@ -9,7 +8,6 @@ interface EventBuilderConfig {
|
|
|
9
8
|
deviceInfo: DeviceInfoService;
|
|
10
9
|
appInfo: AppInfoService;
|
|
11
10
|
networkInfo: NetworkInfoService;
|
|
12
|
-
anonymousId: AnonymousIdService;
|
|
13
11
|
validator: EventValidator;
|
|
14
12
|
externalUserId?: string;
|
|
15
13
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ApiClient } from '../network/ApiClient.js';
|
|
2
2
|
import type { EventIngestionResponse, RecommendationResponse } from '../../types/ApiTypes.js';
|
|
3
|
-
import type { EventPayload,
|
|
3
|
+
import type { EventPayload, TrackEventInput } from '../../types/EventTypes.js';
|
|
4
4
|
import type { EventBuilder } from './EventBuilder.js';
|
|
5
5
|
import type { EventValidator } from '../../validation/EventValidator.js';
|
|
6
6
|
interface EventServiceConfig {
|
|
@@ -13,7 +13,6 @@ export declare class EventService {
|
|
|
13
13
|
private readonly builder;
|
|
14
14
|
private readonly validator;
|
|
15
15
|
constructor(config: EventServiceConfig);
|
|
16
|
-
track(eventName: string, properties?: EventProperties, options?: TrackEventOptions): Promise<EventIngestionResponse>;
|
|
17
16
|
trackBatch(events: readonly TrackEventInput[]): Promise<EventIngestionResponse>;
|
|
18
17
|
sendPayloads(payloads: readonly EventPayload[]): Promise<EventIngestionResponse>;
|
|
19
18
|
getRecommendations(input: TrackEventInput): Promise<RecommendationResponse>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NetworkStatusReader } from '../../platform/types.js';
|
|
2
|
+
export type NetworkState = 'ONLINE' | 'OFFLINE' | 'UNKNOWN';
|
|
3
|
+
export declare class NetworkService {
|
|
4
|
+
private readonly reader?;
|
|
5
|
+
private state;
|
|
6
|
+
private started;
|
|
7
|
+
private unsubscribeReader?;
|
|
8
|
+
private readonly listeners;
|
|
9
|
+
constructor(reader?: NetworkStatusReader | undefined);
|
|
10
|
+
getState(): NetworkState;
|
|
11
|
+
isAvailable(): boolean;
|
|
12
|
+
initialize(): Promise<void>;
|
|
13
|
+
subscribe(listener: () => void): () => void;
|
|
14
|
+
destroy(): void;
|
|
15
|
+
private applyState;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=NetworkService.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { ANONYMOUS_ID_KEY_PREFIX, sanitizeStorageId } from "../config/StorageKeys.js";
|
|
4
|
-
import { createUuid } from "../utils/uuid.js";
|
|
5
|
-
export class AnonymousIdService {
|
|
6
|
-
constructor(appId, storage) {
|
|
7
|
-
this.appId = appId;
|
|
8
|
-
this.storage = storage;
|
|
9
|
-
}
|
|
10
|
-
getAnonymousId() {
|
|
11
|
-
if (!this.anonymousIdPromise) {
|
|
12
|
-
this.anonymousIdPromise = this.loadOrCreate().catch(error => {
|
|
13
|
-
this.anonymousIdPromise = undefined;
|
|
14
|
-
throw error;
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return this.anonymousIdPromise;
|
|
18
|
-
}
|
|
19
|
-
get storageKey() {
|
|
20
|
-
return `${ANONYMOUS_ID_KEY_PREFIX}${sanitizeStorageId(this.appId)}`;
|
|
21
|
-
}
|
|
22
|
-
async loadOrCreate() {
|
|
23
|
-
const stored = await this.storage?.getItem(this.storageKey);
|
|
24
|
-
if (stored) {
|
|
25
|
-
return stored;
|
|
26
|
-
}
|
|
27
|
-
const anonymousId = createUuid();
|
|
28
|
-
await this.storage?.setItem(this.storageKey, anonymousId);
|
|
29
|
-
return anonymousId;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { AdaxonStorageAdapter } from '../types/AdaxonConfig.js';
|
|
2
|
-
export declare class AnonymousIdService {
|
|
3
|
-
private readonly appId;
|
|
4
|
-
private readonly storage?;
|
|
5
|
-
private anonymousIdPromise?;
|
|
6
|
-
constructor(appId: string, storage?: AdaxonStorageAdapter | undefined);
|
|
7
|
-
getAnonymousId(): Promise<string>;
|
|
8
|
-
private get storageKey();
|
|
9
|
-
private loadOrCreate;
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=AnonymousIdService.d.ts.map
|