@teardown/react-native 2.0.29 → 2.0.32

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.
@@ -21,8 +21,11 @@ new TeardownCore(options: TeardownCoreOptions)
21
21
  | `org_id` | `string` | Yes | Organization ID from dashboard |
22
22
  | `project_id` | `string` | Yes | Project ID from dashboard |
23
23
  | `api_key` | `string` | Yes | API key from dashboard |
24
+ | `environment_slug` | `string` | No | Environment slug (default: "production") |
25
+ | `ingestUrl` | `string` | No | Custom ingest API URL for local/staging |
24
26
  | `storageAdapter` | `StorageAdapter` | Yes | Storage adapter instance |
25
27
  | `deviceAdapter` | `DeviceInfoAdapter` | Yes | Device info adapter instance |
28
+ | `notificationAdapter` | `NotificationAdapter` | No | Push notification adapter instance |
26
29
  | `forceUpdate` | `ForceUpdateClientOptions` | No | Force update configuration |
27
30
 
28
31
  ### Methods
@@ -38,8 +41,10 @@ new TeardownCore(options: TeardownCoreOptions)
38
41
  |----------|------|-------------|
39
42
  | `api` | `ApiClient` | Backend API client |
40
43
  | `device` | `DeviceClient` | Device information client |
44
+ | `events` | `EventsClient` | Event tracking client |
41
45
  | `identity` | `IdentityClient` | Identity management client |
42
46
  | `forceUpdate` | `ForceUpdateClient` | Version checking client |
47
+ | `notifications` | `NotificationsClient \| undefined` | Push notifications client (if adapter provided) |
43
48
 
44
49
  ---
45
50
 
@@ -53,7 +58,8 @@ Manages user and device identification.
53
58
  |--------|---------|-------------|
54
59
  | `identify(persona?)` | `AsyncResult<IdentityUser>` | Identify user/device |
55
60
  | `refresh()` | `AsyncResult<IdentityUser>` | Re-identify current user |
56
- | `reset()` | `void` | Clear session data |
61
+ | `signOut(options?)` | `AsyncResult<void>` | Sign out user, preserve device |
62
+ | `signOutAll(options?)` | `AsyncResult<void>` | Sign out and reset device |
57
63
  | `getIdentifyState()` | `IdentifyState` | Get current state |
58
64
  | `getSessionState()` | `Session \| null` | Get current session |
59
65
  | `onIdentifyStateChange(listener)` | `() => void` | Subscribe to state changes |
@@ -89,6 +95,17 @@ type IdentifyState =
89
95
  | { type: 'identified'; session: Session; version_info: VersionInfo };
90
96
  ```
91
97
 
98
+ ### SignOutOptions
99
+
100
+ ```typescript
101
+ interface SignOutOptions {
102
+ // Additional properties to include in the sign out event
103
+ properties?: Record<string, unknown>;
104
+ // Wait for event to be sent before clearing state (default: true)
105
+ waitForEvent?: boolean;
106
+ }
107
+ ```
108
+
92
109
  ---
93
110
 
94
111
  ## ForceUpdateClient
@@ -118,14 +135,144 @@ type VersionStatus =
118
135
  | { type: 'initializing' }
119
136
  | { type: 'checking' }
120
137
  | { type: 'up_to_date' }
121
- | { type: 'update_available' }
122
- | { type: 'update_recommended' }
123
- | { type: 'update_required' }
138
+ | { type: 'update_available'; releaseNotes?: string | null }
139
+ | { type: 'update_recommended'; releaseNotes?: string | null }
140
+ | { type: 'update_required'; releaseNotes?: string | null }
124
141
  | { type: 'disabled' };
125
142
  ```
126
143
 
127
144
  ---
128
145
 
