@teardown/react-native 2.0.2 → 2.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 +104 -34
- package/package.json +15 -6
- package/src/clients/device/adapters/basic.adapter.ts +1 -1
- package/src/clients/device/adapters/device-info.adapter.ts +36 -0
- package/src/clients/device/adapters/device.adpater-interface.ts +23 -15
- package/src/clients/device/adapters/expo.adapter.ts +2 -36
- package/src/clients/force-update/force-update.client.ts +30 -6
- package/src/clients/identity/identity.client.test.ts +2 -2
- package/src/exports/adapters/device-info.ts +1 -0
- package/docs/01-getting-started.mdx +0 -147
- package/docs/02-core-concepts.mdx +0 -188
- package/docs/03-identity.mdx +0 -301
- package/docs/04-force-updates.mdx +0 -339
- package/docs/05-device-info.mdx +0 -324
- package/docs/06-logging.mdx +0 -345
- package/docs/06-storage.mdx +0 -349
- package/docs/07-api-reference.mdx +0 -472
- package/docs/07-logging.mdx +0 -345
- package/docs/08-api-reference.mdx +0 -472
- package/docs/08-hooks-reference.mdx +0 -476
- package/docs/09-advanced.mdx +0 -563
- package/docs/09-hooks-reference.mdx +0 -476
- package/docs/10-advanced.mdx +0 -563
package/README.md
CHANGED
|
@@ -2,49 +2,90 @@
|
|
|
2
2
|
|
|
3
3
|
Comprehensive SDK for managing device identity, force updates, logging, and analytics in React Native and Expo applications.
|
|
4
4
|
|
|
5
|
+
**[Documentation](https://teardown.dev/docs)** | **[Dashboard](https://dash.teardown.dev)**
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- **Device & User Identity** - Unique device fingerprinting and user session management
|
|
10
|
+
- **Force Updates** - Automatic version checking with optional or required update flows
|
|
11
|
+
- **Device Information** - Comprehensive device, OS, and app information collection
|
|
12
|
+
- **Storage** - Namespaced persistent storage with platform adapters
|
|
13
|
+
- **Logging** - Structured logging system with debug modes
|
|
14
|
+
- **Performance** - Optimized with caching, throttling, and efficient state management
|
|
15
|
+
- **Type Safety** - Full TypeScript support with runtime validation
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
17
19
|
```bash
|
|
20
|
+
npm install @teardown/react-native
|
|
21
|
+
# or
|
|
22
|
+
yarn add @teardown/react-native
|
|
23
|
+
# or
|
|
18
24
|
bun add @teardown/react-native
|
|
19
25
|
```
|
|
20
26
|
|
|
21
27
|
### Peer Dependencies
|
|
22
28
|
|
|
29
|
+
Choose adapters based on your project setup:
|
|
30
|
+
|
|
31
|
+
**Expo projects:**
|
|
32
|
+
```bash
|
|
33
|
+
npx expo install expo-device expo-application
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Bare React Native projects:**
|
|
23
37
|
```bash
|
|
24
|
-
|
|
25
|
-
bun add expo-application expo-device expo-updates
|
|
26
|
-
bun add react-native-device-info
|
|
38
|
+
npm install react-native-device-info
|
|
27
39
|
```
|
|
28
40
|
|
|
41
|
+
**Storage (choose one):**
|
|
42
|
+
```bash
|
|
43
|
+
# MMKV (recommended - faster)
|
|
44
|
+
npm install react-native-mmkv
|
|
45
|
+
|
|
46
|
+
# or AsyncStorage
|
|
47
|
+
npm install @react-native-async-storage/async-storage
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Getting Your Credentials
|
|
51
|
+
|
|
52
|
+
1. Sign up or log in at [dash.teardown.dev](https://dash.teardown.dev)
|
|
53
|
+
2. Create or select a project
|
|
54
|
+
3. Copy your `org_id`, `project_id`, and `api_key` from project settings
|
|
55
|
+
|
|
29
56
|
## Quick Start
|
|
30
57
|
|
|
31
58
|
### 1. Initialize the SDK
|
|
32
59
|
|
|
60
|
+
Create a file (e.g., `lib/teardown.ts`):
|
|
61
|
+
|
|
62
|
+
**Expo Setup:**
|
|
33
63
|
```tsx
|
|
34
64
|
import { TeardownCore } from '@teardown/react-native';
|
|
35
|
-
import { ExpoDeviceAdapter } from '@teardown/react-native/expo';
|
|
36
|
-
import {
|
|
65
|
+
import { ExpoDeviceAdapter } from '@teardown/react-native/adapters/expo';
|
|
66
|
+
import { MMKVStorageAdapter } from '@teardown/react-native/adapters/mmkv';
|
|
37
67
|
|
|
38
68
|
export const teardown = new TeardownCore({
|
|
39
69
|
org_id: 'your-org-id',
|
|
40
70
|
project_id: 'your-project-id',
|
|
41
71
|
api_key: 'your-api-key',
|
|
42
|
-
|
|
72
|
+
storageAdapter: new MMKVStorageAdapter(),
|
|
43
73
|
deviceAdapter: new ExpoDeviceAdapter(),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Bare React Native Setup:**
|
|
78
|
+
```tsx
|
|
79
|
+
import { TeardownCore } from '@teardown/react-native';
|
|
80
|
+
import { DeviceInfoAdapter } from '@teardown/react-native/adapters/device-info';
|
|
81
|
+
import { AsyncStorageAdapter } from '@teardown/react-native/adapters/async-storage';
|
|
82
|
+
|
|
83
|
+
export const teardown = new TeardownCore({
|
|
84
|
+
org_id: 'your-org-id',
|
|
85
|
+
project_id: 'your-project-id',
|
|
86
|
+
api_key: 'your-api-key',
|
|
87
|
+
storageAdapter: new AsyncStorageAdapter(),
|
|
88
|
+
deviceAdapter: new DeviceInfoAdapter(),
|
|
48
89
|
});
|
|
49
90
|
```
|
|
50
91
|
|
|
@@ -54,7 +95,7 @@ export const teardown = new TeardownCore({
|
|
|
54
95
|
import { TeardownProvider } from '@teardown/react-native';
|
|
55
96
|
import { teardown } from './lib/teardown';
|
|
56
97
|
|
|
57
|
-
export default function
|
|
98
|
+
export default function App() {
|
|
58
99
|
return (
|
|
59
100
|
<TeardownProvider core={teardown}>
|
|
60
101
|
<YourApp />
|
|
@@ -66,36 +107,65 @@ export default function RootLayout() {
|
|
|
66
107
|
### 3. Use in components
|
|
67
108
|
|
|
68
109
|
```tsx
|
|
69
|
-
import { useTeardown } from '@teardown/react-native';
|
|
110
|
+
import { useTeardown, useForceUpdate, useSession } from '@teardown/react-native';
|
|
70
111
|
|
|
71
112
|
function YourComponent() {
|
|
72
113
|
const { core } = useTeardown();
|
|
73
|
-
|
|
114
|
+
const session = useSession();
|
|
115
|
+
const { status, isUpdateRequired } = useForceUpdate();
|
|
116
|
+
|
|
74
117
|
const handleLogin = async () => {
|
|
75
118
|
await core.identity.identify({
|
|
76
119
|
user_id: 'user-123',
|
|
77
120
|
email: 'user@example.com',
|
|
78
121
|
});
|
|
79
122
|
};
|
|
80
|
-
|
|
81
|
-
return <Button onPress={handleLogin} />;
|
|
123
|
+
|
|
124
|
+
return <Button onPress={handleLogin} title="Login" />;
|
|
82
125
|
}
|
|
83
126
|
```
|
|
84
127
|
|
|
128
|
+
## Configuration Options
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
new TeardownCore({
|
|
132
|
+
org_id: string, // Your organization ID
|
|
133
|
+
project_id: string, // Your project ID
|
|
134
|
+
api_key: string, // Your API key
|
|
135
|
+
storageAdapter: adapter, // Storage implementation
|
|
136
|
+
deviceAdapter: adapter, // Device info source
|
|
137
|
+
forceUpdate: {
|
|
138
|
+
throttleMs: 30_000, // Min time between checks (default: 30s)
|
|
139
|
+
checkCooldownMs: 10_000 // Cooldown after check (default: 10s)
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Available Adapters
|
|
145
|
+
|
|
146
|
+
| Adapter | Import Path | Use Case |
|
|
147
|
+
|---------|-------------|----------|
|
|
148
|
+
| `ExpoDeviceAdapter` | `@teardown/react-native/adapters/expo` | Expo projects |
|
|
149
|
+
| `DeviceInfoAdapter` | `@teardown/react-native/adapters/device-info` | Bare RN projects |
|
|
150
|
+
| `MMKVStorageAdapter` | `@teardown/react-native/adapters/mmkv` | Fast sync storage |
|
|
151
|
+
| `AsyncStorageAdapter` | `@teardown/react-native/adapters/async-storage` | Standard async storage |
|
|
152
|
+
|
|
85
153
|
## Documentation
|
|
86
154
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- [
|
|
92
|
-
- [
|
|
93
|
-
- [
|
|
94
|
-
- [
|
|
95
|
-
- [
|
|
96
|
-
- [
|
|
97
|
-
- [
|
|
98
|
-
- [
|
|
155
|
+
For detailed guides, visit **[teardown.dev/docs](https://teardown.dev/docs)**
|
|
156
|
+
|
|
157
|
+
Local documentation is also available in the [docs](./docs) folder:
|
|
158
|
+
|
|
159
|
+
- [Getting Started](./docs/01-getting-started.mdx)
|
|
160
|
+
- [Core Concepts](./docs/02-core-concepts.mdx)
|
|
161
|
+
- [Identity & Authentication](./docs/03-identity.mdx)
|
|
162
|
+
- [Force Updates](./docs/04-force-updates.mdx)
|
|
163
|
+
- [Device Information](./docs/05-device-info.mdx)
|
|
164
|
+
- [Storage](./docs/06-storage.mdx)
|
|
165
|
+
- [Logging](./docs/07-logging.mdx)
|
|
166
|
+
- [API Reference](./docs/08-api-reference.mdx)
|
|
167
|
+
- [Hooks Reference](./docs/09-hooks-reference.mdx)
|
|
168
|
+
- [Advanced Usage](./docs/10-advanced.mdx)
|
|
99
169
|
|
|
100
170
|
## License
|
|
101
171
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teardown/react-native",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
"types": "./src/exports/adapters/async-storage.ts",
|
|
33
33
|
"import": "./src/exports/adapters/async-storage.ts",
|
|
34
34
|
"default": "./src/exports/adapters/async-storage.ts"
|
|
35
|
+
},
|
|
36
|
+
"./adapters/device-info": {
|
|
37
|
+
"types": "./src/exports/adapters/device-info.ts",
|
|
38
|
+
"import": "./src/exports/adapters/device-info.ts",
|
|
39
|
+
"default": "./src/exports/adapters/device-info.ts"
|
|
35
40
|
}
|
|
36
41
|
},
|
|
37
42
|
"scripts": {
|
|
@@ -62,20 +67,24 @@
|
|
|
62
67
|
"expo-updates": "^29.0.12"
|
|
63
68
|
},
|
|
64
69
|
"peerDependencies": {
|
|
65
|
-
"@react-native-async-storage/async-storage": "^1.21
|
|
66
|
-
"react": "*",
|
|
67
|
-
"react-native": "*",
|
|
68
|
-
"typescript": "*",
|
|
70
|
+
"@react-native-async-storage/async-storage": "^1.21 || ^2.0",
|
|
69
71
|
"expo-application": "*",
|
|
70
72
|
"expo-device": "*",
|
|
71
73
|
"expo-notifications": "*",
|
|
72
74
|
"expo-secure-store": "*",
|
|
73
|
-
"react
|
|
75
|
+
"react": "*",
|
|
76
|
+
"react-native": "*",
|
|
77
|
+
"react-native-device-info": "^15",
|
|
78
|
+
"react-native-mmkv": "^4.0.0",
|
|
79
|
+
"typescript": "*"
|
|
74
80
|
},
|
|
75
81
|
"peerDependenciesMeta": {
|
|
76
82
|
"@react-native-async-storage/async-storage": {
|
|
77
83
|
"optional": true
|
|
78
84
|
},
|
|
85
|
+
"react-native-device-info": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
79
88
|
"react-native-mmkv": {
|
|
80
89
|
"optional": true
|
|
81
90
|
},
|
|
@@ -8,8 +8,8 @@ import * as Application from "expo-application";
|
|
|
8
8
|
import * as Device from "expo-device";
|
|
9
9
|
import { Platform } from "react-native";
|
|
10
10
|
|
|
11
|
-
import { DeviceInfoAdapter } from "./device.adpater-interface";
|
|
12
11
|
import { DevicePlatformEnum, NotificationPlatformEnum } from "../device.client";
|
|
12
|
+
import { DeviceInfoAdapter } from "./device.adpater-interface";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Maps expo-device DeviceType to a string representation
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ApplicationInfo,
|
|
3
|
+
HardwareInfo,
|
|
4
|
+
OSInfo
|
|
5
|
+
} from "@teardown/schemas";
|
|
6
|
+
import { Platform } from "react-native";
|
|
7
|
+
import DeviceInfo from "react-native-device-info";
|
|
8
|
+
import { DeviceInfoAdapter as BaseInfoAdapterInterface } from "./device.adpater-interface";
|
|
9
|
+
|
|
10
|
+
export class DeviceInfoAdapter extends BaseInfoAdapterInterface {
|
|
11
|
+
get applicationInfo(): ApplicationInfo {
|
|
12
|
+
return {
|
|
13
|
+
version: DeviceInfo.getVersion() ?? "0.0.0",
|
|
14
|
+
build_number: Number.parseInt(
|
|
15
|
+
DeviceInfo.getBuildNumber() ?? "0",
|
|
16
|
+
10
|
|
17
|
+
),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get hardwareInfo(): HardwareInfo {
|
|
22
|
+
return {
|
|
23
|
+
device_name: DeviceInfo.getDeviceNameSync() ?? "Unknown Device",
|
|
24
|
+
device_brand: DeviceInfo.getBrand() ?? "Unknown Brand",
|
|
25
|
+
device_type: DeviceInfo.getDeviceType?.() ?? "unknown",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get osInfo(): OSInfo {
|
|
30
|
+
return {
|
|
31
|
+
platform: this.mapPlatform(Platform.OS),
|
|
32
|
+
name: DeviceInfo.getSystemName() ?? Platform.OS,
|
|
33
|
+
version: DeviceInfo.getSystemVersion() ?? "0.0.0",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -2,9 +2,10 @@ import type {
|
|
|
2
2
|
ApplicationInfo,
|
|
3
3
|
DeviceInfo,
|
|
4
4
|
HardwareInfo,
|
|
5
|
-
NotificationsInfo,
|
|
6
5
|
OSInfo
|
|
7
6
|
} from "@teardown/schemas";
|
|
7
|
+
import type { Platform } from "react-native";
|
|
8
|
+
import { DevicePlatformEnum } from "../device.client";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* An interface for a device adapter.
|
|
@@ -16,32 +17,18 @@ import type {
|
|
|
16
17
|
*
|
|
17
18
|
*/
|
|
18
19
|
export abstract class DeviceInfoAdapter {
|
|
19
|
-
// -- Application Information --
|
|
20
20
|
/**
|
|
21
21
|
* The information about the application running.
|
|
22
22
|
*/
|
|
23
23
|
abstract get applicationInfo(): ApplicationInfo;
|
|
24
|
-
// -- Updates Information --
|
|
25
|
-
/**
|
|
26
|
-
* The information about the update running.
|
|
27
|
-
*/
|
|
28
|
-
// abstract get updateInfo(): UpdateInfo | null;
|
|
29
|
-
// -- Hardware Information --
|
|
30
24
|
/**
|
|
31
25
|
* The information about the hardware of the device.
|
|
32
26
|
*/
|
|
33
27
|
abstract get hardwareInfo(): HardwareInfo;
|
|
34
|
-
// -- OS Information --
|
|
35
28
|
/**
|
|
36
29
|
* The information about the operating system of the device.
|
|
37
30
|
*/
|
|
38
31
|
abstract get osInfo(): OSInfo;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* The information about the notifications of the device.
|
|
42
|
-
*/
|
|
43
|
-
abstract get notificationsInfo(): NotificationsInfo;
|
|
44
|
-
|
|
45
32
|
/**
|
|
46
33
|
* The information about the device.
|
|
47
34
|
*/
|
|
@@ -54,4 +41,25 @@ export abstract class DeviceInfoAdapter {
|
|
|
54
41
|
update: null,
|
|
55
42
|
});
|
|
56
43
|
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Maps React Native Platform.OS to DevicePlatformEnum
|
|
47
|
+
*/
|
|
48
|
+
mapPlatform(platform: typeof Platform.OS): DevicePlatformEnum {
|
|
49
|
+
switch (platform) {
|
|
50
|
+
case "ios":
|
|
51
|
+
return DevicePlatformEnum.IOS;
|
|
52
|
+
case "android":
|
|
53
|
+
return DevicePlatformEnum.ANDROID;
|
|
54
|
+
case "web":
|
|
55
|
+
return DevicePlatformEnum.WEB;
|
|
56
|
+
case "macos":
|
|
57
|
+
return DevicePlatformEnum.MACOS;
|
|
58
|
+
case "windows":
|
|
59
|
+
return DevicePlatformEnum.WINDOWS;
|
|
60
|
+
default:
|
|
61
|
+
return DevicePlatformEnum.UNKNOWN;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ApplicationInfo,
|
|
3
3
|
HardwareInfo,
|
|
4
|
-
|
|
5
|
-
OSInfo,
|
|
4
|
+
OSInfo
|
|
6
5
|
} from "@teardown/schemas";
|
|
7
6
|
import * as Application from "expo-application";
|
|
8
7
|
import * as Device from "expo-device";
|
|
9
8
|
import { Platform } from "react-native";
|
|
10
9
|
|
|
11
10
|
import { DeviceInfoAdapter } from "./device.adpater-interface";
|
|
12
|
-
import { DevicePlatformEnum, NotificationPlatformEnum } from "../device.client";
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* Maps expo-device DeviceType to a string representation
|
|
@@ -29,26 +27,6 @@ function mapDeviceType(deviceType: Device.DeviceType | null): string {
|
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
/**
|
|
33
|
-
* Maps React Native Platform.OS to DevicePlatformEnum
|
|
34
|
-
*/
|
|
35
|
-
function mapPlatform(platform: typeof Platform.OS): DevicePlatformEnum {
|
|
36
|
-
switch (platform) {
|
|
37
|
-
case "ios":
|
|
38
|
-
return DevicePlatformEnum.IOS;
|
|
39
|
-
case "android":
|
|
40
|
-
return DevicePlatformEnum.ANDROID;
|
|
41
|
-
case "web":
|
|
42
|
-
return DevicePlatformEnum.WEB;
|
|
43
|
-
case "macos":
|
|
44
|
-
return DevicePlatformEnum.MACOS;
|
|
45
|
-
case "windows":
|
|
46
|
-
return DevicePlatformEnum.WINDOWS;
|
|
47
|
-
default:
|
|
48
|
-
return DevicePlatformEnum.UNKNOWN;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
30
|
export class ExpoDeviceAdapter extends DeviceInfoAdapter {
|
|
53
31
|
get applicationInfo(): ApplicationInfo {
|
|
54
32
|
return {
|
|
@@ -70,21 +48,9 @@ export class ExpoDeviceAdapter extends DeviceInfoAdapter {
|
|
|
70
48
|
|
|
71
49
|
get osInfo(): OSInfo {
|
|
72
50
|
return {
|
|
73
|
-
platform: mapPlatform(Platform.OS),
|
|
51
|
+
platform: this.mapPlatform(Platform.OS),
|
|
74
52
|
name: Device.osName ?? Platform.OS,
|
|
75
53
|
version: Device.osVersion ?? "0.0.0",
|
|
76
54
|
};
|
|
77
55
|
}
|
|
78
|
-
|
|
79
|
-
get notificationsInfo(): NotificationsInfo {
|
|
80
|
-
return {
|
|
81
|
-
push: {
|
|
82
|
-
enabled: false,
|
|
83
|
-
granted: false,
|
|
84
|
-
token: null,
|
|
85
|
-
platform: NotificationPlatformEnum.EXPO,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
56
|
}
|
|
@@ -69,12 +69,31 @@ export type VersionStatusChangeEvents = {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
export type ForceUpdateClientOptions = {
|
|
72
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Minimum time (ms) between foreground transitions to prevent rapid-fire checks.
|
|
74
|
+
* Measured from the last time the app came to foreground.
|
|
75
|
+
* Prevents checking when user quickly switches apps back and forth.
|
|
76
|
+
* Default: 30000 (30 seconds)
|
|
77
|
+
*
|
|
78
|
+
* Special values:
|
|
79
|
+
* - -1: Disable throttling, check on every foreground (respects checkCooldownMs)
|
|
80
|
+
*
|
|
81
|
+
* Example: If throttleMs is 30s and user backgrounds then foregrounds the app
|
|
82
|
+
* twice within 20s, only the first transition triggers a check.
|
|
83
|
+
*/
|
|
73
84
|
throttleMs?: number;
|
|
74
85
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
86
|
+
* Minimum time (ms) since the last successful version check before checking again.
|
|
87
|
+
* Measured from when the last check completed successfully (not when it started).
|
|
88
|
+
* Prevents unnecessary API calls after we already have fresh version data.
|
|
89
|
+
* Default: 300000 (5 minutes)
|
|
90
|
+
*
|
|
91
|
+
* Special values:
|
|
92
|
+
* - 0: Disable cooldown, check on every foreground (respects throttleMs)
|
|
93
|
+
* - -1: Disable all automatic version checking
|
|
94
|
+
*
|
|
95
|
+
* Example: If checkCooldownMs is 5min and a check completes at 12:00pm,
|
|
96
|
+
* no new checks occur until 12:05pm, even if user foregrounds the app multiple times.
|
|
78
97
|
*/
|
|
79
98
|
checkCooldownMs?: number;
|
|
80
99
|
};
|
|
@@ -84,7 +103,7 @@ const DEFAULT_OPTIONS: Required<ForceUpdateClientOptions> = {
|
|
|
84
103
|
checkCooldownMs: 300_000, // 5 minutes
|
|
85
104
|
};
|
|
86
105
|
|
|
87
|
-
export const VERSION_STATUS_STORAGE_KEY = "
|
|
106
|
+
export const VERSION_STATUS_STORAGE_KEY = "version_status";
|
|
88
107
|
|
|
89
108
|
export class ForceUpdateClient {
|
|
90
109
|
private emitter = new EventEmitter<VersionStatusChangeEvents>();
|
|
@@ -220,7 +239,12 @@ export class ForceUpdateClient {
|
|
|
220
239
|
}
|
|
221
240
|
|
|
222
241
|
const now = Date.now();
|
|
223
|
-
|
|
242
|
+
|
|
243
|
+
// If throttleMs is -1, disable throttling (always pass)
|
|
244
|
+
// Otherwise, check if enough time has passed since last foreground
|
|
245
|
+
const throttleOk = this.options.throttleMs === -1
|
|
246
|
+
|| !this.lastForegroundTime
|
|
247
|
+
|| now - this.lastForegroundTime >= this.options.throttleMs;
|
|
224
248
|
|
|
225
249
|
// If checkCooldownMs is 0, always allow check (no cooldown)
|
|
226
250
|
// Otherwise, check if enough time has passed since last successful check
|
|
@@ -27,7 +27,7 @@ function createMockLoggingClient() {
|
|
|
27
27
|
debug: (message: string, ...args: unknown[]) => logs.push({ level: "debug", message, args }),
|
|
28
28
|
}),
|
|
29
29
|
getLogs: () => logs,
|
|
30
|
-
clearLogs: () => logs.length = 0,
|
|
30
|
+
clearLogs: () => { logs.length = 0; },
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -605,7 +605,7 @@ describe("IdentityClient", () => {
|
|
|
605
605
|
|
|
606
606
|
// Should have debug log about state already being 'identifying' (first setIdentifyState call)
|
|
607
607
|
// Then transitions to identified
|
|
608
|
-
const debugLogs = mockLogging.getLogs().filter(l => l.level === "debug");
|
|
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
611
|
expect(mockLogging.getLogs().some(l => l.level === "info")).toBe(true);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../clients/device/adapters/device-info.adapter";
|
|
@@ -1,147 +0,0 @@
|
|
|
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
|