@teardown/react-native 2.0.0 → 2.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.
package/README.md CHANGED
@@ -28,32 +28,46 @@ bun add react-native-device-info
28
28
 
29
29
  ## Quick Start
30
30
 
31
+ ### 1. Initialize the SDK
32
+
31
33
  ```tsx
32
- import { TeardownCore, TeardownProvider, useTeardown } from '@teardown/react-native';
33
-
34
- // Initialize
35
- const teardown = new TeardownCore({
36
- api: {
37
- api_key: 'your-api-key',
38
- org_id: 'your-org-id',
39
- project_id: 'your-project-id',
40
- environment_slug: 'production',
34
+ import { TeardownCore } from '@teardown/react-native';
35
+ import { ExpoDeviceAdapter } from '@teardown/react-native/expo';
36
+ import { createMMKVStorageFactory } from '@teardown/react-native/mmkv';
37
+
38
+ export const teardown = new TeardownCore({
39
+ org_id: 'your-org-id',
40
+ project_id: 'your-project-id',
41
+ api_key: 'your-api-key',
42
+ storageFactory: createMMKVStorageFactory(),
43
+ deviceAdapter: new ExpoDeviceAdapter(),
44
+ forceUpdate: {
45
+ throttleMs: 30_000, // 30 seconds
46
+ checkCooldownMs: 10_000, // 10 seconds
41
47
  },
42
- storage: { /* ... */ },
43
- device: { /* ... */ },
44
- identity: { identifyOnLoad: true },
45
48
  });
49
+ ```
50
+
51
+ ### 2. Wrap your app with TeardownProvider
52
+
53
+ ```tsx
54
+ import { TeardownProvider } from '@teardown/react-native';
55
+ import { teardown } from './lib/teardown';
46
56
 
47
- // Wrap your app
48
- export default function App() {
57
+ export default function RootLayout() {
49
58
  return (
50
59
  <TeardownProvider core={teardown}>
51
60
  <YourApp />
52
61
  </TeardownProvider>
53
62
  );
54
63
  }
64
+ ```
65
+
66
+ ### 3. Use in components
67
+
68
+ ```tsx
69
+ import { useTeardown } from '@teardown/react-native';
55
70
 
56
- // Use in components
57
71
  function YourComponent() {
58
72
  const { core } = useTeardown();
59
73
 
@@ -78,10 +92,10 @@ Complete documentation is available in the [docs](./docs) folder:
78
92
  - [Force Updates](./docs/04-force-updates.mdx) - Version management
79
93
  - [Device Information](./docs/05-device-info.mdx) - Device data collection
80
94
  - [Storage](./docs/06-storage.mdx) - Persistent storage
81
- - [Logging](./docs/06-logging.mdx) - Structured logging
82
- - [API Reference](./docs/07-api-reference.mdx) - Complete API docs
83
- - [Hooks Reference](./docs/08-hooks-reference.mdx) - React hooks
84
- - [Advanced Usage](./docs/09-advanced.mdx) - Advanced patterns
95
+ - [Logging](./docs/07-logging.mdx) - Structured logging
96
+ - [API Reference](./docs/08-api-reference.mdx) - Complete API docs
97
+ - [Hooks Reference](./docs/09-hooks-reference.mdx) - React hooks
98
+ - [Advanced Usage](./docs/10-advanced.mdx) - Advanced patterns
85
99
 
86
100
  ## License
87
101
 
@@ -0,0 +1,147 @@
1
+ # Getting Started
2
+
3
+ This guide will walk you through installing and setting up the Teardown SDK in your React Native or Expo application.
4
+
5
+ ## Installation
6
+
7
+ Install the core package:
8
+
9
+ ```bash
10
+ bun add @teardown/react-native
11
+ ```
12
+
13
+ ### Peer Dependencies
14
+
15
+ Install required peer dependencies:
16
+
17
+ ```bash
18
+ bun add react react-native zod
19
+ ```
20
+
21
+ ### Platform Adapters
22
+
23
+ #### For Expo Projects
24
+
25
+ ```bash
26
+ bun add expo-application expo-device expo-updates
27
+ ```
28
+
29
+ #### For React Native CLI Projects
30
+
31
+ ```bash
32
+ bun add react-native-device-info
33
+ ```
34
+
35
+ ### Storage Adapter
36
+
37
+ Install MMKV for fast, encrypted storage:
38
+
39
+ ```bash
40
+ bun add react-native-mmkv
41
+ ```
42
+
43
+ ## Initial Setup
44
+
45
+ ### 1. Create SDK Configuration
46
+
47
+ Create a file to initialize the Teardown SDK (e.g., `lib/teardown.ts`):
48
+
49
+ ```typescript
50
+ import { TeardownCore } from '@teardown/react-native';
51
+ import { ExpoDeviceAdapter } from '@teardown/react-native/expo';
52
+ import { createMMKVStorageFactory } from '@teardown/react-native/mmkv';
53
+
54
+ export const teardown = new TeardownCore({
55
+ org_id: 'your-org-id',
56
+ project_id: 'your-project-id',
57
+ api_key: 'your-api-key',
58
+ storageFactory: createMMKVStorageFactory(),
59
+ deviceAdapter: new ExpoDeviceAdapter(),
60
+ forceUpdate: {
61
+ throttleMs: 30_000, // Check every 30 seconds when app returns to foreground
62
+ checkCooldownMs: 300_000, // Wait 5 minutes between checks
63
+ },
64
+ });
65
+ ```
66
+
67
+ ### 2. Wrap Your App with TeardownProvider
68
+
69
+ In your root layout file (e.g., `app/_layout.tsx` for Expo Router):
70
+
71
+ ```typescript
72
+ import { TeardownProvider } from '@teardown/react-native';
73
+ import { teardown } from '../lib/teardown';
74
+
75
+ export default function RootLayout() {
76
+ return (
77
+ <TeardownProvider core={teardown}>
78
+ <YourApp />
79
+ </TeardownProvider>
80
+ );
81
+ }
82
+ ```
83
+
84
+ For traditional React Native:
85
+
86
+ ```typescript
87
+ import { TeardownProvider } from '@teardown/react-native';
88
+ import { teardown } from './lib/teardown';
89
+
90
+ export default function App() {
91
+ return (
92
+ <TeardownProvider core={teardown}>
93
+ <YourNavigationStack />
94
+ </TeardownProvider>
95
+ );
96
+ }
97
+ ```
98
+
99
+ ### 3. Use the SDK in Your Components
100
+
101
+ ```typescript
102
+ import { useTeardown } from '@teardown/react-native';
103
+
104
+ function MyComponent() {
105
+ const { core } = useTeardown();
106
+
107
+ const handleLogin = async () => {
108
+ const result = await core.identity.identify({
109
+ user_id: 'user-123',
110
+ email: 'user@example.com',
111
+ name: 'John Doe',
112
+ });
113
+
114
+ if (result.success) {
115
+ console.log('User identified:', result.data);
116
+ }
117
+ };
118
+
119
+ return <Button onPress={handleLogin} title="Login" />;
120
+ }
121
+ ```
122
+
123
+ ## Configuration Options
124
+
125
+ ### TeardownCore Options
126
+
127
+ | Option | Type | Required | Description |
128
+ |--------|------|----------|-------------|
129
+ | `org_id` | string | Yes | Your organization ID from Teardown dashboard |
130
+ | `project_id` | string | Yes | Your project ID from Teardown dashboard |
131
+ | `api_key` | string | Yes | Your API key from Teardown dashboard |
132
+ | `storageFactory` | function | Yes | Storage adapter factory (e.g., MMKV) |
133
+ | `deviceAdapter` | object | Yes | Device info adapter (Expo or RN) |
134
+ | `forceUpdate` | object | No | Force update configuration |
135
+
136
+ ### Force Update Options
137
+
138
+ | Option | Type | Default | Description |
139
+ |--------|------|---------|-------------|
140
+ | `throttleMs` | number | 30000 | Minimum time between foreground checks (ms) |
141
+ | `checkCooldownMs` | number | 300000 | Minimum time since last successful check (ms) |
142
+
143
+ ## Next Steps
144
+
145
+ - [Core Concepts](./02-core-concepts.mdx) - Understand the architecture
146
+ - [Identity & Authentication](./03-identity.mdx) - Manage user sessions
147
+ - [Force Updates](./04-force-updates.mdx) - Handle version management
@@ -0,0 +1,188 @@
1
+ # Core Concepts
2
+
3
+ Understanding the architecture and design principles of the Teardown SDK.
4
+
5
+ ## Architecture Overview
6
+
7
+ The Teardown SDK is built around a central `TeardownCore` instance that manages several specialized clients:
8
+
9
+ ```
10
+ TeardownCore
11
+ ├── IdentityClient - User and device identity
12
+ ├── ForceUpdateClient - Version management
13
+ ├── DeviceClient - Device information
14
+ ├── StorageClient - Persistent storage
15
+ ├── LoggingClient - Structured logging
16
+ └── ApiClient - API communication
17
+ ```
18
+
19
+ ## Core Principles
20
+
21
+ ### 1. Single Source of Truth
22
+
23
+ The `TeardownCore` instance is your single entry point to all SDK functionality. It's initialized once and passed through your app via the `TeardownProvider`.
24
+
25
+ ```typescript
26
+ const teardown = new TeardownCore({...});
27
+
28
+ // Use throughout your app
29
+ <TeardownProvider core={teardown}>
30
+ <App />
31
+ </TeardownProvider>
32
+ ```
33
+
34
+ ### 2. Automatic Initialization
35
+
36
+ The SDK automatically initializes when the `TeardownProvider` mounts:
37
+
38
+ - Loads persisted session data
39
+ - Identifies the device
40
+ - Checks version status
41
+ - Subscribes to app lifecycle events
42
+
43
+ ### 3. Reactive State Management
44
+
45
+ All clients use event emitters to notify of state changes. React hooks automatically subscribe to these events:
46
+
47
+ ```typescript
48
+ // Hooks automatically subscribe and cleanup
49
+ const session = useSession();
50
+ const { versionStatus } = useForceUpdate();
51
+ ```
52
+
53
+ ### 4. Namespaced Storage
54
+
55
+ Each client gets its own namespaced storage to prevent key collisions:
56
+
57
+ ```typescript
58
+ // Storage is automatically namespaced
59
+ teardown:v1:identity:IDENTIFY_STATE
60
+ teardown:v1:device:deviceId
61
+ teardown:v1:version:VERSION_STATUS
62
+ ```
63
+
64
+ ## Client Responsibilities
65
+
66
+ ### IdentityClient
67
+
68
+ Manages device and user identity:
69
+
70
+ - Generates unique device IDs
71
+ - Tracks user sessions
72
+ - Persists authentication state
73
+ - Provides persona management (anonymous or identified users)
74
+
75
+ ### ForceUpdateClient
76
+
77
+ Handles version management:
78
+
79
+ - Checks app version against backend
80
+ - Monitors app state changes
81
+ - Throttles version checks
82
+ - Emits update status changes
83
+
84
+ ### DeviceClient
85
+
86
+ Collects device information:
87
+
88
+ - OS version and platform
89
+ - Device model and manufacturer
90
+ - App version and build number
91
+ - Uses platform adapters for consistency
92
+
93
+ ### StorageClient
94
+
95
+ Provides persistent storage:
96
+
97
+ - Namespaced key-value storage
98
+ - Platform-agnostic interface
99
+ - Support for multiple adapters (MMKV, AsyncStorage, etc.)
100
+ - Automatic cleanup on shutdown
101
+
102
+ ### LoggingClient
103
+
104
+ Structured logging system:
105
+
106
+ - Configurable log levels
107
+ - Named loggers for each client
108
+ - Console binding preservation
109
+ - Debug mode support
110
+
111
+ ### ApiClient
112
+
113
+ Handles API communication:
114
+
115
+ - Type-safe API client
116
+ - Automatic header injection
117
+ - Request/response logging
118
+ - Error handling
119
+
120
+ ## Lifecycle Management
121
+
122
+ ### Initialization
123
+
124
+ ```typescript
125
+ const teardown = new TeardownCore({...});
126
+ // Automatically initializes identity client
127
+ ```
128
+
129
+ ### Runtime
130
+
131
+ The SDK operates automatically:
132
+ - Listens for app state changes
133
+ - Checks version on foreground
134
+ - Persists state changes
135
+
136
+ ### Cleanup
137
+
138
+ ```typescript
139
+ // Automatic cleanup when provider unmounts
140
+ useEffect(() => {
141
+ return () => {
142
+ core.shutdown();
143
+ };
144
+ }, [core]);
145
+ ```
146
+
147
+ ## State Persistence
148
+
149
+ All critical state is automatically persisted:
150
+
151
+ ```typescript
152
+ // Identity state
153
+ { type: "identified", session: {...}, version_info: {...} }
154
+
155
+ // Version status
156
+ { type: "up_to_date" | "update_available" | "update_required" | ... }
157
+ ```
158
+
159
+ State is restored on app restart for seamless user experience.
160
+
161
+ ## Error Handling
162
+
163
+ The SDK uses `AsyncResult` pattern for predictable error handling:
164
+
165
+ ```typescript
166
+ const result = await core.identity.identify({...});
167
+
168
+ if (result.success) {
169
+ // Handle success
170
+ console.log(result.data);
171
+ } else {
172
+ // Handle error
173
+ console.error(result.error);
174
+ }
175
+ ```
176
+
177
+ ## Type Safety
178
+
179
+ Full TypeScript support with:
180
+ - Runtime validation using Zod schemas
181
+ - Discriminated unions for state types
182
+ - Exported types for all public APIs
183
+
184
+ ## Next Steps
185
+
186
+ - [Identity & Authentication](./03-identity.mdx)
187
+ - [Force Updates](./04-force-updates.mdx)
188
+ - [API Reference](./07-api-reference.mdx)
@@ -0,0 +1,301 @@
1
+ # Identity & Authentication
2
+
3
+ Manage user sessions and device identity with the IdentityClient.
4
+
5
+ ## Overview
6
+
7
+ The IdentityClient handles:
8
+ - Device fingerprinting
9
+ - User identification and session management
10
+ - Anonymous and identified user states
11
+ - Automatic session persistence
12
+
13
+ ## Identity States
14
+
15
+ The SDK uses a discriminated union to represent identity states:
16
+
17
+ ```typescript
18
+ type IdentifyState =
19
+ | { type: "unidentified" }
20
+ | { type: "identifying" }
21
+ | { type: "identified", session: Session, version_info: {...} }
22
+ ```
23
+
24
+ ### State Transitions
25
+
26
+ ```
27
+ unidentified → identifying → identified
28
+ ↑ ↓
29
+ └───────── reset() ─────────┘
30
+ ```
31
+
32
+ ## Using the Identity Client
33
+
34
+ ### Access via Hook
35
+
36
+ ```typescript
37
+ import { useSession } from '@teardown/react-native';
38
+
39
+ function MyComponent() {
40
+ const session = useSession();
41
+
42
+ if (!session) {
43
+ return <LoginScreen />;
44
+ }
45
+
46
+ return <div>Welcome, {session.persona_id}</div>;
47
+ }
48
+ ```
49
+
50
+ ### Access via Core
51
+
52
+ ```typescript
53
+ import { useTeardown } from '@teardown/react-native';
54
+
55
+ function MyComponent() {
56
+ const { core } = useTeardown();
57
+
58
+ const handleLogin = async () => {
59
+ const result = await core.identity.identify({
60
+ user_id: 'user-123',
61
+ email: 'user@example.com',
62
+ name: 'John Doe',
63
+ });
64
+ };
65
+ }
66
+ ```
67
+
68
+ ## Identifying Users
69
+
70
+ ### Anonymous Identification
71
+
72
+ Automatically happens on SDK initialization:
73
+
74
+ ```typescript
75
+ // Happens automatically when TeardownProvider mounts
76
+ await core.identity.identify();
77
+ ```
78
+
79
+ This creates an anonymous session with:
80
+ - Unique device ID
81
+ - Session ID
82
+ - Device information
83
+ - Version status
84
+
85
+ ### Identified Users
86
+
87
+ Identify users with their information:
88
+
89
+ ```typescript
90
+ const result = await core.identity.identify({
91
+ user_id: 'user-123',
92
+ email: 'user@example.com',
93
+ name: 'John Doe',
94
+ });
95
+
96
+ if (result.success) {
97
+ console.log('Session:', result.data.session_id);
98
+ console.log('Device:', result.data.device_id);
99
+ console.log('Persona:', result.data.persona_id);
100
+ console.log('Token:', result.data.token);
101
+ }
102
+ ```
103
+
104
+ ### Persona Object
105
+
106
+ ```typescript
107
+ type Persona = {
108
+ user_id?: string;
109
+ email?: string;
110
+ name?: string;
111
+ }
112
+ ```
113
+
114
+ All fields are optional. You can identify with just an email, just a user_id, or any combination.
115
+
116
+ ## Session Management
117
+
118
+ ### Getting Current Session
119
+
120
+ ```typescript
121
+ // Via hook (reactive)
122
+ const session = useSession();
123
+
124
+ // Via client (direct)
125
+ const session = core.identity.getSessionState();
126
+ ```
127
+
128
+ ### Session Object
129
+
130
+ ```typescript
131
+ type Session = {
132
+ session_id: string; // Unique session identifier
133
+ device_id: string; // Unique device identifier
134
+ persona_id: string; // User/persona identifier
135
+ token: string; // Authentication token
136
+ }
137
+ ```
138
+
139
+ ### Refreshing Session
140
+
141
+ Re-identify the current persona to refresh session data:
142
+
143
+ ```typescript
144
+ const result = await core.identity.refresh();
145
+
146
+ if (result.success) {
147
+ console.log('Session refreshed');
148
+ }
149
+ ```
150
+
151
+ Note: `refresh()` only works if already identified.
152
+
153
+ ## Logging Out
154
+
155
+ Reset the identity state to anonymous:
156
+
157
+ ```typescript
158
+ core.identity.reset();
159
+ ```
160
+
161
+ This will:
162
+ - Clear persisted session data
163
+ - Set state to "unidentified"
164
+ - Emit state change event
165
+ - Trigger re-identification on next app open
166
+
167
+ ## Listening to State Changes
168
+
169
+ ### Via Hook
170
+
171
+ ```typescript
172
+ const session = useSession();
173
+
174
+ // Automatically re-renders on state changes
175
+ useEffect(() => {
176
+ console.log('Session changed:', session);
177
+ }, [session]);
178
+ ```
179
+
180
+ ### Via Event Listener
181
+
182
+ ```typescript
183
+ useEffect(() => {
184
+ const unsubscribe = core.identity.onIdentifyStateChange((state) => {
185
+ console.log('State changed:', state.type);
186
+
187
+ if (state.type === 'identified') {
188
+ console.log('User session:', state.session);
189
+ }
190
+ });
191
+
192
+ return unsubscribe;
193
+ }, []);
194
+ ```
195
+
196
+ ## Identity State
197
+
198
+ Get the full identity state (not just session):
199
+
200
+ ```typescript
201
+ const state = core.identity.getIdentifyState();
202
+
203
+ switch (state.type) {
204
+ case 'unidentified':
205
+ // No session yet
206
+ break;
207
+ case 'identifying':
208
+ // Identifying in progress
209
+ break;
210
+ case 'identified':
211
+ // Has session and version info
212
+ console.log(state.session);
213
+ console.log(state.version_info);
214
+ break;
215
+ }
216
+ ```
217
+
218
+ ## Error Handling
219
+
220
+ All identity operations return `AsyncResult`:
221
+
222
+ ```typescript
223
+ const result = await core.identity.identify({
224
+ email: 'invalid-email',
225
+ });
226
+
227
+ if (!result.success) {
228
+ console.error('Identification failed:', result.error);
229
+ // Handle error (show message, retry, etc.)
230
+ }
231
+ ```
232
+
233
+ Common errors:
234
+ - Network errors
235
+ - Invalid API credentials
236
+ - Validation errors (422)
237
+ - Server errors
238
+
239
+ ## Best Practices
240
+
241
+ ### 1. Let Auto-Initialization Happen
242
+
243
+ ```typescript
244
+ // ✅ Good - automatic anonymous identification
245
+ <TeardownProvider core={teardown}>
246
+ <App />
247
+ </TeardownProvider>
248
+
249
+ // ❌ Bad - don't manually call identify on mount
250
+ useEffect(() => {
251
+ core.identity.identify();
252
+ }, []);
253
+ ```
254
+
255
+ ### 2. Identify on Login/Signup
256
+
257
+ ```typescript
258
+ const handleLogin = async (email: string, password: string) => {
259
+ // Your auth logic
260
+ const user = await login(email, password);
261
+
262
+ // Identify with Teardown
263
+ await core.identity.identify({
264
+ user_id: user.id,
265
+ email: user.email,
266
+ name: user.name,
267
+ });
268
+ };
269
+ ```
270
+
271
+ ### 3. Reset on Logout
272
+
273
+ ```typescript
274
+ const handleLogout = async () => {
275
+ // Your logout logic
276
+ await logout();
277
+
278
+ // Reset Teardown identity
279
+ core.identity.reset();
280
+ };
281
+ ```
282
+
283
+ ### 4. Use the Hook for Reactive UI
284
+
285
+ ```typescript
286
+ function UserProfile() {
287
+ const session = useSession();
288
+
289
+ if (!session) {
290
+ return <LoginPrompt />;
291
+ }
292
+
293
+ return <Profile personaId={session.persona_id} />;
294
+ }
295
+ ```
296
+
297
+ ## Next Steps
298
+
299
+ - [Force Updates](./04-force-updates.mdx)
300
+ - [Device Information](./05-device-info.mdx)
301
+ - [API Reference](./07-api-reference.mdx)