@bytem/bytem-tracker-app 0.0.7 → 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/README.md CHANGED
@@ -2,16 +2,12 @@
2
2
 
3
3
  This is the official Bytem Tracker SDK for React Native applications.
4
4
 
5
- ## Documentation
6
-
7
- - [Usage Guide](docs/USAGE.md): Detailed instructions on initialization, event tracking, and API usage.
8
-
9
5
  ## Installation
10
6
 
11
7
  Install the package and its peer dependencies using Yarn:
12
8
 
13
9
  ```bash
14
- yarn add @bytem/bytem-tracker-app @react-native-async-storage/async-storage react-native-device-info uuid
10
+ yarn add @bytem/bytem-tracker-app
15
11
  ```
16
12
 
17
13
  ### iOS Setup
@@ -20,47 +16,95 @@ Don't forget to install pods for iOS:
20
16
  cd ios && pod install
21
17
  ```
22
18
 
23
- ## Development
19
+ ## Usage
24
20
 
25
- This project uses **Yarn** for dependency management.
21
+ ### Initialization
22
+ Initialize the tracker in your app entry point (e.g., `App.tsx` or `index.js`). Note that `init` is asynchronous.
26
23
 
27
- ### Setup
24
+ ```typescript
25
+ import React, { useEffect } from 'react';
26
+ import BytemTracker from '@bytem/bytem-tracker-app';
28
27
 
