@teardown/react-native 1.2.38 → 2.0.0

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.
Files changed (47) hide show
  1. package/README.md +75 -7
  2. package/package.json +65 -47
  3. package/src/clients/api/api.client.ts +55 -0
  4. package/src/clients/api/index.ts +1 -0
  5. package/src/clients/device/device.adpater-interface.ts +57 -0
  6. package/src/clients/device/device.client.test.ts +195 -0
  7. package/src/clients/device/device.client.ts +69 -0
  8. package/src/clients/device/expo-adapter.ts +128 -0
  9. package/src/clients/device/index.ts +4 -0
  10. package/src/clients/force-update/force-update.client.test.ts +296 -0
  11. package/src/clients/force-update/force-update.client.ts +224 -0
  12. package/src/clients/force-update/index.ts +1 -0
  13. package/src/clients/identity/identity.client.test.ts +454 -0
  14. package/src/clients/identity/identity.client.ts +249 -0
  15. package/src/clients/identity/index.ts +1 -0
  16. package/src/clients/logging/index.ts +1 -0
  17. package/src/clients/logging/logging.client.ts +92 -0
  18. package/src/clients/notifications/notifications.client.ts +10 -0
  19. package/src/clients/storage/index.ts +1 -0
  20. package/src/clients/storage/mmkv-adapter.ts +23 -0
  21. package/src/clients/storage/storage.client.ts +75 -0
  22. package/src/clients/utils/index.ts +1 -0
  23. package/src/clients/utils/utils.client.ts +75 -0
  24. package/src/components/ui/button.tsx +0 -0
  25. package/src/components/ui/input.tsx +0 -0
  26. package/src/contexts/index.ts +1 -0
  27. package/src/contexts/teardown.context.ts +17 -0
  28. package/src/exports/expo.ts +1 -0
  29. package/src/exports/index.ts +16 -0
  30. package/src/exports/mmkv.ts +1 -0
  31. package/src/hooks/use-force-update.ts +38 -0
  32. package/src/hooks/use-session.ts +26 -0
  33. package/src/providers/teardown.provider.tsx +28 -0
  34. package/src/teardown.core.ts +76 -0
  35. package/dist/components/index.js +0 -2
  36. package/dist/components/teardown-logo.js +0 -34
  37. package/dist/containers/index.js +0 -17
  38. package/dist/containers/teardown.container.js +0 -25
  39. package/dist/index.js +0 -21
  40. package/dist/plugins/http.plugin.js +0 -144
  41. package/dist/plugins/index.js +0 -19
  42. package/dist/plugins/logging.plugin.js +0 -35
  43. package/dist/plugins/websocket.plugin.js +0 -107
  44. package/dist/services/index.js +0 -17
  45. package/dist/services/teardown.service.js +0 -21
  46. package/dist/teardown.client.js +0 -59
  47. package/dist/utils/log.js +0 -8
package/README.md CHANGED
@@ -1,20 +1,88 @@
1
+ # @teardown/react-native
1
2
 
3
+ Comprehensive SDK for managing device identity, force updates, logging, and analytics in React Native and Expo applications.
2
4
 
5
+ ## Features
3
6
 
7
+ - 🔐 **Device & User Identity** - Unique device fingerprinting and user session management
8
+ - 🔄 **Force Updates** - Automatic version checking with optional or required update flows
9
+ - 📱 **Device Information** - Comprehensive device, OS, and app information collection
10
+ - 💾 **Storage** - Namespaced persistent storage with platform adapters
11
+ - 📝 **Logging** - Structured logging system with debug modes
12
+ - ⚡ **Performance** - Optimized with caching, throttling, and efficient state management
13
+ - 🎯 **Type Safety** - Full TypeScript support with runtime validation
4
14
 
5
-
6
- To get started:
15
+ ## Installation
7
16
 
8
17
  ```bash
9
- yarn add react-native-gesture-handler @teardown/react-native
18
+ bun add @teardown/react-native
10
19
  ```
11
20
 
12
- If you're using CocoaPods you need to install pods:
21
+ ### Peer Dependencies
13
22
 
