@axyle/expo-sdk 1.0.0 → 1.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/README.md +263 -8
- package/dist/client.d.ts +2 -1
- package/dist/client.js +15 -4
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/types.d.ts +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,12 +4,13 @@ A privacy-focused, lightweight analytics SDK for Expo and React Native applicati
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Simple Integration** - One-line initialization with just an API key
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
7
|
+
- **[Simple Integration](#quick-start)** - One-line initialization with just an API key
|
|
8
|
+
- **[Flexible User Identification](#user-identification)** - Set user ID at init time or during runtime with `identify()`
|
|
9
|
+
- **[Automatic Tracking](#automatically-tracked-events)** - App lifecycle, sessions, and screen views
|
|
10
|
+
- **[Offline Support](#quick-start)** - Events are queued and sent when connection is available
|
|
11
|
+
- **[Privacy First](#privacy-methods)** - No PII collected by default, opt-out support, GDPR-compliant data deletion
|
|
12
|
+
- **[Zero Configuration](#configuration)** - Sensible defaults for all settings
|
|
13
|
+
- **[TypeScript Support](#typescript)** - Full type definitions included
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -44,6 +45,22 @@ export default function App() {
|
|
|
44
45
|
}
|
|
45
46
|
```
|
|
46
47
|
|
|
48
|
+
**For pre-authenticated users** (already logged in before app loads):
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
export default function App() {
|
|
52
|
+
useEffect(async () => {
|
|
53
|
+
const user = await getLoggedInUser();
|
|
54
|
+
Axyle.init({
|
|
55
|
+
apiKey: "your-api-key",
|
|
56
|
+
userId: user?.id, // Optional: set user ID at init
|
|
57
|
+
});
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
return <YourApp />;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
47
64
|
### 2. Track Custom Events
|
|
48
65
|
|
|
49
66
|
```tsx
|
|
@@ -80,7 +97,7 @@ Axyle.reset();
|
|
|
80
97
|
|
|
81
98
|
### Core Methods
|
|
82
99
|
|
|
83
|
-
#### `Axyle.init(apiKey)`
|
|
100
|
+
#### `Axyle.init(apiKey | config)`
|
|
84
101
|
|
|
85
102
|
Initialize the SDK. Must be called before any other methods.
|
|
86
103
|
|
|
@@ -90,8 +107,19 @@ Axyle.init("your-api-key");
|
|
|
90
107
|
|
|
91
108
|
// Or as an object
|
|
92
109
|
Axyle.init({ apiKey: "your-api-key" });
|
|
110
|
+
|
|
111
|
+
// With optional user ID (for pre-authenticated users)
|
|
112
|
+
Axyle.init({
|
|
113
|
+
apiKey: "your-api-key",
|
|
114
|
+
userId: "user-123",
|
|
115
|
+
});
|
|
93
116
|
```
|
|
94
117
|
|
|
118
|
+
**Parameters:**
|
|
119
|
+
|
|
120
|
+
- `apiKey` (string, required) - Your Axyle API key
|
|
121
|
+
- `userId` (string, optional) - Set user ID at initialization
|
|
122
|
+
|
|
95
123
|
#### `Axyle.track(eventName, properties?)`
|
|
96
124
|
|
|
97
125
|
Track a custom event with optional properties.
|
|
@@ -194,6 +222,154 @@ Get events filtered by type.
|
|
|
194
222
|
const buttonClicks = Axyle.getEventsByType("Button Clicked");
|
|
195
223
|
```
|
|
196
224
|
|
|
225
|
+
## User Identification
|
|
226
|
+
|
|
227
|
+
The SDK offers flexible user identification that works with different authentication patterns. You can set a user ID at initialization time or dynamically during runtime.
|
|
228
|
+
|
|
229
|
+
### Initialization with User ID
|
|
230
|
+
|
|
231
|
+
Set the user ID during SDK initialization for pre-authenticated users:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
export default function App() {
|
|
235
|
+
useEffect(async () => {
|
|
236
|
+
// Retrieve cached/stored user ID
|
|
237
|
+
const cachedUserId = await AsyncStorage.getItem("user_id");
|
|
238
|
+
|
|
239
|
+
// Initialize with optional user ID
|
|
240
|
+
Axyle.init({
|
|
241
|
+
apiKey: "your-api-key",
|
|
242
|
+
userId: cachedUserId, // Optional: pre-authenticated user
|
|
243
|
+
});
|
|
244
|
+
}, []);
|
|
245
|
+
|
|
246
|
+
return <YourApp />;
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**When to use:**
|
|
251
|
+
|
|
252
|
+
- User is already logged in when app starts
|
|
253
|
+
- You have a cached/stored user ID from previous session
|
|
254
|
+
- You want all events from app start to include user ID
|
|
255
|
+
|
|
256
|
+
**Result:** All events immediately have the correct user ID.
|
|
257
|
+
|
|
258
|
+
### Dynamic User Identification
|
|
259
|
+
|
|
260
|
+
Call `identify()` when user logs in during app runtime:
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
async function handleLogin(username, password) {
|
|
264
|
+
const user = await login(username, password);
|
|
265
|
+
|
|
266
|
+
// Set user ID and associate with previous anonymous events
|
|
267
|
+
Axyle.identify(user.id, {
|
|
268
|
+
username: user.username,
|
|
269
|
+
email: user.email,
|
|
270
|
+
loginMethod: "email",
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**When to use:**
|
|
276
|
+
|
|
277
|
+
- User starts anonymous and logs in later
|
|
278
|
+
- You want to transition from anonymous to identified tracking
|
|
279
|
+
- You need to capture user traits at login
|
|
280
|
+
|
|
281
|
+
**Result:** Generates "User Identified" event with traits; previous anonymous events are associated with new user ID.
|
|
282
|
+
|
|
283
|
+
### Comparing Init User ID vs identify()
|
|
284
|
+
|
|
285
|
+
| Scenario | Init `userId` | `identify()` |
|
|
286
|
+
| -------------------------------- | ----------------- | -------------------------------- |
|
|
287
|
+
| **Pre-authenticated users** | ✅ Best choice | Not needed |
|
|
288
|
+
| **Users logging in mid-session** | ❌ Too early | ✅ Use this |
|
|
289
|
+
| **App restart with cached user** | ✅ Convenient | Works but requires extra storage |
|
|
290
|
+
| **Anonymous to identified flow** | ❌ Not applicable | ✅ Perfect |
|
|
291
|
+
| **Traits/properties needed?** | ❌ No (just ID) | ✅ Yes (second param) |
|
|
292
|
+
| **Generates event?** | ❌ No | ✅ "User Identified" |
|
|
293
|
+
|
|
294
|
+
### Logout and Session Reset
|
|
295
|
+
|
|
296
|
+
Clear user data when user logs out:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
async function handleLogout() {
|
|
300
|
+
// Flush any pending events
|
|
301
|
+
await Axyle.flush();
|
|
302
|
+
|
|
303
|
+
// Clear user ID and start new session
|
|
304
|
+
Axyle.reset();
|
|
305
|
+
|
|
306
|
+
// Optional: disable tracking if user requested it
|
|
307
|
+
// Axyle.optOut();
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**What `reset()` does:**
|
|
312
|
+
|
|
313
|
+
- Clears the current user ID
|
|
314
|
+
- Starts a new session with new session ID
|
|
315
|
+
- Generates "Session Started" event for new session
|
|
316
|
+
- Keeps anonymous ID for continuity
|
|
317
|
+
|
|
318
|
+
### Complete Authentication Flow Example
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
322
|
+
import { Axyle } from "@axyle/expo-sdk";
|
|
323
|
+
|
|
324
|
+
export default function App() {
|
|
325
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
326
|
+
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
initializeAnalytics();
|
|
329
|
+
}, []);
|
|
330
|
+
|
|
331
|
+
const initializeAnalytics = async () => {
|
|
332
|
+
try {
|
|
333
|
+
// Try to restore user from previous session
|
|
334
|
+
const savedUserId = await AsyncStorage.getItem("user_id");
|
|
335
|
+
|
|
336
|
+
Axyle.init({
|
|
337
|
+
apiKey: "your-api-key",
|
|
338
|
+
userId: savedUserId || undefined, // Set if available
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
setIsInitialized(true);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error("Analytics init failed:", error);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const handleLogin = async (email, password) => {
|
|
348
|
+
const user = await authenticateUser(email, password);
|
|
349
|
+
|
|
350
|
+
// Save user ID for next app restart
|
|
351
|
+
await AsyncStorage.setItem("user_id", user.id);
|
|
352
|
+
|
|
353
|
+
// Identify user in analytics
|
|
354
|
+
Axyle.identify(user.id, {
|
|
355
|
+
email: user.email,
|
|
356
|
+
name: user.name,
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const handleLogout = async () => {
|
|
361
|
+
// Clear saved user ID
|
|
362
|
+
await AsyncStorage.removeItem("user_id");
|
|
363
|
+
|
|
364
|
+
// Reset analytics
|
|
365
|
+
await Axyle.flush();
|
|
366
|
+
Axyle.reset();
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
return isInitialized ? <YourApp /> : <LoadingScreen />;
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
197
373
|
## Screen Tracking
|
|
198
374
|
|
|
199
375
|
### Manual Screen Tracking (Recommended)
|
|
@@ -413,7 +589,44 @@ function App() {
|
|
|
413
589
|
}
|
|
414
590
|
```
|
|
415
591
|
|
|
416
|
-
### 2.
|
|
592
|
+
### 2. Handle User ID Correctly
|
|
593
|
+
|
|
594
|
+
**For pre-authenticated apps:**
|
|
595
|
+
|
|
596
|
+
```tsx
|
|
597
|
+
useEffect(() => {
|
|
598
|
+
(async () => {
|
|
599
|
+
const userId = await getStoredUserId();
|
|
600
|
+
Axyle.init({
|
|
601
|
+
apiKey: "your-api-key",
|
|
602
|
+
userId: userId,
|
|
603
|
+
});
|
|
604
|
+
})();
|
|
605
|
+
}, []);
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**For apps with login flow:**
|
|
609
|
+
|
|
610
|
+
```tsx
|
|
611
|
+
// Don't set user ID at init
|
|
612
|
+
Axyle.init("your-api-key");
|
|
613
|
+
|
|
614
|
+
// Set it when user logs in
|
|
615
|
+
const handleLogin = (user) => {
|
|
616
|
+
Axyle.identify(user.id, { email: user.email });
|
|
617
|
+
};
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**Always flush before logout:**
|
|
621
|
+
|
|
622
|
+
```tsx
|
|
623
|
+
const handleLogout = async () => {
|
|
624
|
+
await Axyle.flush(); // Ensure all events sent
|
|
625
|
+
Axyle.reset();
|
|
626
|
+
};
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### 3. Use Descriptive Event Names
|
|
417
630
|
|
|
418
631
|
```tsx
|
|
419
632
|
// Good
|
|
@@ -483,6 +696,48 @@ function SettingsScreen() {
|
|
|
483
696
|
3. Call `Axyle.flush()` to force send events
|
|
484
697
|
4. Check if user has opted out
|
|
485
698
|
|
|
699
|
+
### User ID Not Set
|
|
700
|
+
|
|
701
|
+
**Issue:** Events don't have the expected user ID
|
|
702
|
+
|
|
703
|
+
**Solutions:**
|
|
704
|
+
|
|
705
|
+
- Verify `userId` is passed to `Axyle.init()` or set via `Axyle.identify()`
|
|
706
|
+
- Check that `init()` is called before tracking events
|
|
707
|
+
- Use `Axyle.getSessionStats()` to verify user ID in current session
|
|
708
|
+
|
|
709
|
+
```tsx
|
|
710
|
+
const stats = Axyle.getSessionStats();
|
|
711
|
+
console.log("Current user ID:", stats?.userId); // Check if set correctly
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
### User ID Not Persisting Across App Restarts
|
|
715
|
+
|
|
716
|
+
**Issue:** User ID is lost when app restarts
|
|
717
|
+
|
|
718
|
+
**Solution:** Store and restore user ID from AsyncStorage
|
|
719
|
+
|
|
720
|
+
```tsx
|
|
721
|
+
// On login - save user ID
|
|
722
|
+
Axyle.identify(userId, traits);
|
|
723
|
+
await AsyncStorage.setItem("user_id", userId);
|
|
724
|
+
|
|
725
|
+
// On app start - restore user ID
|
|
726
|
+
const savedUserId = await AsyncStorage.getItem("user_id");
|
|
727
|
+
Axyle.init({
|
|
728
|
+
apiKey: "your-api-key",
|
|
729
|
+
userId: savedUserId,
|
|
730
|
+
});
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### User ID Changed But Events Still Use Old ID
|
|
734
|
+
|
|
735
|
+
**Issue:** After calling `identify()` with new user ID, some events still have old ID
|
|
736
|
+
|
|
737
|
+
**Likely cause:** Events were queued before `identify()` was called
|
|
738
|
+
|
|
739
|
+
**Solution:** This is expected behavior. Queued events retain their original user ID. Call `Axyle.flush()` after `identify()` to ensure new user ID is used for subsequent events.
|
|
740
|
+
|
|
486
741
|
### TypeScript Errors
|
|
487
742
|
|
|
488
743
|
Ensure peer dependencies are installed:
|
package/dist/client.d.ts
CHANGED
|
@@ -23,7 +23,8 @@ export declare class AxyleClient {
|
|
|
23
23
|
constructor();
|
|
24
24
|
/**
|
|
25
25
|
* Initialize the SDK
|
|
26
|
-
*
|
|
26
|
+
* Accepts API key (required) and optional user ID
|
|
27
|
+
* All other settings use defaults
|
|
27
28
|
*/
|
|
28
29
|
init(config: AxyleInitConfig): Promise<void>;
|
|
29
30
|
/**
|
package/dist/client.js
CHANGED
|
@@ -49,12 +49,14 @@ class AxyleClient {
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Initialize the SDK
|
|
52
|
-
*
|
|
52
|
+
* Accepts API key (required) and optional user ID
|
|
53
|
+
* All other settings use defaults
|
|
53
54
|
*/
|
|
54
55
|
async init(config) {
|
|
55
56
|
try {
|
|
56
|
-
// Extract API key from string or object
|
|
57
|
+
// Extract API key and user ID from string or object
|
|
57
58
|
const apiKey = typeof config === "string" ? config : config.apiKey;
|
|
59
|
+
const providedUserId = typeof config === "string" ? undefined : config.userId;
|
|
58
60
|
if (!apiKey) {
|
|
59
61
|
this.logger.warn("API key is required for SDK initialization");
|
|
60
62
|
}
|
|
@@ -80,8 +82,17 @@ class AxyleClient {
|
|
|
80
82
|
}
|
|
81
83
|
// Load anonymous ID
|
|
82
84
|
this.anonymousId = await storage_1.Storage.getAnonymousId();
|
|
83
|
-
// Load user ID
|
|
84
|
-
|
|
85
|
+
// Load or set user ID
|
|
86
|
+
if (providedUserId) {
|
|
87
|
+
// Use provided user ID (e.g., pre-authenticated user)
|
|
88
|
+
this.userId = providedUserId;
|
|
89
|
+
await storage_1.Storage.setUserId(providedUserId);
|
|
90
|
+
this.logger.log("User ID set from init config:", providedUserId);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Load user ID from storage (e.g., app restart with existing user)
|
|
94
|
+
this.userId = await storage_1.Storage.getUserId();
|
|
95
|
+
}
|
|
85
96
|
// Initialize session manager
|
|
86
97
|
this.sessionManager = new session_1.SessionManager(this.config.sessionTimeout, this.logger, {
|
|
87
98
|
onSessionStart: (sessionId) => this.handleSessionStart(sessionId),
|
package/dist/index.d.ts
CHANGED
|
@@ -8,15 +8,18 @@ import type { AnalyticsEvent, AxyleInitConfig, EventContext, EventProperties, Us
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const Axyle: {
|
|
10
10
|
/**
|
|
11
|
-
* Initialize the SDK with your API key
|
|
11
|
+
* Initialize the SDK with your API key and optional user ID
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```ts
|
|
15
15
|
* // Simple: just pass the API key as a string
|
|
16
16
|
* Axyle.init('your-api-key');
|
|
17
17
|
*
|
|
18
|
-
* //
|
|
19
|
-
* Axyle.init({
|
|
18
|
+
* // With optional user ID (e.g., pre-authenticated user)
|
|
19
|
+
* Axyle.init({
|
|
20
|
+
* apiKey: 'your-api-key',
|
|
21
|
+
* userId: 'user-123'
|
|
22
|
+
* });
|
|
20
23
|
* ```
|
|
21
24
|
*/
|
|
22
25
|
init: (config: AxyleInitConfig) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -12,15 +12,18 @@ const core_1 = require("./core");
|
|
|
12
12
|
*/
|
|
13
13
|
exports.Axyle = {
|
|
14
14
|
/**
|
|
15
|
-
* Initialize the SDK with your API key
|
|
15
|
+
* Initialize the SDK with your API key and optional user ID
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
19
|
* // Simple: just pass the API key as a string
|
|
20
20
|
* Axyle.init('your-api-key');
|
|
21
21
|
*
|
|
22
|
-
* //
|
|
23
|
-
* Axyle.init({
|
|
22
|
+
* // With optional user ID (e.g., pre-authenticated user)
|
|
23
|
+
* Axyle.init({
|
|
24
|
+
* apiKey: 'your-api-key',
|
|
25
|
+
* userId: 'user-123'
|
|
26
|
+
* });
|
|
24
27
|
* ```
|
|
25
28
|
*/
|
|
26
29
|
init: (config) => core_1.client.init(config),
|
package/dist/types.d.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* Core type definitions for Axyle Analytics SDK
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* SDK initialization
|
|
5
|
+
* SDK initialization config
|
|
6
|
+
* Accepts API key (required) and optional user ID
|
|
6
7
|
* All other settings use internal defaults
|
|
7
8
|
*/
|
|
8
9
|
export type AxyleInitConfig = string | {
|
|
9
10
|
apiKey: string;
|
|
11
|
+
userId?: string;
|
|
10
12
|
};
|
|
11
13
|
/**
|
|
12
14
|
* Internal configuration type with all settings
|