@rejourneyco/react-native 1.0.0 → 1.0.1

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.
@@ -162,16 +162,16 @@ function initAutoTracking(trackingConfig, callbacks = {}) {
162
162
  onErrorCaptured = callbacks.onError || null;
163
163
  onScreenChange = callbacks.onScreen || null;
164
164
 
165
- // Initialize metrics
166
- metrics = createEmptyMetrics();
167
- sessionStartTime = Date.now();
168
- anonymousId = generateAnonymousId();
169
-
170
165
  // Setup error tracking
171
166
  setupErrorTracking();
172
167
 
173
168
  // Setup React Navigation tracking (if available)
174
169
  setupNavigationTracking();
170
+
171
+ // Load anonymous ID from native storage (or generate new one)
172
+ loadAnonymousId().then(id => {
173
+ anonymousId = id;
174
+ });
175
175
  isInitialized = true;
176
176
  }
177
177
 
@@ -1148,7 +1148,7 @@ function collectDeviceInfo() {
1148
1148
  // =============================================================================
1149
1149
 
1150
1150
  // Storage key for anonymous ID
1151
- const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
1151
+ // const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
1152
1152
 
1153
1153
  /**
1154
1154
  * Generate a persistent anonymous ID
@@ -1159,39 +1159,12 @@ function generateAnonymousId() {
1159
1159
  return `anon_${timestamp}_${random}`;
1160
1160
  }
1161
1161
 
1162
- /**
1163
- * Initialize anonymous ID - tries to load from storage, generates new if not found
1164
- * This is called internally and runs asynchronously
1165
- */
1166
- async function initAnonymousId() {
1167
- try {
1168
- // Try to load from AsyncStorage
1169
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
1170
- const storedId = await AsyncStorage.getItem(ANONYMOUS_ID_KEY);
1171
- if (storedId) {
1172
- anonymousId = storedId;
1173
- } else {
1174
- // Generate new ID and persist
1175
- anonymousId = generateAnonymousId();
1176
- await AsyncStorage.setItem(ANONYMOUS_ID_KEY, anonymousId);
1177
- }
1178
- } catch {
1179
- // AsyncStorage not available or error - just generate without persistence
1180
- if (!anonymousId) {
1181
- anonymousId = generateAnonymousId();
1182
- }
1183
- }
1184
- }
1185
-
1186
1162
  /**
1187
1163
  * Get the anonymous ID (synchronous - returns generated ID immediately)
1188
- * For persistent ID, call initAnonymousId() first
1189
1164
  */
1190
1165
  function getAnonymousId() {
1191
1166
  if (!anonymousId) {
1192
1167
  anonymousId = generateAnonymousId();
1193
- // Try to persist asynchronously (fire and forget)
1194
- initAnonymousId().catch(() => {});
1195
1168
  }
1196
1169
  return anonymousId;
1197
1170
  }
@@ -1204,11 +1177,10 @@ async function ensurePersistentAnonymousId() {
1204
1177
  if (anonymousId) return anonymousId;
1205
1178
  if (!anonymousIdPromise) {
1206
1179
  anonymousIdPromise = (async () => {
1207
- await initAnonymousId();
1208
- if (!anonymousId) {
1209
- anonymousId = generateAnonymousId();
1210
- }
1211
- return anonymousId;
1180
+ // Just use the load logic which now delegates to native or memory
1181
+ const id = await loadAnonymousId();
1182
+ anonymousId = id;
1183
+ return id;
1212
1184
  })();
1213
1185
  }
1214
1186
  return anonymousIdPromise;
@@ -1219,22 +1191,23 @@ async function ensurePersistentAnonymousId() {
1219
1191
  * Call this at app startup for best results
1220
1192
  */
1221
1193
  async function loadAnonymousId() {
1222
- await initAnonymousId();
1223
- return anonymousId || generateAnonymousId();
1194
+ const nativeModule = getRejourneyNativeModule();
1195
+ if (nativeModule && nativeModule.getUserIdentity) {
1196
+ try {
1197
+ return (await nativeModule.getUserIdentity()) || generateAnonymousId();
1198
+ } catch {
1199
+ return generateAnonymousId();
1200
+ }
1201
+ }
1202
+ return generateAnonymousId();
1224
1203
  }
1225
1204
 
1226
1205
  /**
1227
- * Set a custom anonymous ID (e.g., from persistent storage)
1206
+ * Set a custom anonymous ID
1228
1207
  */
1229
1208
  function setAnonymousId(id) {
1230
1209
  anonymousId = id;
1231
- // Try to persist asynchronously
1232
- try {
1233
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
1234
- AsyncStorage.setItem(ANONYMOUS_ID_KEY, id).catch(() => {});
1235
- } catch {
1236
- // Ignore if AsyncStorage not available
1237
- }
1210
+ // No-op for async persistence as we moved to native-only or memory-only
1238
1211
  }
1239
1212
 
1240
1213
  // =============================================================================
@@ -168,7 +168,6 @@ function getAutoTracking() {
168
168
  }
169
169
 
170
170
  // State
171
- const USER_IDENTITY_KEY = '@rejourney_user_identity';
172
171
  let _isInitialized = false;
173
172
  let _isRecording = false;
174
173
  let _initializationFailed = false;
@@ -183,22 +182,17 @@ let _lastScrollOffset = 0;
183
182
  const SCROLL_THROTTLE_MS = 100;
184
183
 
185
184
  // Helper to save/load user identity
186
- async function persistUserIdentity(identity) {
187
- try {
188
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
189
- if (identity) {
190
- await AsyncStorage.setItem(USER_IDENTITY_KEY, identity);
191
- } else {
192
- await AsyncStorage.removeItem(USER_IDENTITY_KEY);
193
- }
194
- } catch (e) {
195
- // Ignore storage errors
196
- }
185
+ // NOW HANDLED NATIVELY - No-op on JS side to avoid unnecessary bridge calls
186
+ async function persistUserIdentity(_identity) {
187
+ // Native module handles persistence automatically in setUserIdentity
197
188
  }
198
189
  async function loadPersistedUserIdentity() {
199
190
  try {
200
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
201
- return await AsyncStorage.getItem(USER_IDENTITY_KEY);
191
+ const nativeModule = getRejourneyNative();
192
+ if (!nativeModule) return null;
193
+
194
+ // NATIVE STORAGE: Read directly from SharedPreferences/NSUserDefaults
195
+ return await nativeModule.getUserIdentity();
202
196
  } catch (e) {
203
197
  return null;
204
198
  }
@@ -135,16 +135,16 @@ export function initAutoTracking(trackingConfig, callbacks = {}) {
135
135
  onErrorCaptured = callbacks.onError || null;
136
136
  onScreenChange = callbacks.onScreen || null;
137
137
 
138
- // Initialize metrics
139
- metrics = createEmptyMetrics();
140
- sessionStartTime = Date.now();
141
- anonymousId = generateAnonymousId();
142
-
143
138
  // Setup error tracking
144
139
  setupErrorTracking();
145
140
 
146
141
  // Setup React Navigation tracking (if available)
147
142
  setupNavigationTracking();
143
+
144
+ // Load anonymous ID from native storage (or generate new one)
145
+ loadAnonymousId().then(id => {
146
+ anonymousId = id;
147
+ });
148
148
  isInitialized = true;
149
149
  }
150
150
 
@@ -1121,7 +1121,7 @@ export function collectDeviceInfo() {
1121
1121
  // =============================================================================
1122
1122
 
1123
1123
  // Storage key for anonymous ID
1124
- const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
1124
+ // const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
1125
1125
 
1126
1126
  /**
1127
1127
  * Generate a persistent anonymous ID
@@ -1132,39 +1132,12 @@ function generateAnonymousId() {
1132
1132
  return `anon_${timestamp}_${random}`;
1133
1133
  }
1134
1134
 
1135
- /**
1136
- * Initialize anonymous ID - tries to load from storage, generates new if not found
1137
- * This is called internally and runs asynchronously
1138
- */
1139
- async function initAnonymousId() {
1140
- try {
1141
- // Try to load from AsyncStorage
1142
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
1143
- const storedId = await AsyncStorage.getItem(ANONYMOUS_ID_KEY);
1144
- if (storedId) {
1145
- anonymousId = storedId;
1146
- } else {
1147
- // Generate new ID and persist
1148
- anonymousId = generateAnonymousId();
1149
- await AsyncStorage.setItem(ANONYMOUS_ID_KEY, anonymousId);
1150
- }
1151
- } catch {
1152
- // AsyncStorage not available or error - just generate without persistence
1153
- if (!anonymousId) {
1154
- anonymousId = generateAnonymousId();
1155
- }
1156
- }
1157
- }
1158
-
1159
1135
  /**
1160
1136
  * Get the anonymous ID (synchronous - returns generated ID immediately)
1161
- * For persistent ID, call initAnonymousId() first
1162
1137
  */
1163
1138
  export function getAnonymousId() {
1164
1139
  if (!anonymousId) {
1165
1140
  anonymousId = generateAnonymousId();
1166
- // Try to persist asynchronously (fire and forget)
1167
- initAnonymousId().catch(() => {});
1168
1141
  }
1169
1142
  return anonymousId;
1170
1143
  }
@@ -1177,11 +1150,10 @@ export async function ensurePersistentAnonymousId() {
1177
1150
  if (anonymousId) return anonymousId;
1178
1151
  if (!anonymousIdPromise) {
1179
1152
  anonymousIdPromise = (async () => {
1180
- await initAnonymousId();
1181
- if (!anonymousId) {
1182
- anonymousId = generateAnonymousId();
1183
- }
1184
- return anonymousId;
1153
+ // Just use the load logic which now delegates to native or memory
1154
+ const id = await loadAnonymousId();
1155
+ anonymousId = id;
1156
+ return id;
1185
1157
  })();
1186
1158
  }
1187
1159
  return anonymousIdPromise;
@@ -1192,22 +1164,23 @@ export async function ensurePersistentAnonymousId() {
1192
1164
  * Call this at app startup for best results
1193
1165
  */
1194
1166
  export async function loadAnonymousId() {
1195
- await initAnonymousId();
1196
- return anonymousId || generateAnonymousId();
1167
+ const nativeModule = getRejourneyNativeModule();
1168
+ if (nativeModule && nativeModule.getUserIdentity) {
1169
+ try {
1170
+ return (await nativeModule.getUserIdentity()) || generateAnonymousId();
1171
+ } catch {
1172
+ return generateAnonymousId();
1173
+ }
1174
+ }
1175
+ return generateAnonymousId();
1197
1176
  }
1198
1177
 
1199
1178
  /**
1200
- * Set a custom anonymous ID (e.g., from persistent storage)
1179
+ * Set a custom anonymous ID
1201
1180
  */
1202
1181
  export function setAnonymousId(id) {
1203
1182
  anonymousId = id;
1204
- // Try to persist asynchronously
1205
- try {
1206
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
1207
- AsyncStorage.setItem(ANONYMOUS_ID_KEY, id).catch(() => {});
1208
- } catch {
1209
- // Ignore if AsyncStorage not available
1210
- }
1183
+ // No-op for async persistence as we moved to native-only or memory-only
1211
1184
  }
1212
1185
 
1213
1186
  // =============================================================================
@@ -129,6 +129,7 @@ export interface Spec extends TurboModule {
129
129
  setUserIdentity(userId: string): Promise<{
130
130
  success: boolean;
131
131
  }>;
132
+ getUserIdentity(): Promise<string | null>;
132
133
  setDebugMode(enabled: boolean): Promise<{
133
134
  success: boolean;
134
135
  }>;
@@ -167,7 +167,6 @@ export declare function getRemainingSessionDurationMs(): number;
167
167
  export declare function collectDeviceInfo(): DeviceInfo;
168
168
  /**
169
169
  * Get the anonymous ID (synchronous - returns generated ID immediately)
170
- * For persistent ID, call initAnonymousId() first
171
170
  */
172
171
  export declare function getAnonymousId(): string;
173
172
  /**
@@ -181,7 +180,7 @@ export declare function ensurePersistentAnonymousId(): Promise<string>;
181
180
  */
182
181
  export declare function loadAnonymousId(): Promise<string>;
183
182
  /**
184
- * Set a custom anonymous ID (e.g., from persistent storage)
183
+ * Set a custom anonymous ID
185
184
  */
186
185
  export declare function setAnonymousId(id: string): void;
187
186
  declare const _default: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rejourneyco/react-native",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Rejourney Session Recording SDK for React Native",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -40,7 +40,10 @@
40
40
  "test:coverage": "vitest run --coverage",
41
41
  "release": "release-it",
42
42
  "verify:ios": "./scripts/verify-ios-structure.sh",
43
- "test:ios-install": "./scripts/test-ios-install.sh"
43
+ "verify:package": "./scripts/verify-package-content.sh",
44
+ "test:ios-install": "./scripts/test-ios-install.sh",
45
+ "test:android-install": "./scripts/test-android-install.sh",
46
+ "audit:deps": "depcruise src --config .dependency-cruiser.js"
44
47
  },
45
48
  "keywords": [
46
49
  "react-native",
@@ -61,6 +64,12 @@
61
64
  "@typescript-eslint/eslint-plugin": "^8.15.0",
62
65
  "@typescript-eslint/parser": "^8.15.0",
63
66
  "@vitest/coverage-v8": "^2.1.0",
67
+ "dependency-cruiser": "^16.10.4",
68
+ "@react-navigation/native": "*",
69
+ "expo-router": "*",
70
+ "expo-constants": "*",
71
+ "expo-application": "*",
72
+ "react-native-device-info": "*",
64
73
  "eslint": "^9.39.2",
65
74
  "react": "*",
66
75
  "react-native": "*",
@@ -70,7 +79,12 @@
70
79
  },
71
80
  "peerDependencies": {
72
81
  "react": "*",
73
- "react-native": "*"
82
+ "react-native": "*",
83
+ "@react-navigation/native": ">=6.0.0",
84
+ "expo-router": ">=3.0.0",
85
+ "expo-constants": ">=15.0.0",
86
+ "expo-application": ">=5.0.0",
87
+ "react-native-device-info": ">=10.0.0"
74
88
  },
75
89
  "codegenConfig": {
76
90
  "name": "RejourneySpec",
@@ -150,6 +150,8 @@ export interface Spec extends TurboModule {
150
150
 
151
151
  setUserIdentity(userId: string): Promise<{ success: boolean }>;
152
152
 
153
+ getUserIdentity(): Promise<string | null>;
154
+
153
155
  setDebugMode(enabled: boolean): Promise<{ success: boolean }>;
154
156
  }
155
157
 
package/src/index.ts CHANGED
@@ -193,8 +193,6 @@ function getAutoTracking() {
193
193
  }
194
194
 
195
195
  // State
196
- const USER_IDENTITY_KEY = '@rejourney_user_identity';
197
-
198
196
  let _isInitialized = false;
199
197
  let _isRecording = false;
200
198
  let _initializationFailed = false;
@@ -209,23 +207,18 @@ let _lastScrollOffset: number = 0;
209
207
  const SCROLL_THROTTLE_MS = 100;
210
208
 
211
209
  // Helper to save/load user identity
212
- async function persistUserIdentity(identity: string | null): Promise<void> {
213
- try {
214
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
215
- if (identity) {
216
- await AsyncStorage.setItem(USER_IDENTITY_KEY, identity);
217
- } else {
218
- await AsyncStorage.removeItem(USER_IDENTITY_KEY);
219
- }
220
- } catch (e) {
221
- // Ignore storage errors
222
- }
210
+ // NOW HANDLED NATIVELY - No-op on JS side to avoid unnecessary bridge calls
211
+ async function persistUserIdentity(_identity: string | null): Promise<void> {
212
+ // Native module handles persistence automatically in setUserIdentity
223
213
  }
224
214
 
225
215
  async function loadPersistedUserIdentity(): Promise<string | null> {
226
216
  try {
227
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
228
- return await AsyncStorage.getItem(USER_IDENTITY_KEY);
217
+ const nativeModule = getRejourneyNative();
218
+ if (!nativeModule) return null;
219
+
220
+ // NATIVE STORAGE: Read directly from SharedPreferences/NSUserDefaults
221
+ return await nativeModule.getUserIdentity();
229
222
  } catch (e) {
230
223
  return null;
231
224
  }
@@ -232,17 +232,17 @@ export function initAutoTracking(
232
232
  onErrorCaptured = callbacks.onError || null;
233
233
  onScreenChange = callbacks.onScreen || null;
234
234
 
235
- // Initialize metrics
236
- metrics = createEmptyMetrics();
237
- sessionStartTime = Date.now();
238
- anonymousId = generateAnonymousId();
239
-
240
235
  // Setup error tracking
241
236
  setupErrorTracking();
242
237
 
243
238
  // Setup React Navigation tracking (if available)
244
239
  setupNavigationTracking();
245
240
 
241
+ // Load anonymous ID from native storage (or generate new one)
242
+ loadAnonymousId().then(id => {
243
+ anonymousId = id;
244
+ });
245
+
246
246
  isInitialized = true;
247
247
  }
248
248
 
@@ -674,15 +674,15 @@ function setupNavigationTracking(): void {
674
674
  // We retry a few times with increasing delays
675
675
  let attempts = 0;
676
676
  const maxAttempts = 5;
677
-
677
+
678
678
  const trySetup = () => {
679
679
  attempts++;
680
680
  if (__DEV__) {
681
681
  logger.debug('Navigation setup attempt', attempts, 'of', maxAttempts);
682
682
  }
683
-
683
+
684
684
  const success = trySetupExpoRouter();
685
-
685
+
686
686
  if (success) {
687
687
  if (__DEV__) {
688
688
  logger.debug('Expo Router setup: SUCCESS on attempt', attempts);
@@ -1152,10 +1152,10 @@ export function collectDeviceInfo(): DeviceInfo {
1152
1152
  const Dimensions = getDimensions();
1153
1153
  const Platform = getPlatform();
1154
1154
  const NativeModules = getNativeModules();
1155
-
1155
+
1156
1156
  // Default values if react-native isn't available
1157
1157
  let width = 0, height = 0, scale = 1;
1158
-
1158
+
1159
1159
  if (Dimensions) {
1160
1160
  const windowDims = Dimensions.get('window');
1161
1161
  const screenDims = Dimensions.get('screen');
@@ -1261,7 +1261,7 @@ export function collectDeviceInfo(): DeviceInfo {
1261
1261
  // =============================================================================
1262
1262
 
1263
1263
  // Storage key for anonymous ID
1264
- const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
1264
+ // const ANONYMOUS_ID_KEY = '@rejourney_anonymous_id';
1265
1265
 
1266
1266
  /**
1267
1267
  * Generate a persistent anonymous ID
@@ -1272,40 +1272,12 @@ function generateAnonymousId(): string {
1272
1272
  return `anon_${timestamp}_${random}`;
1273
1273
  }
1274
1274
 
1275
- /**
1276
- * Initialize anonymous ID - tries to load from storage, generates new if not found
1277
- * This is called internally and runs asynchronously
1278
- */
1279
- async function initAnonymousId(): Promise<void> {
1280
- try {
1281
- // Try to load from AsyncStorage
1282
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
1283
- const storedId = await AsyncStorage.getItem(ANONYMOUS_ID_KEY);
1284
-
1285
- if (storedId) {
1286
- anonymousId = storedId;
1287
- } else {
1288
- // Generate new ID and persist
1289
- anonymousId = generateAnonymousId();
1290
- await AsyncStorage.setItem(ANONYMOUS_ID_KEY, anonymousId);
1291
- }
1292
- } catch {
1293
- // AsyncStorage not available or error - just generate without persistence
1294
- if (!anonymousId) {
1295
- anonymousId = generateAnonymousId();
1296
- }
1297
- }
1298
- }
1299
-
1300
1275
  /**
1301
1276
  * Get the anonymous ID (synchronous - returns generated ID immediately)
1302
- * For persistent ID, call initAnonymousId() first
1303
1277
  */
1304
1278
  export function getAnonymousId(): string {
1305
1279
  if (!anonymousId) {
1306
1280
  anonymousId = generateAnonymousId();
1307
- // Try to persist asynchronously (fire and forget)
1308
- initAnonymousId().catch(() => { });
1309
1281
  }
1310
1282
  return anonymousId;
1311
1283
  }
@@ -1318,11 +1290,10 @@ export async function ensurePersistentAnonymousId(): Promise<string> {
1318
1290
  if (anonymousId) return anonymousId;
1319
1291
  if (!anonymousIdPromise) {
1320
1292
  anonymousIdPromise = (async () => {
1321
- await initAnonymousId();
1322
- if (!anonymousId) {
1323
- anonymousId = generateAnonymousId();
1324
- }
1325
- return anonymousId;
1293
+ // Just use the load logic which now delegates to native or memory
1294
+ const id = await loadAnonymousId();
1295
+ anonymousId = id;
1296
+ return id;
1326
1297
  })();
1327
1298
  }
1328
1299
  return anonymousIdPromise;
@@ -1333,22 +1304,23 @@ export async function ensurePersistentAnonymousId(): Promise<string> {
1333
1304
  * Call this at app startup for best results
1334
1305
  */
1335
1306
  export async function loadAnonymousId(): Promise<string> {
1336
- await initAnonymousId();
1337
- return anonymousId || generateAnonymousId();
1307
+ const nativeModule = getRejourneyNativeModule();
1308
+ if (nativeModule && nativeModule.getUserIdentity) {
1309
+ try {
1310
+ return await nativeModule.getUserIdentity() || generateAnonymousId();
1311
+ } catch {
1312
+ return generateAnonymousId();
1313
+ }
1314
+ }
1315
+ return generateAnonymousId();
1338
1316
  }
1339
1317
 
1340
1318
  /**
1341
- * Set a custom anonymous ID (e.g., from persistent storage)
1319
+ * Set a custom anonymous ID
1342
1320
  */
1343
1321
  export function setAnonymousId(id: string): void {
1344
1322
  anonymousId = id;
1345
- // Try to persist asynchronously
1346
- try {
1347
- const AsyncStorage = require('@react-native-async-storage/async-storage').default;
1348
- AsyncStorage.setItem(ANONYMOUS_ID_KEY, id).catch(() => { });
1349
- } catch {
1350
- // Ignore if AsyncStorage not available
1351
- }
1323
+ // No-op for async persistence as we moved to native-only or memory-only
1352
1324
  }
1353
1325
 
1354
1326
  // =============================================================================