14
23
  ```bash
15
- cd ios && pod install
24
+ bun add react react-native zod
25
+ bun add expo-application expo-device expo-updates
26
+ bun add react-native-device-info
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```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',
41
+ },
42
+ storage: { /* ... */ },
43
+ device: { /* ... */ },
44
+ identity: { identifyOnLoad: true },
45
+ });
16
46
 
17
- or
47
+ // Wrap your app
48
+ export default function App() {
49
+ return (
50
+ <TeardownProvider core={teardown}>
51
+ <YourApp />
52
+ </TeardownProvider>
53
+ );
54
+ }
18
55
 
19
- npx pod-install
56
+ // Use in components
57
+ function YourComponent() {
58
+ const { core } = useTeardown();
59
+
60
+ const handleLogin = async () => {
61
+ await core.identity.identify({
62
+ user_id: 'user-123',
63
+ email: 'user@example.com',
64
+ });
65
+ };
66
+
67
+ return <Button onPress={handleLogin} />;
68
+ }
20
69
  ```
70
+
71
+ ## Documentation
72
+
73
+ Complete documentation is available in the [docs](./docs) folder:
74
+
75
+ - [Getting Started](./docs/01-getting-started.mdx) - Installation and setup
76
+ - [Core Concepts](./docs/02-core-concepts.mdx) - Architecture overview
77
+ - [Identity & Authentication](./docs/03-identity.mdx) - User session management
78
+ - [Force Updates](./docs/04-force-updates.mdx) - Version management
79
+ - [Device Information](./docs/05-device-info.mdx) - Device data collection
80
+ - [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
85
+
86
+ ## License
87
+
88
+ Proprietary - See LICENSE file for details
package/package.json CHANGED
@@ -1,49 +1,67 @@
1
1
  {
2
- "name": "@teardown/react-native",
3
- "version": "1.2.38",
4
- "description": "",
5
- "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1",
7
- "build": "tsc",
8
- "dev": "tsc --watch"
9
- },
10
- "main": "./dist/index.js",
11
- "types": "./dist/index.d.ts",
12
- "files": [
13
- "README.md",
14
- "dist/**/*"
15
- ],
16
- "dependencies": {
17
- "@react-native/dev-middleware": "^0.76.1",
18
- "@teardown/cli": "^1.2.20",
19
- "@teardown/logger": "1.2.38",
20
- "@teardown/util": "1.2.38",
21
- "@teardown/websocket": "1.2.38",
22
- "async-mutex": "^0.5.0",
23
- "chalk": "^5.3.0",
24
- "compression": "^1.7.5",
25
- "connect": "^3.7.0",
26
- "debug": "^4.3.7",
27
- "pretty-format": "^29.7.0",
28
- "ws": "^8.18.0"
29
- },
30
- "devDependencies": {
31
- "@teardown/config": "1.2.38",
32
- "@types/compression": "^1.7.5",
33
- "@types/connect": "^3.4.38",
34
- "@types/react": "^18.3.12",
35
- "@types/react-native": "^0.73.0",
36
- "metro-config": "^0.81.0",
37
- "react": "^18.3.1",
38
- "react-native": "^0.75.4",
39
- "react-native-device-info": "^14.0.0",
40
- "react-native-gesture-handler": "^2.20.2",
41
- "typescript": "^5.6.3"
42
- },
43
- "peerDependencies": {
44
- "react": "^18",
45
- "react-native": "^0.75",
46
- "react-native-device-info": "^14",
47
- "react-native-gesture-handler": "^2"
48
- }
2
+ "name": "@teardown/react-native",
3
+ "version": "2.0.0",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "files": [
9
+ "src/**/*",
10
+ "docs/**/*"
11
+ ],
12
+ "main": "./src/exports/index.ts",
13
+ "module": "./src/exports/index.ts",
14
+ "types": "./src/exports/index.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./src/exports/index.ts",
18
+ "import": "./src/exports/index.ts",
19
+ "default": "./src/exports/index.ts"
20
+ },
21
+ "./expo": {
22
+ "types": "./src/exports/expo.ts",
23
+ "import": "./src/exports/expo.ts",
24
+ "default": "./src/exports/expo.ts"
25
+ },
26
+ "./mmkv": {
27
+ "types": "./src/exports/mmkv.ts",
28
+ "import": "./src/exports/mmkv.ts",
29
+ "default": "./src/exports/mmkv.ts"
30
+ }
31
+ },
32
+ "scripts": {
33
+ "build": "tsc --project ./tsconfig.json",
34
+ "dev": "bun run build && tsc --watch --project ./tsconfig.json",
35
+ "typecheck": "tsc --noEmit --project ./tsconfig.json",
36
+ "lint": "biome lint --write",
37
+ "fmt": "biome format --write",
38
+ "test": "bun test",
39
+ "prepublishOnly": "bun run build",
40
+ "nuke": "cd ../../ && bun run nuke"
41
+ },
42
+ "dependencies": {
43
+ "@teardown/ingest-api": "0.1.36",
44
+ "@teardown/schemas": "0.1.36",
45
+ "@teardown/types": "0.1.36",
46
+ "eventemitter3": "^5.0.1",
47
+ "uuid": "^13.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@elysiajs/eden": "^1.4.5",
51
+ "@teardown/tsconfig": "1.0.0",
52
+ "@types/bun": "latest",
53
+ "@types/react": "*",
54
+ "@types/uuid": "^11.0.0",
55
+ "expo-updates": "^29.0.12"
56
+ },
57
+ "peerDependencies": {
58
+ "react": "*",
59
+ "react-native": "*",
60
+ "typescript": "*",
61
+ "expo-application": "^7.0",
62
+ "expo-device": "^8.0",
63
+ "expo-notifications": "^0.29",
64
+ "react-native-mmkv": "^3.0",
65
+ "zod": "^4"
66
+ }
49
67
  }
@@ -0,0 +1,55 @@
1
+ import * as Eden from "@elysiajs/eden";
2
+ import * as IngestApi from "@teardown/ingest-api";
3
+ import type { LoggingClient } from "../logging";
4
+ import type { StorageClient } from "../storage";
5
+
6
+ export { Eden, IngestApi };
7
+
8
+ const TEARDOWN_INGEST_URL = "http://localhost:4880";
9
+ const TEARDOWN_API_KEY_HEADER = "td-api-key";
10
+ const TEARDOWN_ORG_ID_HEADER = "td-org-id";
11
+ const TEARDOWN_PROJECT_ID_HEADER = "td-project-id";
12
+ const TEARDOWN_ENVIRONMENT_SLUG_HEADER = "td-environment-slug";
13
+
14
+ export type ApiClientOptions = {
15
+ api_key: string;
16
+ org_id: string;
17
+ project_id: string;
18
+ environment_slug: string;
19
+ onRequest?: (endpoint: IngestApi.Endpoints, options: IngestApi.RequestOptions) => Promise<IngestApi.RequestOptions>;
20
+ };
21
+
22
+ export class ApiClient {
23
+ public client: IngestApi.Client;
24
+
25
+ constructor(
26
+ _logging: LoggingClient,
27
+ _storage: StorageClient,
28
+ private readonly options: ApiClientOptions
29
+ ) {
30
+ this.client = IngestApi.client(TEARDOWN_INGEST_URL, {
31
+ headers: {
32
+ [TEARDOWN_API_KEY_HEADER]: `Bearer ${this.options.api_key}`,
33
+ [TEARDOWN_ORG_ID_HEADER]: this.options.org_id,
34
+ [TEARDOWN_PROJECT_ID_HEADER]: this.options.project_id,
35
+ [TEARDOWN_ENVIRONMENT_SLUG_HEADER]: this.options.environment_slug,
36
+ },
37
+ });
38
+ }
39
+
40
+ get orgId(): string {
41
+ return this.options.org_id;
42
+ }
43
+
44
+ get projectId(): string {
45
+ return this.options.project_id;
46
+ }
47
+
48
+ get apiKey(): string {
49
+ return this.options.api_key;
50
+ }
51
+
52
+ get environmentSlug(): string {
53
+ return this.options.environment_slug;
54
+ }
55
+ }
@@ -0,0 +1 @@
1
+ export * from "./api.client";
@@ -0,0 +1,57 @@
1
+ import type {
2
+ ApplicationInfo,
3
+ DeviceInfo,
4
+ HardwareInfo,
5
+ NotificationsInfo,
6
+ OSInfo
7
+ } from "@teardown/schemas";
8
+
9
+ /**
10
+ * An interface for a device adapter.
11
+ * This interface is used to abstract the device adapter implementation.
12
+ * Everything is optional and should be implemented by the adapter.
13
+ *
14
+ * The aim of this interface is to provide a consistent way to get information about the device.
15
+ * With an opt-in approach to get the information your want to use and track.
16
+ *
17
+ */
18
+ export abstract class DeviceInfoAdapter {
19
+ // -- Application Information --
20
+ /**
21
+ * The information about the application running.
22
+ */
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
+ /**
31
+ * The information about the hardware of the device.
32
+ */
33
+ abstract get hardwareInfo(): HardwareInfo;
34
+ // -- OS Information --
35
+ /**
36
+ * The information about the operating system of the device.
37
+ */
38
+ abstract get osInfo(): OSInfo;
39
+
40
+ /**
41
+ * The information about the notifications of the device.
42
+ */
43
+ abstract get notificationsInfo(): NotificationsInfo;
44
+
45
+ /**
46
+ * The information about the device.
47
+ */
48
+ public getDeviceInfo(): Promise<DeviceInfo> {
49
+ return Promise.resolve({
50
+ application: this.applicationInfo,
51
+ hardware: this.hardwareInfo,
52
+ os: this.osInfo,
53
+ notifications: this.notificationsInfo,
54
+ update: null,
55
+ });
56
+ }
57
+ }
@@ -0,0 +1,195 @@
1
+ import type { DeviceInfo } from "@teardown/schemas";
2
+ import { describe, expect, test } from "bun:test";
3
+ import type { DeviceInfoAdapter } from "./device.adpater-interface";
4
+ import { DeviceClient } from "./device.client";
5
+
6
+ function createMockLoggingClient() {
7
+ return {
8
+ createLogger: () => ({
9
+ info: () => { },
10
+ warn: () => { },
11
+ error: () => { },
12
+ debug: () => { },
13
+ }),
14
+ };
15
+ }
16
+
17
+ function createMockStorageClient() {
18
+ const storage = new Map<string, string>();
19
+ return {
20
+ createStorage: () => ({
21
+ getItem: (key: string) => storage.get(key) ?? null,
22
+ setItem: (key: string, value: string) => storage.set(key, value),
23
+ removeItem: (key: string) => storage.delete(key),
24
+ }),
25
+ getStorage: () => storage,
26
+ };
27
+ }
28
+
29
+ function createMockUtilsClient() {
30
+ let uuidCounter = 0;
31
+ return {
32
+ generateRandomUUID: async () => `mock-uuid-${++uuidCounter}`,
33
+ getUuidCounter: () => uuidCounter,
34
+ };
35
+ }
36
+
37
+ function createMockDeviceAdapter(): DeviceInfoAdapter {
38
+ const mockDeviceInfo: DeviceInfo = {
39
+ application: {
40
+ name: "TestApp",
41
+ version: "1.0.0",
42
+ build: "100",
43
+ bundle_id: "com.test.app",
44
+ },
45
+ hardware: {
46
+ brand: "Apple",
47
+ model: "iPhone 15",
48
+ device_type: "PHONE",
49
+ },
50
+ os: {
51
+ name: "iOS",
52
+ version: "17.0",
53
+ },
54
+ notifications: {
55
+ push_token: null,
56
+ platform: null,
57
+ },
58
+ update: null,
59
+ };
60
+
61
+ return {
62
+ applicationInfo: mockDeviceInfo.application,
63
+ hardwareInfo: mockDeviceInfo.hardware,
64
+ osInfo: mockDeviceInfo.os,
65
+ notificationsInfo: mockDeviceInfo.notifications,
66
+ getDeviceInfo: async () => mockDeviceInfo,
67
+ } as DeviceInfoAdapter;
68
+ }
69
+
70
+ describe("DeviceClient", () => {
71
+ describe("getDeviceId", () => {
72
+ test("generates new UUID when no device ID stored", async () => {
73
+ const mockLogging = createMockLoggingClient();
74
+ const mockStorage = createMockStorageClient();
75
+ const mockUtils = createMockUtilsClient();
76
+ const mockAdapter = createMockDeviceAdapter();
77
+
78
+ const client = new DeviceClient(
79
+ mockLogging as never,
80
+ mockUtils as never,
81
+ mockStorage as never,
82
+ { adapter: mockAdapter }
83
+ );
84
+
85
+ const deviceId = await client.getDeviceId();
86
+
87
+ expect(deviceId).toBe("mock-uuid-1");
88
+ expect(mockUtils.getUuidCounter()).toBe(1);
89
+ });
90
+
91
+ test("returns stored device ID when available", async () => {
92
+ const mockLogging = createMockLoggingClient();
93
+ const mockStorage = createMockStorageClient();
94
+ const mockUtils = createMockUtilsClient();
95
+ const mockAdapter = createMockDeviceAdapter();
96
+
97
+ // Pre-populate storage with a device ID
98
+ mockStorage.getStorage().set("deviceId", "existing-device-id");
99
+
100
+ const client = new DeviceClient(
101
+ mockLogging as never,
102
+ mockUtils as never,
103
+ mockStorage as never,
104
+ { adapter: mockAdapter }
105
+ );
106
+
107
+ const deviceId = await client.getDeviceId();
108
+
109
+ expect(deviceId).toBe("existing-device-id");
110
+ // Should not have generated a new UUID
111
+ expect(mockUtils.getUuidCounter()).toBe(0);
112
+ });
113
+
114
+ test("returns consistent device ID on multiple calls", async () => {
115
+ const mockLogging = createMockLoggingClient();
116
+ const mockStorage = createMockStorageClient();
117
+ const mockUtils = createMockUtilsClient();
118
+ const mockAdapter = createMockDeviceAdapter();
119
+
120
+ mockStorage.getStorage().set("deviceId", "consistent-id");
121
+
122
+ const client = new DeviceClient(
123
+ mockLogging as never,
124
+ mockUtils as never,
125
+ mockStorage as never,
126
+ { adapter: mockAdapter }
127
+ );
128
+
129
+ const id1 = await client.getDeviceId();
130
+ const id2 = await client.getDeviceId();
131
+ const id3 = await client.getDeviceId();
132
+
133
+ expect(id1).toBe("consistent-id");
134
+ expect(id2).toBe("consistent-id");
135
+ expect(id3).toBe("consistent-id");
136
+ });
137
+ });
138
+
139
+ describe("getDeviceInfo", () => {
140
+ test("returns device info from adapter", async () => {
141
+ const mockLogging = createMockLoggingClient();
142
+ const mockStorage = createMockStorageClient();
143
+ const mockUtils = createMockUtilsClient();
144
+ const mockAdapter = createMockDeviceAdapter();
145
+
146
+ const client = new DeviceClient(
147
+ mockLogging as never,
148
+ mockUtils as never,
149
+ mockStorage as never,
150
+ { adapter: mockAdapter }
151
+ );
152
+
153
+ const deviceInfo = await client.getDeviceInfo();
154
+
155
+ expect(deviceInfo.application.name).toBe("TestApp");
156
+ expect(deviceInfo.application.version).toBe("1.0.0");
157
+ expect(deviceInfo.hardware.brand).toBe("Apple");
158
+ expect(deviceInfo.hardware.model).toBe("iPhone 15");
159
+ expect(deviceInfo.os.name).toBe("iOS");
160
+ expect(deviceInfo.os.version).toBe("17.0");
161
+ });
162
+
163
+ test("delegates to adapter getDeviceInfo", async () => {
164
+ const mockLogging = createMockLoggingClient();
165
+ const mockStorage = createMockStorageClient();
166
+ const mockUtils = createMockUtilsClient();
167
+
168
+ let getDeviceInfoCalled = false;
169
+ const customAdapter = {
170
+ getDeviceInfo: async () => {
171
+ getDeviceInfoCalled = true;
172
+ return {
173
+ application: { name: "Custom", version: "2.0.0", build: "200", bundle_id: "com.custom" },
174
+ hardware: { brand: "Custom", model: "Device", device_type: "TABLET" },
175
+ os: { name: "CustomOS", version: "1.0" },
176
+ notifications: { push_token: null, platform: null },
177
+ update: null,
178
+ };
179
+ },
180
+ } as DeviceInfoAdapter;
181
+
182
+ const client = new DeviceClient(
183
+ mockLogging as never,
184
+ mockUtils as never,
185
+ mockStorage as never,
186
+ { adapter: customAdapter }
187
+ );
188
+
189
+ const deviceInfo = await client.getDeviceInfo();
190
+
191
+ expect(getDeviceInfoCalled).toBe(true);
192
+ expect(deviceInfo.application.name).toBe("Custom");
193
+ });
194
+ });
195
+ });
@@ -0,0 +1,69 @@
1
+ import type { DeviceInfo } from "@teardown/schemas";
2
+ import type { Logger, LoggingClient } from "../logging/";
3
+ import type { StorageClient, SupportedStorage } from "../storage";
4
+ import type { UtilsClient } from "../utils/utils.client";
5
+ import type { DeviceInfoAdapter } from "./device.adpater-interface";
6
+
7
+ // TODO: sort out why importing these enuims from schemas is not working - @teardown/schemas
8
+ export enum NotificationPlatformEnum {
9
+ APNS = "APNS", // Apple Push Notification Service
10
+ FCM = "FCM", // Firebase Cloud Messaging
11
+ EXPO = "EXPO", // Expo Push Notifications
12
+ }
13
+
14
+ // TODO: sort out why importing these enuims from schemas is not working - @teardown/schemas
15
+ export enum DevicePlatformEnum {
16
+ IOS = "IOS",
17
+ ANDROID = "ANDROID",
18
+ WEB = "WEB",
19
+ WINDOWS = "WINDOWS",
20
+ MACOS = "MACOS",
21
+ LINUX = "LINUX",
22
+ PHONE = "PHONE",
23
+ TABLET = "TABLET",
24
+ DESKTOP = "DESKTOP",
25
+ CONSOLE = "CONSOLE",
26
+ TV = "TV",
27
+ WEARABLE = "WEARABLE",
28
+ GAME_CONSOLE = "GAME_CONSOLE",
29
+ VR = "VR",
30
+ UNKNOWN = "UNKNOWN",
31
+ OTHER = "OTHER",
32
+ }
33
+
34
+ export type DeviceClientOptions = {
35
+ adapter: DeviceInfoAdapter;
36
+ };
37
+ export class DeviceClient {
38
+ private logger: Logger;
39
+ private storage: SupportedStorage;
40
+
41
+ constructor(
42
+ logging: LoggingClient,
43
+ private readonly utils: UtilsClient,
44
+ storage: StorageClient,
45
+ private readonly options: DeviceClientOptions
46
+ ) {
47
+ this.logger = logging.createLogger({
48
+ name: "DeviceClient",
49
+ });
50
+ this.storage = storage.createStorage("device");
51
+ }
52
+
53
+ async getDeviceId(): Promise<string> {
54
+
55
+ this.logger.debug("Getting device ID");
56
+ const deviceId = this.storage.getItem("deviceId");
57
+ if (deviceId) {
58
+ return deviceId;
59
+ }
60
+
61
+ const newDeviceId = await this.utils.generateRandomUUID();
62
+
63
+ return newDeviceId;
64
+ }
65
+
66
+ async getDeviceInfo(): Promise<DeviceInfo> {
67
+ return this.options.adapter.getDeviceInfo();
68
+ }
69
+ }