@teardown/react-native 2.0.9 → 2.0.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teardown/react-native",
3
- "version": "2.0.9",
3
+ "version": "2.0.11",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -50,9 +50,9 @@
50
50
  "nuke": "cd ../../ && bun run nuke"
51
51
  },
52
52
  "dependencies": {
53
- "@teardown/ingest-api": "0.1.41",
54
- "@teardown/schemas": "0.1.41",
55
- "@teardown/types": "0.1.41",
53
+ "@teardown/ingest-api": "0.1.46",
54
+ "@teardown/schemas": "0.1.46",
55
+ "@teardown/types": "0.1.46",
56
56
  "eventemitter3": "^5.0.1",
57
57
  "react-native-get-random-values": "^2.0.0",
58
58
  "uuid": "^13.0.0",
@@ -5,7 +5,7 @@ import type { StorageClient } from "../storage";
5
5
 
6
6
  export type { Eden, IngestApi };
7
7
 
8
- const TEARDOWN_INGEST_URL = "https://ingest.teardown.dev";
8
+ const TEARDOWN_INGEST_URL = "http://localhost:4880";// "https://ingest.teardown.dev";
9
9
  const TEARDOWN_API_KEY_HEADER = "td-api-key";
10
10
  const TEARDOWN_ORG_ID_HEADER = "td-org-id";
11
11
  const TEARDOWN_PROJECT_ID_HEADER = "td-project-id";
@@ -42,7 +42,7 @@ function createMockIdentityClient(initialState?: IdentifyState) {
42
42
  emitter.emit("IDENTIFY_STATE_CHANGED", currentState);
43
43
  currentState = {
44
44
  type: "identified",
45
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
45
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
46
46
  version_info: { status: nextIdentifyResult.data?.version_info.status ?? IdentifyVersionStatusEnum.UP_TO_DATE, update: null },
47
47
  };
48
48
  emitter.emit("IDENTIFY_STATE_CHANGED", currentState);
@@ -87,7 +87,7 @@ describe("ForceUpdateClient", () => {
87
87
  test("initializes version status when identity is already identified", () => {
88
88
  const mockIdentity = createMockIdentityClient({
89
89
  type: "identified",
90
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
90
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
91
91
  version_info: { status: IdentifyVersionStatusEnum.UPDATE_REQUIRED, update: null },
92
92
  });
93
93
  const mockLogging = createMockLoggingClient();
@@ -127,7 +127,7 @@ describe("ForceUpdateClient", () => {
127
127
  test("emits status change during initialization when already identified", () => {
128
128
  const mockIdentity = createMockIdentityClient({
129
129
  type: "identified",
130
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
130
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
131
131
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE, update: null },
132
132
  });
133
133
  const mockLogging = createMockLoggingClient();
@@ -148,7 +148,7 @@ describe("ForceUpdateClient", () => {
148
148
  // Trigger another identify to verify no duplicate
149
149
  mockIdentity.emitter.emit("IDENTIFY_STATE_CHANGED", {
150
150
  type: "identified",
151
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
151
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
152
152
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE, update: null },
153
153
  });
154
154
 
@@ -181,7 +181,7 @@ describe("ForceUpdateClient", () => {
181
181
  mockIdentity.emitter.emit("IDENTIFY_STATE_CHANGED", { type: "identifying" });
182
182
  mockIdentity.emitter.emit("IDENTIFY_STATE_CHANGED", {
183
183
  type: "identified",
184
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
184
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
185
185
  version_info: { status: IdentifyVersionStatusEnum.UPDATE_AVAILABLE, update: null },
186
186
  });
187
187
 
@@ -253,7 +253,7 @@ describe("ForceUpdateClient", () => {
253
253
  // After shutdown, emitting should not trigger listener
254
254
  mockIdentity.emitter.emit("IDENTIFY_STATE_CHANGED", {
255
255
  type: "identified",
256
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
256
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
257
257
  version_info: { status: IdentifyVersionStatusEnum.UPDATE_AVAILABLE, update: null },
258
258
  });
259
259
 
@@ -515,7 +515,7 @@ describe("ForceUpdateClient", () => {
515
515
 
516
516
  mockIdentity.emitter.emit("IDENTIFY_STATE_CHANGED", {
517
517
  type: "identified",
518
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
518
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
519
519
  version_info: { status: apiStatus, update: null },
520
520
  });
521
521
 
@@ -48,7 +48,7 @@ function createMockUtilsClient() {
48
48
  let uuidCounter = 0;
49
49
  return {
50
50
  generateRandomUUID: async () => `mock-uuid-${++uuidCounter}`,
51
- resetCounter: () => uuidCounter = 0,
51
+ resetCounter: () => { uuidCounter = 0; },
52
52
  };
53
53
  }
54
54
 
@@ -90,12 +90,12 @@ type ApiCallRecord = {
90
90
 
91
91
  function createMockApiClient(options: {
92
92
  success?: boolean;
93
- versionStatus?: IdentifyVersionStatusEnum;
93
+ versionStatus?: (typeof IdentifyVersionStatusEnum)[keyof typeof IdentifyVersionStatusEnum];
94
94
  errorStatus?: number;
95
95
  errorMessage?: string;
96
96
  sessionId?: string;
97
97
  deviceId?: string;
98
- personaId?: string;
98
+ user_id?: string;
99
99
  token?: string;
100
100
  throwError?: Error;
101
101
  } = {}) {
@@ -106,7 +106,7 @@ function createMockApiClient(options: {
106
106
  errorMessage,
107
107
  sessionId = "session-123",
108
108
  deviceId = "device-123",
109
- personaId = "persona-123",
109
+ user_id = "user-123",
110
110
  token = "token-123",
111
111
  throwError,
112
112
  } = options;
@@ -143,7 +143,7 @@ function createMockApiClient(options: {
143
143
  data: {
144
144
  session_id: sessionId,
145
145
  device_id: deviceId,
146
- persona_id: personaId,
146
+ user_id: user_id,
147
147
  token: token,
148
148
  version_info: { status: versionStatus },
149
149
  },
@@ -152,7 +152,7 @@ function createMockApiClient(options: {
152
152
  },
153
153
  getCalls: () => calls,
154
154
  getLastCall: () => calls[calls.length - 1],
155
- clearCalls: () => calls.length = 0,
155
+ clearCalls: () => { calls.length = 0; },
156
156
  };
157
157
  }
158
158
 
@@ -214,7 +214,7 @@ describe("IdentityClient", () => {
214
214
  const mockStorage = createMockStorageClient();
215
215
  const storedState = {
216
216
  type: "identified",
217
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
217
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
218
218
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE, update: null },
219
219
  };
220
220
  mockStorage.getStorage().set(IDENTIFY_STORAGE_KEY, JSON.stringify(storedState));
@@ -227,7 +227,7 @@ describe("IdentityClient", () => {
227
227
  if (state.type === "identified") {
228
228
  expect(state.session.session_id).toBe("s1");
229
229
  expect(state.session.device_id).toBe("d1");
230
- expect(state.session.persona_id).toBe("p1");
230
+ expect(state.session.user_id).toBe("p1");
231
231
  expect(state.session.token).toBe("t1");
232
232
  }
233
233
  });
@@ -296,7 +296,7 @@ describe("IdentityClient", () => {
296
296
  versionStatus: IdentifyVersionStatusEnum.UPDATE_AVAILABLE,
297
297
  sessionId: "custom-session",
298
298
  deviceId: "custom-device",
299
- personaId: "custom-persona",
299
+ user_id: "custom-user_id",
300
300
  token: "custom-token",
301
301
  });
302
302
  const { client } = createTestClient({ api: mockApi });
@@ -307,7 +307,7 @@ describe("IdentityClient", () => {
307
307
  if (result.success) {
308
308
  expect(result.data.session_id).toBe("custom-session");
309
309
  expect(result.data.device_id).toBe("custom-device");
310
- expect(result.data.persona_id).toBe("custom-persona");
310
+ expect(result.data.user_id).toBe("custom-user_id");
311
311
  expect(result.data.token).toBe("custom-token");
312
312
  expect(result.data.version_info.status).toBe(IdentifyVersionStatusEnum.UPDATE_AVAILABLE);
313
313
  expect(result.data.version_info.update).toBeNull();
@@ -360,7 +360,7 @@ describe("IdentityClient", () => {
360
360
  const mockStorage = createMockStorageClient();
361
361
  const storedState = {
362
362
  type: "identified",
363
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
363
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
364
364
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE, update: null },
365
365
  };
366
366
  mockStorage.getStorage().set(IDENTIFY_STORAGE_KEY, JSON.stringify(storedState));
@@ -484,7 +484,7 @@ describe("IdentityClient", () => {
484
484
 
485
485
  const persona: Persona = {
486
486
  name: "John Doe",
487
- user_id: "user-456",
487
+ user_id: "user-123",
488
488
  email: "john@example.com",
489
489
  };
490
490
 
@@ -566,7 +566,7 @@ describe("IdentityClient", () => {
566
566
  const mockStorage = createMockStorageClient();
567
567
  const storedState = {
568
568
  type: "identified",
569
- session: { session_id: "s1", device_id: "d1", persona_id: "p1", token: "t1" },
569
+ session: { session_id: "s1", device_id: "d1", user_id: "p1", token: "t1" },
570
570
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE, update: null },
571
571
  };
572
572
  mockStorage.getStorage().set(IDENTIFY_STORAGE_KEY, JSON.stringify(storedState));
@@ -608,7 +608,7 @@ describe("IdentityClient", () => {
608
608
  const debugLogs = mockLogging.getLogs().filter((l) => l.level === "debug");
609
609
  // The "identifying" to "identifying" won't happen since we start from "identified"
610
610
  // But we can check that state changes are logged properly
611
- expect(mockLogging.getLogs().some(l => l.level === "info")).toBe(true);
611
+ expect(mockLogging.getLogs().some(l => l.level === "debug")).toBe(true);
612
612
  });
613
613
  });
614
614
 
@@ -731,7 +731,7 @@ describe("IdentityClient", () => {
731
731
  data: {
732
732
  session_id: "refreshed-session",
733
733
  device_id: "device-123",
734
- persona_id: "persona-123",
734
+ user_id: "user-123",
735
735
  token: "token-123",
736
736
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE },
737
737
  },
@@ -873,7 +873,7 @@ describe("IdentityClient", () => {
873
873
  expect(session).not.toBeNull();
874
874
  expect(session?.session_id).toBe("session-123");
875
875
  expect(session?.device_id).toBe("device-123");
876
- expect(session?.persona_id).toBe("persona-123");
876
+ expect(session?.user_id).toBe("user-123");
877
877
  expect(session?.token).toBe("token-123");
878
878
  });
879
879
 
@@ -891,7 +891,7 @@ describe("IdentityClient", () => {
891
891
  data: {
892
892
  session_id: "second-session",
893
893
  device_id: "device-123",
894
- persona_id: "persona-123",
894
+ user_id: "user-123",
895
895
  token: "token-123",
896
896
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE },
897
897
  },
@@ -1002,7 +1002,7 @@ describe("IdentityClient", () => {
1002
1002
  data: {
1003
1003
  session_id: "session-123",
1004
1004
  device_id: "device-123",
1005
- persona_id: "persona-123",
1005
+ user_id: "user-123",
1006
1006
  token: "token-123",
1007
1007
  version_info: { status: IdentifyVersionStatusEnum.UP_TO_DATE },
1008
1008
  },
@@ -17,7 +17,7 @@ export type Persona = {
17
17
  export type IdentityUser = {
18
18
  session_id: string;
19
19
  device_id: string;
20
- persona_id: string;
20
+ user_id: string;
21
21
  token: string;
22
22
  version_info: {
23
23
  status: IdentifyVersionStatusEnum;
@@ -40,7 +40,7 @@ export const UpdateVersionStatusBodySchema = z.object({
40
40
  export const SessionSchema = z.object({
41
41
  session_id: z.string(),
42
42
  device_id: z.string(),
43
- persona_id: z.string(),
43
+ user_id: z.string(),
44
44
  token: z.string(),
45
45
  });
46
46
  export type Session = z.infer<typeof SessionSchema>;
@@ -211,8 +211,8 @@ export class IdentityClient {
211
211
  }
212
212
  }
213
213
 
214
- async identify(persona?: Persona): AsyncResult<IdentityUser> {
215
- this.logger.debug(`Identifying user with persona: ${persona?.name ?? "none"}`);
214
+ async identify(user?: Persona): AsyncResult<IdentityUser> {
215
+ this.logger.debug(`Identifying user with persona: ${user?.name ?? "none"}`);
216
216
  const previousState = this.identifyState;
217
217
  this.setIdentifyState({ type: "identifying" });
218
218
 
@@ -231,8 +231,7 @@ export class IdentityClient {
231
231
  "td-device-id": deviceId,
232
232
  },
233
233
  body: {
234
- persona,
235
- // @ts-expect-error - notifications is not yet implemented
234
+ user,
236
235
  device: {
237
236
  timestamp: deviceInfo.timestamp,
238
237
  os: deviceInfo.os,
@@ -21,29 +21,27 @@ export class AsyncStorageAdapter extends StorageAdapter {
21
21
  const prefixedKey = (key: string): string => `${storageKey}:${key}`;
22
22
 
23
23
  return {
24
- preload: () => {
24
+ preload: async (): Promise<void> => {
25
25
  if (hydrated) return;
26
26
 
27
- // Fire async hydration - cache will be populated when complete
28
- AsyncStorage.getAllKeys()
29
- .then((allKeys) => {
30
- const relevantKeys = allKeys.filter((k) =>
31
- k.startsWith(`${storageKey}:`)
32
- );
33
- return AsyncStorage.multiGet(relevantKeys);
34
- })
35
- .then((pairs) => {
36
- for (const [fullKey, value] of pairs) {
37
- if (value != null) {
38
- const key = fullKey.replace(`${storageKey}:`, "");
39
- cache[key] = value;
40
- }
27
+ try {
28
+ const allKeys = await AsyncStorage.getAllKeys();
29
+ const relevantKeys = allKeys.filter((k) =>
30
+ k.startsWith(`${storageKey}:`)
31
+ );
32
+ const pairs = await AsyncStorage.multiGet(relevantKeys);
33
+
34
+ for (const [fullKey, value] of pairs) {
35
+ if (value != null) {
36
+ const key = fullKey.replace(`${storageKey}:`, "");
37
+ cache[key] = value;
41
38
  }
42
- hydrated = true;
43
- })
44
- .catch(() => {
45
- // Silently fail - cache remains empty
46
- });
39
+ }
40
+ } catch {
41
+ // Silently fail - cache remains empty
42
+ } finally {
43
+ hydrated = true;
44
+ }
47
45
  },
48
46
 
49
47
  getItem: (key: string): string | null => {
@@ -3,7 +3,7 @@
3
3
  * A storage interface that is used to store data.
4
4
  */
5
5
  export type SupportedStorage = {
6
- preload: () => void;
6
+ preload: () => void | Promise<void>;
7
7
  getItem: (key: string) => string | null;
8
8
  setItem: (key: string, value: string) => void;
9
9
  removeItem: (key: string) => void;
@@ -8,6 +8,14 @@ export class StorageClient {
8
8
 
9
9
  private readonly storage: Map<string, SupportedStorage> = new Map();
10
10
 
11
+ private readonly preloadPromises: Promise<void>[] = [];
12
+
13
+ private _isReady = false;
14
+
15
+ get isReady(): boolean {
16
+ return this._isReady;
17
+ }
18
+
11
19
  constructor(
12
20
  logging: LoggingClient,
13
21
  private readonly orgId: string,
@@ -40,7 +48,10 @@ export class StorageClient {
40
48
 
41
49
  this.logger.debug(`Creating new storage for ${fullStorageKey}`);
42
50
  const newStorage = this.storageAdapter.createStorage(fullStorageKey);
43
- newStorage.preload();
51
+ const preloadResult = newStorage.preload();
52
+ if (preloadResult instanceof Promise) {
53
+ this.preloadPromises.push(preloadResult);
54
+ }
44
55
 
45
56
  const remappedStorage = {
46
57
  ...newStorage,
@@ -55,6 +66,11 @@ export class StorageClient {
55
66
  return remappedStorage;
56
67
  }
57
68
 
69
+ async whenReady(): Promise<void> {
70
+ await Promise.all(this.preloadPromises);
71
+ this._isReady = true;
72
+ }
73
+
58
74
  shutdown(): void {
59
75
  this.storage.forEach((storage) => {
60
76
  storage.clear();
@@ -69,7 +69,9 @@ export class TeardownCore {
69
69
  }
70
70
 
71
71
  async initialize(): Promise<void> {
72
- // Initialize identity first (loads from storage, then identifies if needed)
72
+ // Wait for all storage hydration to complete
73
+ await this.storage.whenReady();
74
+ // Initialize identity (loads from storage, then identifies if needed)
73
75
  await this.identity.initialize();
74
76
  // Then initialize force update (subscribes to identity events)
75
77
  this.forceUpdate.initialize();