@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 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
- - **Automatic Tracking** - App lifecycle, sessions, and screen views
9
- - **Offline Support** - Events are queued and sent when connection is available
10
- - **Privacy First** - No PII collected by default, opt-out support, GDPR-compliant data deletion
11
- - **Zero Configuration** - Sensible defaults for all settings
12
- - **TypeScript Support** - Full type definitions included
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. Use Descriptive Event Names
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
- * Only accepts API key - all other settings use defaults
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
- * Only accepts API key - all other settings use defaults
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 from storage (not configurable at init)
84
- this.userId = await storage_1.Storage.getUserId();
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
- * // Or as an object
19
- * Axyle.init({ apiKey: 'your-api-key' });
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
- * // Or as an object
23
- * Axyle.init({ apiKey: 'your-api-key' });
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 accepts only the API key
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axyle/expo-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Axyle Analytics SDK for Expo and React Native applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",