29
- ```bash
30
- yarn install
31
- ```
28
+ const App = () => {
29
+ useEffect(() => {
30
+ const initTracker = async () => {
31
+ await BytemTracker.init({
32
+ appId: 'your-app-id',
33
+ endpoint: 'https://api.bytem.com/track',
34
+ debug: __DEV__, // Enable debug logs in development
35
+ });
36
+ };
32
37
 
33
- ### Running Tests
38
+ initTracker();
39
+ }, []);
34
40
 
35
- ```bash
36
- yarn test
41
+ return <YourApp />;
42
+ };
37
43
  ```
38
44
 
39
- ### Build
45
+ ### Tracking Events
40
46
 
41
- ```bash
42
- yarn build
43
- ```
47
+ #### Identify User
48
+ Track user identification when a user logs in or updates their profile.
44
49
 
45
- ## Release & Publish
50
+ ```typescript
51
+ // Signature: trackUser(userId: string, traits?: object)
52
+ await BytemTracker.trackUser('user_12345', {
53
+ email: 'user@example.com',
54
+ age: 25,
55
+ membership: 'gold'
56
+ });
57
+ ```
46
58
 
47
- Automate version bump and publish via npm scripts:
59
+ #### View Product
60
+ Track when a user views a product details page.
61
+
62
+ ```typescript
63
+ await BytemTracker.trackViewProduct({
64
+ productId: 'prod_001',
65
+ name: 'Awesome Gadget',
66
+ price: 99.99,
67
+ currency: 'USD',
68
+ category: 'Electronics'
69
+ });
70
+ ```
48
71
 
49
- ```bash
50
- # Patch release (1.0.0 -> 1.0.1)
51
- npm run release:patch
72
+ #### Checkout Order
73
+ Track when a user initiates checkout.
74
+
75
+ ```typescript
76
+ await BytemTracker.trackCheckOutOrder({
77
+ orderId: 'order_abc123',
78
+ total: 150.00,
79
+ currency: 'USD',
80
+ products: [
81
+ { productId: 'prod_001', price: 99.99, quantity: 1 },
82
+ { productId: 'prod_002', price: 50.01, quantity: 1 }
83
+ ]
84
+ });
85
+ ```
52
86
 
53
- # Minor release (1.0.0 -> 1.1.0)
54
- npm run release:minor
87
+ #### Pay Order
88
+ Track when a user completes a payment.
89
+
90
+ ```typescript
91
+ await BytemTracker.trackPayOrder({
92
+ orderId: 'order_abc123',
93
+ total: 150.00,
94
+ currency: 'USD',
95
+ products: [
96
+ { productId: 'prod_001', price: 99.99, quantity: 1 },
97
+ { productId: 'prod_002', price: 50.01, quantity: 1 }
98
+ ]
99
+ });
100
+ ```
55
101
 
56
- # Major release (1.0.0 -> 2.0.0)
57
- npm run release:major
102
+ ## Platform Compatibility
58
103
 
59
- # Beta prerelease (1.0.0 -> 1.0.1-beta.0)
60
- npm run release:beta
61
- ```
104
+ This SDK is designed for **React Native** and supports:
105
+ - iOS
106
+ - Android
62
107
 
63
- Notes:
64
- - These scripts bump package.json version and publish to npm.
65
- - `prepublishOnly` builds the package before publishing.
66
- - Configure auth via user-level `~/.npmrc`. Do not commit tokens in the repo.
108
+ It relies on:
109
+ - `@react-native-async-storage/async-storage`: For persisting session and visitor IDs.
110
+ - `react-native-device-info`: For capturing device metrics (model, OS version, etc.).
@@ -23,16 +23,72 @@ declare class BytemTracker {
23
23
  private lastViewStoredDuration;
24
24
  private constructor();
25
25
  static getInstance(): BytemTracker;
26
+ /**
27
+ * Initializes the BytemTracker SDK with the provided configuration.
28
+ * This method must be called before any other tracking methods.
29
+ *
30
+ * @param config - The configuration object for the tracker.
31
+ * @returns A promise that resolves when initialization is complete.
32
+ */
26
33
  init(config: TrackerConfig): Promise<void>;
27
34
  private ensureInitialized;
35
+ /**
36
+ * Begins a new user session.
37
+ * Handles session cookie logic to prevent excessive session starts if one is already active.
38
+ *
39
+ * @param force - If true, forces a new session start request even if the session cookie is valid.
40
+ */
28
41
  beginSession(force?: boolean): Promise<void>;
42
+ /**
43
+ * Reports session duration to the server.
44
+ * Usually called internally or when app state changes.
45
+ *
46
+ * @param sec - The duration in seconds to report.
47
+ */
29
48
  sessionDuration(sec: number): Promise<void>;
49
+ /**
50
+ * Ends the current user session.
51
+ *
52
+ * @param sec - Optional duration of the session in seconds. If not provided, it's calculated from the last beat.
53
+ * @param force - If true, forces the end session request even if using session cookies.
54
+ */
30
55
  endSession(sec?: number, force?: boolean): Promise<void>;
56
+ /**
57
+ * Manually tracks or extends a session.
58
+ * If a session is active, it reports duration. If not, it begins a new session.
59
+ */
31
60
  trackSessions(): Promise<void>;
61
+ /**
62
+ * Resumes time tracking for session duration.
63
+ */
32
64
  startTime(): void;
65
+ /**
66
+ * Pauses time tracking for session duration.
67
+ */
33
68
  stopTime(): void;
69
+ /**
70
+ * Tracks a custom event.
71
+ *
72
+ * @param eventKey - The unique key/name for the event.
73
+ * @param segmentation - Optional key-value pairs to attach to the event.
74
+ * @param count - The number of times this event occurred (default: 1).
75
+ * @param sum - Optional numeric value associated with the event (e.g., purchase amount).
76
+ * @param duration - Optional duration associated with the event.
77
+ */
34
78
  trackEvent(eventKey: string, segmentation?: Record<string, any>, count?: number, sum?: number, duration?: number): Promise<void>;
79
+ /**
80
+ * Tracks a page view.
81
+ * Automatically handles reporting the duration of the previous page view.
82
+ *
83
+ * @param current - The name or path of the current page.
84
+ * @param referrer - Optional name or path of the previous page.
85
+ */
35
86
  trackPageview(current: string, referrer?: string): Promise<void>;
87
+ /**
88
+ * Tracks a click on a product/goods item.
89
+ *
90
+ * @param params - The parameters for the goods click event.
91
+ */
36
92
  trackGoodsClick(params: {
37
93
  gid?: string;
38
94
  skuid?: string;
@@ -48,9 +104,34 @@ declare class BytemTracker {
48
104
  private buildBaseRequestParams;
49
105
  private extendSession;
50
106
  private sendRequest;
107
+ /**
108
+ * Tracks a product view event.
109
+ * Maps the product details to a 'view_product' event.
110
+ *
111
+ * @param product - The product object to track.
112
+ */
51
113
  trackViewProduct(product: Product): Promise<void>;
114
+ /**
115
+ * Tracks a checkout order event.
116
+ * Automatically splits product lists into parallel arrays (gids, prices, quantities)
117
+ * to match the backend requirement.
118
+ *
119
+ * @param order - The order object containing products to checkout.
120
+ */
52
121
  trackCheckOutOrder(order: Order): Promise<void>;
122
+ /**
123
+ * Tracks a payment order event.
124
+ * Maps product list to a 'goods' array structure expected by the backend.
125
+ *
126
+ * @param order - The order object that was paid for.
127
+ */
53
128
  trackPayOrder(order: Order): Promise<void>;
129
+ /**
130
+ * Tracks user details/profile updates.
131
+ *
132
+ * @param userId - The unique identifier for the user.
133
+ * @param traits - Additional user properties (email, name, custom fields).
134
+ */
54
135
  trackUser(userId: string, traits?: UserTraits): Promise<void>;
55
136
  private sendUserDetails;
56
137
  }
@@ -35,6 +35,13 @@ class BytemTracker {
35
35
  }
36
36
  return BytemTracker.instance;
37
37
  }
38
+ /**
39
+ * Initializes the BytemTracker SDK with the provided configuration.
40
+ * This method must be called before any other tracking methods.
41
+ *
42
+ * @param config - The configuration object for the tracker.
43
+ * @returns A promise that resolves when initialization is complete.
44
+ */
38
45
  async init(config) {
39
46
  if (this.isInitialized) {
40
47
  console.warn('[BytemTracker] Already initialized');
@@ -49,7 +56,10 @@ class BytemTracker {
49
56
  if (config.path) {
50
57
  this.apiPath = config.path.startsWith('/') ? config.path : '/' + config.path;
51
58
  }
52
- // Initialize Visitor ID
59
+ // Initialize Visitor ID logic:
60
+ // 1. If provided in config, use it.
61
+ // 2. If not, try to retrieve from storage.
62
+ // 3. If not in storage, generate a new UUID.
53
63
  if (config.visitorId) {
54
64
  this.visitorId = config.visitorId;
55
65
  await (0, storage_1.setItem)(storage_1.StorageKeys.VISITOR_ID, this.visitorId);
@@ -69,7 +79,9 @@ class BytemTracker {
69
79
  console.log(`[BytemTracker] Restored visitor ID: ${this.visitorId}`);
70
80
  }
71
81
  }
72
- // Initialize Device ID type
82
+ // Initialize Device ID type logic:
83
+ // 0: Developer supplied
84
+ // 1: SDK generated (default)
73
85
  if (config.deviceId) {
74
86
  this.deviceIdType = 0; // Developer set
75
87
  await (0, storage_1.setItem)(storage_1.StorageKeys.DEVICE_ID, config.deviceId);
@@ -88,7 +100,7 @@ class BytemTracker {
88
100
  if (this.appScheme)
89
101
  console.log(` app_scheme: ${this.appScheme}`);
90
102
  }
91
- // Begin session
103
+ // Attempt to begin a session automatically on init
92
104
  try {
93
105
  await this.beginSession();
94
106
  }
@@ -104,6 +116,12 @@ class BytemTracker {
104
116
  throw new Error('[BytemTracker] Not initialized. Call await init() first.');
105
117
  }
106
118
  }
119
+ /**
120
+ * Begins a new user session.
121
+ * Handles session cookie logic to prevent excessive session starts if one is already active.
122
+ *
123
+ * @param force - If true, forces a new session start request even if the session cookie is valid.
124
+ */
107
125
  async beginSession(force = false) {
108
126
  this.ensureInitialized();
109
127
  if (this.sessionStarted) {
@@ -114,7 +132,7 @@ class BytemTracker {
114
132
  try {
115
133
  this.sessionStarted = true;
116
134
  this.lastBeat = Date.now();
117
- // Check session cookie
135
+ // Check session cookie to avoid duplicate session starts
118
136
  const sessionKey = `${this.appKey}/cly_session`;
119
137
  const expireTimestampStr = await (0, storage_1.getItem)(sessionKey);
120
138
  const expireTimestamp = expireTimestampStr ? parseInt(expireTimestampStr, 10) : null;
@@ -126,7 +144,7 @@ class BytemTracker {
126
144
  if (!shouldSendRequest) {
127
145
  if (this.debug)
128
146
  console.log('[BytemTracker] Session cookie still valid, skipping begin_session request');
129
- // Update cookie
147
+ // Extend the cookie validity
130
148
  await (0, storage_1.setItem)(sessionKey, (currentTimestamp + this.sessionCookieTimeout * 60 * 1000).toString());
131
149
  return;
132
150
  }
@@ -144,6 +162,12 @@ class BytemTracker {
144
162
  console.error('[BytemTracker] Error in beginSession:', e);
145
163
  }
146
164
  }
165
+ /**
166
+ * Reports session duration to the server.
167
+ * Usually called internally or when app state changes.
168
+ *
169
+ * @param sec - The duration in seconds to report.
170
+ */
147
171
  async sessionDuration(sec) {
148
172
  this.ensureInitialized();
149
173
  if (!this.sessionStarted) {
@@ -158,6 +182,12 @@ class BytemTracker {
158
182
  this.lastBeat = Date.now();
159
183
  await this.extendSession();
160
184
  }
185
+ /**
186
+ * Ends the current user session.
187
+ *
188
+ * @param sec - Optional duration of the session in seconds. If not provided, it's calculated from the last beat.
189
+ * @param force - If true, forces the end session request even if using session cookies.
190
+ */
161
191
  async endSession(sec, force = false) {
162
192
  this.ensureInitialized();
163
193
  if (!this.sessionStarted) {
@@ -181,6 +211,10 @@ class BytemTracker {
181
211
  await this.sendRequest(this.apiPath, body, 'End Session');
182
212
  this.sessionStarted = false;
183
213
  }
214
+ /**
215
+ * Manually tracks or extends a session.
216
+ * If a session is active, it reports duration. If not, it begins a new session.
217
+ */
184
218
  async trackSessions() {
185
219
  this.ensureInitialized();
186
220
  if (this.debug)
@@ -200,6 +234,9 @@ class BytemTracker {
200
234
  if (this.debug)
201
235
  console.log('[BytemTracker] Session tracking started');
202
236
  }
237
+ /**
238
+ * Resumes time tracking for session duration.
239
+ */
203
240
  startTime() {
204
241
  if (!this.trackTime) {
205
242
  this.trackTime = true;
@@ -223,6 +260,9 @@ class BytemTracker {
223
260
  this.extendSession();
224
261
  }
225
262
  }
263
+ /**
264
+ * Pauses time tracking for session duration.
265
+ */
226
266
  stopTime() {
227
267
  if (this.trackTime) {
228
268
  this.trackTime = false;
@@ -237,6 +277,15 @@ class BytemTracker {
237
277
  console.log('[BytemTracker] Time tracking stopped');
238
278
  }
239
279
  }
280
+ /**
281
+ * Tracks a custom event.
282
+ *
283
+ * @param eventKey - The unique key/name for the event.
284
+ * @param segmentation - Optional key-value pairs to attach to the event.
285
+ * @param count - The number of times this event occurred (default: 1).
286
+ * @param sum - Optional numeric value associated with the event (e.g., purchase amount).
287
+ * @param duration - Optional duration associated with the event.
288
+ */
240
289
  async trackEvent(eventKey, segmentation, count = 1, sum, duration) {
241
290
  this.ensureInitialized();
242
291
  const deviceId = await this.getRealDeviceId();
@@ -257,6 +306,13 @@ class BytemTracker {
257
306
  body['events'] = JSON.stringify([event]);
258
307
  await this.sendRequest(this.apiPath, body, 'Event');
259
308
  }
309
+ /**
310
+ * Tracks a page view.
311
+ * Automatically handles reporting the duration of the previous page view.
312
+ *
313
+ * @param current - The name or path of the current page.
314
+ * @param referrer - Optional name or path of the previous page.
315
+ */
260
316
  async trackPageview(current, referrer) {
261
317
  await this.reportViewDuration();
262
318
  const segmentation = {
@@ -279,6 +335,11 @@ class BytemTracker {
279
335
  }
280
336
  await this.trackEvent(types_1.BytemEventKeys.view, segmentation, 1, undefined, duration);
281
337
  }
338
+ /**
339
+ * Tracks a click on a product/goods item.
340
+ *
341
+ * @param params - The parameters for the goods click event.
342
+ */
282
343
  async trackGoodsClick(params) {
283
344
  this.ensureInitialized();
284
345
  const segmentation = {
@@ -423,6 +484,12 @@ class BytemTracker {
423
484
  }
424
485
  }
425
486
  // Wrappers for Business methods to maintain compatibility or you can refactor them too
487
+ /**
488
+ * Tracks a product view event.
489
+ * Maps the product details to a 'view_product' event.
490
+ *
491
+ * @param product - The product object to track.
492
+ */
426
493
  async trackViewProduct(product) {
427
494
  // Map Product to segmentation
428
495
  const segmentation = {
@@ -435,25 +502,58 @@ class BytemTracker {
435
502
  // In Dart, trackViewProduct sends "view_product" event.
436
503
  await this.trackEvent('view_product', segmentation);
437
504
  }
505
+ /**
506
+ * Tracks a checkout order event.
507
+ * Automatically splits product lists into parallel arrays (gids, prices, quantities)
508
+ * to match the backend requirement.
509
+ *
510
+ * @param order - The order object containing products to checkout.
511
+ */
438
512
  async trackCheckOutOrder(order) {
439
513
  // Map Order to segmentation
440
514
  const segmentation = {
441
515
  order_id: order.orderId,
442
516
  total: order.total,
443
517
  currency: order.currency,
444
- // products need to be handled. Dart sends lists of gids, etc.
445
- // This is a simplification. Ideally we replicate trackCheckOutOrder logic fully.
446
518
  };
519
+ if (order.products && order.products.length > 0) {
520
+ segmentation.gids = order.products.map(p => p.productId);
521
+ segmentation.prices = order.products.map(p => p.price);
522
+ segmentation.quantity = order.products.map(p => p.quantity || 1);
523
+ // Optional: skuids if available in Product type, currently not in interface but good to handle if extended
524
+ // segmentation.skuids = ...
525
+ }
447
526
  await this.trackEvent('check_out_order', segmentation);
448
527
  }
528
+ /**
529
+ * Tracks a payment order event.
530
+ * Maps product list to a 'goods' array structure expected by the backend.
531
+ *
532
+ * @param order - The order object that was paid for.
533
+ */
449
534
  async trackPayOrder(order) {
450
535
  const segmentation = {
451
536
  order_id: order.orderId,
452
537
  total: order.total,
453
538
  currency: order.currency,
454
539
  };
540
+ if (order.products && order.products.length > 0) {
541
+ // Dart maps this to a list of maps called "goods"
542
+ segmentation.goods = order.products.map(p => ({
543
+ gid: p.productId,
544
+ name: p.name,
545
+ price: p.price,
546
+ quantity: p.quantity || 1,
547
+ }));
548
+ }
455
549
  await this.trackEvent('pay_order', segmentation);
456
550
  }
551
+ /**
552
+ * Tracks user details/profile updates.
553
+ *
554
+ * @param userId - The unique identifier for the user.
555
+ * @param traits - Additional user properties (email, name, custom fields).
556
+ */
457
557
  async trackUser(userId, traits) {
458
558
  // Map to user_details
459
559
  // This is a special request in Dart: "user_details" param
@@ -1,12 +1,25 @@
1
+ /**
2
+ * Configuration for initializing the BytemTracker.
3
+ */
1
4
  export interface TrackerConfig {
5
+ /** The unique application ID provided by Bytem. */
2
6
  appId: string;
7
+ /** Custom endpoint URL for tracking requests. */
3
8
  endpoint?: string;
9
+ /** Custom path for the API endpoint (default: '/i'). */
4
10
  path?: string;
11
+ /** Enable debug logging for development. */
5
12
  debug?: boolean;
13
+ /** Optional: Manually set a visitor ID. */
6
14
  visitorId?: string;
15
+ /** Optional: Manually set a device ID. */
7
16
  deviceId?: string;
17
+ /** Optional: App scheme for domain field in tracking. */
8
18
  appScheme?: string;
9
19
  }
20
+ /**
21
+ * Predefined event keys used internally by the SDK.
22
+ */
10
23
  export declare const BytemEventKeys: {
11
24
  nps: string;
12
25
  survey: string;
@@ -16,6 +29,9 @@ export declare const BytemEventKeys: {
16
29
  pushAction: string;
17
30
  action: string;
18
31
  };
32
+ /**
33
+ * Structure containing device information collected by the SDK.
34
+ */
19
35
  export interface DeviceInfo {
20
36
  platform: string;
21
37
  os: string;
@@ -44,9 +60,15 @@ export interface BaseParams {
44
60
  screen_height: number;
45
61
  user_agent: string;
46
62
  }
63
+ /**
64
+ * Arbitrary key-value pairs for user properties.
65
+ */
47
66
  export interface UserTraits {
48
67
  [key: string]: any;
49
68
  }
69
+ /**
70
+ * Represents a product in the system.
71
+ */
50
72
  export interface Product {
51
73
  productId: string;
52
74
  name?: string;
@@ -55,6 +77,9 @@ export interface Product {
55
77
  category?: string;
56
78
  quantity?: number;
57
79
  }
80
+ /**
81
+ * Represents an order (checkout or payment).
82
+ */
58
83
  export interface Order {
59
84
  orderId: string;
60
85
  total: number;
package/dist/src/types.js CHANGED
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BytemEventKeys = void 0;
4
+ /**
5
+ * Predefined event keys used internally by the SDK.
6
+ */
4
7
  exports.BytemEventKeys = {
5
8
  nps: "[CLY]_nps",
6
9
  survey: "[CLY]_survey",
@@ -223,7 +223,10 @@ describe('BytemTracker SDK', () => {
223
223
  expect(events[0].segmentation).toMatchObject({
224
224
  order_id: 'order_123',
225
225
  total: 99.99,
226
- currency: 'USD'
226
+ currency: 'USD',
227
+ gids: ['prod_1', 'prod_2'],
228
+ prices: [50, 49.99],
229
+ quantity: [1, 1]
227
230
  });
228
231
  });
229
232
  it('should track pay order', async () => {
@@ -231,7 +234,10 @@ describe('BytemTracker SDK', () => {
231
234
  orderId: 'order_123',
232
235
  total: 99.99,
233
236
  currency: 'USD',
234
- products: []
237
+ products: [
238
+ { productId: 'prod_1', name: 'Product 1', price: 50, quantity: 1 },
239
+ { productId: 'prod_2', name: 'Product 2', price: 49.99, quantity: 2 }
240
+ ]
235
241
  };
236
242
  await BytemTracker_1.default.trackPayOrder(order);
237
243
  const calls = global.fetch.mock.calls;
@@ -241,7 +247,11 @@ describe('BytemTracker SDK', () => {
241
247
  expect(events[0].segmentation).toMatchObject({
242
248
  order_id: 'order_123',
243
249
  total: 99.99,
244
- currency: 'USD'
250
+ currency: 'USD',
251
+ goods: [
252
+ { gid: 'prod_1', name: 'Product 1', price: 50, quantity: 1 },
253
+ { gid: 'prod_2', name: 'Product 2', price: 49.99, quantity: 2 }
254
+ ]
245
255
  });
246
256
  });
247
257
  });
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@bytem/bytem-tracker-app",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Bytem Tracker SDK for React Native",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
7
  "files": [
8
8
  "dist"
9
9
  ],
@@ -25,13 +25,13 @@
25
25
  "author": "Barry",
26
26
  "license": "ISC",
27
27
  "peerDependencies": {
28
- "@react-native-async-storage/async-storage": "^1.0.0",
29
28
  "react": ">=16.8.0",
30
- "react-native": ">=0.60.0",
31
- "react-native-device-info": "^10.0.0"
29
+ "react-native": ">=0.60.0"
32
30
  },
33
31
  "dependencies": {
34
- "uuid": "^9.0.0"
32
+ "uuid": "^9.0.0",
33
+ "@react-native-async-storage/async-storage": "^1.19.0",
34
+ "react-native-device-info": "^10.8.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@react-native-async-storage/async-storage": "^1.19.0",