146
+ ## EventsClient
147
+
148
+ Tracks events to the backend.
149
+
150
+ ### Methods
151
+
152
+ | Method | Returns | Description |
153
+ |--------|---------|-------------|
154
+ | `track(event, sessionId?)` | `AsyncResult<void>` | Track a single event |
155
+ | `trackBatch(events, sessionId?)` | `AsyncResult<void>` | Track multiple events |
156
+
157
+ ### EventPayload
158
+
159
+ ```typescript
160
+ interface EventPayload {
161
+ /** Name of the event (e.g., "button_clicked", "page_viewed") */
162
+ event_name: string;
163
+ /** Type of event */
164
+ event_type?: "action" | "screen_view" | "custom";
165
+ /** Additional properties to attach to the event */
166
+ properties?: Record<string, unknown>;
167
+ /** ISO timestamp. Defaults to current time if not provided */
168
+ timestamp?: string;
169
+ }
170
+ ```
171
+
172
+ ### Usage
173
+
174
+ ```typescript
175
+ const { core } = useTeardown();
176
+
177
+ // Track a single event
178
+ await core.events.track({
179
+ event_name: 'button_clicked',
180
+ event_type: 'action',
181
+ properties: { button_id: 'submit-form' }
182
+ });
183
+
184
+ // Track multiple events
185
+ await core.events.trackBatch([
186
+ { event_name: 'page_viewed', event_type: 'screen_view', properties: { page: 'home' } },
187
+ { event_name: 'cta_shown', event_type: 'action' }
188
+ ]);
189
+ ```
190
+
191
+ ---
192
+
193
+ ## NotificationsClient
194
+
195
+ Manages push notifications across different providers. Only available if `notificationAdapter` is provided in TeardownCore options.
196
+
197
+ ### Methods
198
+
199
+ | Method | Returns | Description |
200
+ |--------|---------|-------------|
201
+ | `requestPermissions()` | `Promise<PermissionStatus>` | Request notification permissions |
202
+ | `getToken()` | `Promise<string \| null>` | Get push notification token |
203
+ | `onTokenChange(listener)` | `Unsubscribe` | Subscribe to token changes |
204
+ | `onNotificationReceived(listener)` | `Unsubscribe` | Subscribe to foreground notifications |
205
+ | `onNotificationOpened(listener)` | `Unsubscribe` | Subscribe to notification taps |
206
+ | `onDataMessage(listener)` | `Unsubscribe` | Subscribe to silent/data messages |
207
+ | `destroy()` | `void` | Cleanup resources |
208
+
209
+ ### Properties
210
+
211
+ | Property | Type | Description |
212
+ |----------|------|-------------|
213
+ | `platform` | `NotificationPlatformEnum` | Notification platform (APNS, FCM, EXPO) |
214
+
215
+ ### PermissionStatus
216
+
217
+ ```typescript
218
+ interface PermissionStatus {
219
+ granted: boolean;
220
+ canAskAgain: boolean;
221
+ }
222
+ ```
223
+
224
+ ### PushNotification
225
+
226
+ ```typescript
227
+ interface PushNotification {
228
+ title?: string;
229
+ body?: string;
230
+ data?: Record<string, unknown>;
231
+ }
232
+ ```
233
+
234
+ ### DataMessage
235
+
236
+ ```typescript
237
+ interface DataMessage {
238
+ data: Record<string, unknown>;
239
+ }
240
+ ```
241
+
242
+ ### NotificationPlatformEnum
243
+
244
+ ```typescript
245
+ enum NotificationPlatformEnum {
246
+ APNS = "APNS", // Apple Push Notification Service
247
+ FCM = "FCM", // Firebase Cloud Messaging
248
+ EXPO = "EXPO", // Expo Push Notifications
249
+ }
250
+ ```
251
+
252
+ ### Usage
253
+
254
+ ```typescript
255
+ const { core } = useTeardown();
256
+
257
+ if (core.notifications) {
258
+ // Request permissions
259
+ const status = await core.notifications.requestPermissions();
260
+
261
+ // Get token for backend
262
+ const token = await core.notifications.getToken();
263
+
264
+ // Listen for foreground notifications
265
+ const unsub = core.notifications.onNotificationReceived((notification) => {
266
+ console.log('Notification:', notification.title);
267
+ });
268
+
269
+ // Cleanup
270
+ unsub();
271
+ }
272
+ ```
273
+
274
+ ---
275
+
129
276
  ## DeviceClient
130
277
 
131
278
  Collects device information.
@@ -136,15 +283,18 @@ Collects device information.
136
283
  |--------|---------|-------------|
137
284
  | `getDeviceId()` | `Promise<string>` | Get stable device UUID |
138
285
  | `getDeviceInfo()` | `Promise<DeviceInfo>` | Get full device info |
286
+ | `reset()` | `void` | Clear stored deviceId (new ID on next call) |
139
287
 
140
288
  ### DeviceInfo
141
289
 
142
290
  ```typescript
143
291
  interface DeviceInfo {
144
- timestamp: string;
292
+ timestamp?: Date;
145
293
  os: OSInfo;
146
294
  hardware: HardwareInfo;
147
295
  application: ApplicationInfo;
296
+ update: DeviceUpdateInfo | null;
297
+ notifications?: NotificationsInfo;
148
298
  }
149
299
  ```
150
300
 
@@ -192,8 +342,7 @@ Interface for device information adapters.
192
342
  ```typescript
193
343
  interface ApplicationInfo {
194
344
  version: string;
195
- buildNumber: string;
196
- bundleId: string;
345
+ build_number: number;
197
346
  }
198
347
  ```
199
348
 
@@ -201,9 +350,9 @@ interface ApplicationInfo {
201
350
 
202
351
  ```typescript
203
352
  interface HardwareInfo {
204
- deviceName: string;
205
- brand: string;
206
- deviceType: string;
353
+ device_name: string;
354
+ device_brand: string;
355
+ device_type: string;
207
356
  }
208
357
  ```
209
358
 
@@ -211,8 +360,24 @@ interface HardwareInfo {
211
360
 
212
361
  ```typescript
213
362
  interface OSInfo {
214
- osName: string;
215
- osVersion: string;
363
+ platform: DevicePlatformEnum;
364
+ name: string;
365
+ version: string;
366
+ }
367
+ ```
368
+
369
+ ### DevicePlatformEnum
370
+
371
+ ```typescript
372
+ enum DevicePlatformEnum {
373
+ IOS = "IOS",
374
+ ANDROID = "ANDROID",
375
+ WEB = "WEB",
376
+ WINDOWS = "WINDOWS",
377
+ MACOS = "MACOS",
378
+ LINUX = "LINUX",
379
+ UNKNOWN = "UNKNOWN",
380
+ // ... and more
216
381
  }
217
382
  ```
218
383
 
@@ -10,12 +10,14 @@ The Teardown SDK is built around a central `TeardownCore` class that orchestrate
10
10
 
11
11
  ```
12
12
  TeardownCore
13
- ├── IdentityClient # User/device identification
14
- ├── ForceUpdateClient # Version checking
15
- ├── DeviceClient # Device information
16
- ├── StorageClient # Persistent storage
17
- ├── ApiClient # Backend communication
18
- └── LoggingClient # Structured logging
13
+ ├── IdentityClient # User/device identification
14
+ ├── ForceUpdateClient # Version checking
15
+ ├── DeviceClient # Device information
16
+ ├── EventsClient # Event tracking
17
+ ├── NotificationsClient # Push notifications (optional)
18
+ ├── StorageClient # Persistent storage
19
+ ├── ApiClient # Backend communication
20
+ └── LoggingClient # Structured logging
19
21
  ```
20
22
 
21
23
  ## Initialization Flow
@@ -58,9 +60,9 @@ type VersionStatus =
58
60
  | { type: 'initializing' }
59
61
  | { type: 'checking' }
60
62
  | { type: 'up_to_date' }
61
- | { type: 'update_available' }
62
- | { type: 'update_recommended' }
63
- | { type: 'update_required' }
63
+ | { type: 'update_available'; releaseNotes?: string | null }
64
+ | { type: 'update_recommended'; releaseNotes?: string | null }
65
+ | { type: 'update_required'; releaseNotes?: string | null }
64
66
  | { type: 'disabled' };
65
67
  ```
66
68
 
@@ -88,10 +90,26 @@ interface SupportedStorage {
88
90
  Provide device and app information:
89
91
 
90
92
  ```typescript
91
- interface DeviceInfoAdapter {
92
- applicationInfo: ApplicationInfo; // version, buildNumber, bundleId
93
- hardwareInfo: HardwareInfo; // deviceName, brand, deviceType
94
- osInfo: OSInfo; // osName, osVersion
93
+ abstract class DeviceInfoAdapter {
94
+ applicationInfo: ApplicationInfo; // version, build_number
95
+ hardwareInfo: HardwareInfo; // device_name, device_brand, device_type
96
+ osInfo: OSInfo; // platform, name, version
97
+ }
98
+ ```
99
+
100
+ ### Notification Adapters
101
+
102
+ Handle push notification registration and events (optional):
103
+
104
+ ```typescript
105
+ abstract class NotificationAdapter {
106
+ platform: NotificationPlatformEnum;
107
+ getToken(): Promise<string | null>;
108
+ requestPermissions(): Promise<PermissionStatus>;
109
+ onTokenRefresh(listener): Unsubscribe;
110
+ onNotificationReceived(listener): Unsubscribe;
111
+ onNotificationOpened(listener): Unsubscribe;
112
+ onDataMessage(listener): Unsubscribe;
95
113
  }
96
114
  ```
97
115
 
@@ -21,12 +21,14 @@ type VersionStatus =
21
21
  | { type: 'initializing' } // SDK starting up
22
22
  | { type: 'checking' } // Version check in progress
23
23
  | { type: 'up_to_date' } // Current version is valid
24
- | { type: 'update_available' } // Optional update exists
25
- | { type: 'update_recommended' } // Recommended update exists
26
- | { type: 'update_required' } // Must update to continue
24
+ | { type: 'update_available'; releaseNotes?: string | null } // Optional update exists
25
+ | { type: 'update_recommended'; releaseNotes?: string | null } // Recommended update exists
26
+ | { type: 'update_required'; releaseNotes?: string | null } // Must update to continue
27
27
  | { type: 'disabled' } // Version/build has been disabled
28
28
  ```
29
29
 
30
+ Update status types include an optional `releaseNotes` field containing notes set in the dashboard when changing version/build status.
31
+
30
32
  ## Basic Usage
31
33
 
32
34
  ### Using the Hook
@@ -35,16 +37,16 @@ type VersionStatus =
35
37
  import { useForceUpdate } from '@teardown/react-native';
36
38
 
37
39
  function App() {
38
- const { versionStatus, isUpdateRequired, isUpdateAvailable, isUpdateRecommended } = useForceUpdate();
40
+ const { versionStatus, isUpdateRequired, isUpdateAvailable, isUpdateRecommended, releaseNotes } = useForceUpdate();
39
41
 
40
42
  if (isUpdateRequired) {
41
- return <ForceUpdateModal />;
43
+ return <ForceUpdateModal releaseNotes={releaseNotes} />;
42
44
  }
43
45
 
44
46
  if (isUpdateRecommended) {
45
47
  return (
46
48
  <>
47
- <UpdateBanner onDismiss={() => {}} />
49
+ <UpdateBanner releaseNotes={releaseNotes} onDismiss={() => {}} />
48
50
  <MainApp />
49
51
  </>
50
52
  );
@@ -62,6 +64,7 @@ interface UseForceUpdateResult {
62
64
  isUpdateAvailable: boolean; // Any update exists (available, recommended, or required)
63
65
  isUpdateRecommended: boolean; // Update is recommended but not required
64
66
  isUpdateRequired: boolean; // Update is mandatory
67
+ releaseNotes: string | null; // Release notes for the update, if available
65
68
  }
66
69
  ```
67
70
 
@@ -114,7 +117,7 @@ unsubscribe();
114
117
  ### Force Update Modal
115
118
 
116
119
  ```tsx
117
- function ForceUpdateModal() {
120
+ function ForceUpdateModal({ releaseNotes }: { releaseNotes: string | null }) {
118
121
  const handleUpdate = () => {
119
122
  // Open app store
120
123
  Linking.openURL('https://apps.apple.com/app/your-app');
@@ -125,6 +128,12 @@ function ForceUpdateModal() {
125
128
  <View style={styles.container}>
126
129
  <Text>Update Required</Text>
127
130
  <Text>Please update to continue using the app.</Text>
131
+ {releaseNotes && (
132
+ <View style={styles.releaseNotes}>
133
+ <Text style={styles.releaseNotesTitle}>What's New:</Text>
134
+ <Text>{releaseNotes}</Text>
135
+ </View>
136
+ )}
128
137
  <Button onPress={handleUpdate} title="Update Now" />
129
138
  </View>
130
139
  </Modal>
@@ -135,7 +144,7 @@ function ForceUpdateModal() {
135
144
  ### Soft Update Banner
136
145
 
137
146
  ```tsx
138
- function UpdateBanner({ onDismiss }: { onDismiss: () => void }) {
147
+ function UpdateBanner({ releaseNotes, onDismiss }: { releaseNotes: string | null; onDismiss: () => void }) {
139
148
  const { isUpdateRecommended } = useForceUpdate();
140
149
 
141
150
  if (!isUpdateRecommended) return null;
@@ -143,6 +152,7 @@ function UpdateBanner({ onDismiss }: { onDismiss: () => void }) {
143
152
  return (
144
153
  <View style={styles.banner}>
145
154
  <Text>A new version is available</Text>
155
+ {releaseNotes && <Text style={styles.notes}>{releaseNotes}</Text>}
146
156
  <Button onPress={onDismiss} title="Later" />
147
157
  <Button onPress={() => Linking.openURL('...')} title="Update" />
148
158
  </View>
@@ -105,7 +105,7 @@ Returns `null` if not identified.
105
105
 
106
106
  The hook subscribes to `identity.onIdentifyStateChange()` and re-renders when:
107
107
  - User identifies (session available)
108
- - User resets (session becomes null)
108
+ - User signs out (session becomes null)
109
109
  - Session refreshes (new session data)
110
110
 
111
111
  ---
@@ -122,17 +122,18 @@ function App() {
122
122
  versionStatus,
123
123
  isUpdateAvailable,
124
124
  isUpdateRecommended,
125
- isUpdateRequired
125
+ isUpdateRequired,
126
+ releaseNotes
126
127
  } = useForceUpdate();
127
128
 
128
129
  if (isUpdateRequired) {
129
- return <ForceUpdateModal />;
130
+ return <ForceUpdateModal releaseNotes={releaseNotes} />;
130
131
  }
131
132
 
132
133
  if (isUpdateRecommended) {
133
134
  return (
134
135
  <>
135
- <UpdateBanner />
136
+ <UpdateBanner releaseNotes={releaseNotes} />
136
137
  <MainApp />
137
138
  </>
138
139
  );
@@ -154,6 +155,8 @@ interface UseForceUpdateResult {
154
155
  isUpdateRecommended: boolean;
155
156
  /** Update is mandatory */
156
157
  isUpdateRequired: boolean;
158
+ /** Release notes for the update, if available (set in dashboard when changing status) */
159
+ releaseNotes: string | null;
157
160
  }
158
161
  ```
159
162
 
@@ -222,8 +225,8 @@ function App() {
222
225
  function LogoutButton() {
223
226
  const { core } = useTeardown();
224
227
 
225
- const handleLogout = () => {
226
- core.identity.reset();
228
+ const handleLogout = async () => {
229
+ await core.identity.signOut();
227
230
  // Session will become null, useSession will re-render
228
231
  };
229
232
 
package/docs/identity.mdx CHANGED
@@ -58,12 +58,51 @@ if (session) {
58
58
  }
59
59
  ```
60
60
 
61
- ### Reset Identity
61
+ ### Sign Out
62
62
 
63
- Clear the current session and device association:
63
+ Sign out the current user while preserving the device identity:
64
64
 
65
65
  ```typescript
66
- core.identity.reset();
66
+ const result = await core.identity.signOut();
67
+
68
+ if (result.success) {
69
+ console.log('Signed out successfully');
70
+ } else {
71
+ console.error('Sign out failed:', result.error);
72
+ }
73
+ ```
74
+
75
+ This clears the session and user association but keeps the device ID. The same device will be recognized on the next `identify()` call.
76
+
77
+ ### Sign Out All (Full Reset)
78
+
79
+ Sign out and reset all SDK state including the device ID:
80
+
81
+ ```typescript
82
+ const result = await core.identity.signOutAll();
83
+ ```
84
+
85
+ This clears everything - the device will appear as a fresh install on the next `identify()` call.
86
+
87
+ ### Sign Out Options
88
+
89
+ Both `signOut()` and `signOutAll()` accept optional configuration:
90
+
91
+ ```typescript
92
+ interface SignOutOptions {
93
+ // Additional properties to include in the sign out event
94
+ properties?: Record<string, unknown>;
95
+ // Wait for event to be sent before clearing state (default: true)
96
+ waitForEvent?: boolean;
97
+ }
98
+
99
+ // Example: Fire-and-forget sign out
100
+ await core.identity.signOut({ waitForEvent: false });
101
+
102
+ // Example: Include custom properties
103
+ await core.identity.signOut({
104
+ properties: { reason: 'user_requested' }
105
+ });
67
106
  ```
68
107
 
69
108
  ### Refresh Session
@@ -166,6 +205,7 @@ if (!result.success) {
166
205
  ## Best Practices
167
206
 
168
207
  1. **Call identify on login** - Associate users with their devices
169
- 2. **Call reset on logout** - Clear session data
170
- 3. **Handle offline** - The SDK caches session data for offline use
171
- 4. **Use useSession hook** - For reactive UI updates
208
+ 2. **Call signOut on logout** - Clear session data and notify the backend
209
+ 3. **Use signOutAll for account switching** - When users switch accounts, use `signOutAll()` for a clean slate
210
+ 4. **Handle offline** - The SDK caches session data for offline use
211
+ 5. **Use useSession hook** - For reactive UI updates
package/docs/logging.mdx CHANGED
@@ -54,17 +54,6 @@ The internal `Logger` class provides these methods:
54
54
  | `debug()` | verbose | `console.debug` |
55
55
  | `trace()` | verbose | `console.trace` |
56
56
 
57
- ### Debug Variants
58
-
59
- Additional debug methods for categorized output:
60
-
61
- ```typescript
62
- logger.debugError('Something went wrong', { error }); // Error: [prefix] ...
63
- logger.debugWarn('Warning message'); // Warning: [prefix] ...
64
- logger.debugInfo('Info message'); // Info: [prefix] ...
65
- logger.debugVerbose('Verbose details'); // Verbose: [prefix] ...
66
- ```
67
-
68
57
  ## Production Logging
69
58
 
70
59
  For production builds, set log level to `none` or `error`:
@@ -135,10 +124,11 @@ class Logger {
135
124
  1. **Use `verbose` in development** - See all SDK activity
136
125
  2. **Use `error` in production** - Minimize noise, catch issues
137
126
  3. **Filter by prefix** - Isolate SDK logs from app logs
138
- 4. **Check log level first** - Avoid expensive string operations:
127
+ 4. **Set log level based on environment**:
139
128
 
140
129
  ```typescript
141
- if (core.logging.shouldLog('verbose')) {
142
- console.log('Complex debug:', JSON.stringify(largeObject));
143
- }
130
+ const teardown = new TeardownCore({ /* ... */ });
131
+
132
+ // Set based on environment
133
+ teardown.setLogLevel(__DEV__ ? 'verbose' : 'error');
144
134
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teardown/react-native",
3
- "version": "2.0.29",
3
+ "version": "2.0.32",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -52,9 +52,9 @@
52
52
  "prepublishOnly": "bun run build"
53
53
  },
54
54
  "dependencies": {
55
- "@teardown/ingest-api": "2.0.29",
56
- "@teardown/schemas": "2.0.29",
57
- "@teardown/types": "2.0.29",
55
+ "@teardown/ingest-api": "2.0.32",
56
+ "@teardown/schemas": "2.0.32",
57
+ "@teardown/types": "2.0.32",
58
58
  "eventemitter3": "^5.0.1",
59
59
  "react-native-get-random-values": "^2.0.0",
60
60
  "uuid": "^13.0.0",
@@ -64,7 +64,7 @@
64
64
  "@biomejs/biome": "2.3.10",
65
65
  "@elysiajs/eden": "1.4.5",
66
66
  "@react-native-firebase/messaging": "*",
67
- "@teardown/tsconfig": "2.0.29",
67
+ "@teardown/tsconfig": "2.0.32",
68
68
  "@types/bun": "1.3.5",
69
69
  "@types/react": "~19.1.0",
70
70
  "@types/uuid": "^11.0.0",
@@ -77,4 +77,8 @@ export class ApiClient {
77
77
  get environmentSlug(): string {
78
78
  return this.options.environment_slug ?? "production";
79
79
  }
80
+
81
+ get ingestUrl(): string {
82
+ return this.options.ingestUrl ?? TEARDOWN_INGEST_URL;
83
+ }
80
84
  }
@@ -67,4 +67,13 @@ export class DeviceClient {
67
67
  async getDeviceInfo(): Promise<DeviceInfo> {
68
68
  return this.options.adapter.getDeviceInfo();
69
69
  }
70
+
71
+ /**
72
+ * Reset the device client by clearing the stored deviceId.
73
+ * This will generate a new deviceId on next getDeviceId() call.
74
+ */
75
+ reset(): void {
76
+ this.logger.debug("Resetting device - clearing deviceId");
77
+ this.storage.removeItem("deviceId");
78
+ }
70
79
  }