@symbiote-native/device 0.0.1 → 0.1.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.
package/README.md CHANGED
@@ -186,9 +186,9 @@ export class DeviceScreen {
186
186
 
187
187
  There's no per-instance service to `inject()` in the Angular case — every constant/function is a
188
188
  plain export off the core package, read straight in the constructor or class-field initializer.
189
- These snippets are not yet demoed in a real canary screen — no `DeviceScreen.*` exists yet in
190
- `examples/expo-*` — so treat them as realistic reference code, not a trimmed-down copy of a
191
- running demo.
189
+ These snippets mirror the real canary demo screens — `examples/expo-react/screens/DeviceScreen.tsx`,
190
+ `examples/expo-vue-sfc/screens/DeviceScreen.vue`, `examples/expo-vue-tsx/screens/DeviceScreen.tsx`,
191
+ `examples/expo-angular/src/screens/DeviceScreen.ts`.
192
192
 
193
193
  ## API
194
194
 
@@ -1,4 +1,4 @@
1
1
  // @symbiote-native/device/angular: the Angular entry over the framework-agnostic core. Same
2
2
  // reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
3
  // service, so this is a plain re-export.
4
- export * from '../core';
4
+ export * from '../core/index.js';
@@ -15,8 +15,8 @@
15
15
  // below falls back with `??`, matching that per-field behavior without a redundant whole-module
16
16
  // check the rest of this codebase's convention doesn't otherwise use.
17
17
  import { UnavailabilityError } from 'expo-modules-core';
18
- import { expoDevice } from './native-module';
19
- import { DeviceType } from './types';
18
+ import { expoDevice } from './native-module.js';
19
+ import { DeviceType } from './types.js';
20
20
  const NATIVE_MODULE_NAME = 'expo-device';
21
21
  const MAX_MEMORY_UNLIMITED_SENTINEL = -1;
22
22
  /**
@@ -1,2 +1,2 @@
1
- export { isDevice, brand, manufacturer, modelId, modelName, designName, productName, deviceType, deviceYearClass, totalMemory, supportedCpuArchitectures, osName, osVersion, osBuildId, osInternalBuildId, osBuildFingerprint, platformApiLevel, deviceName, getDeviceTypeAsync, getUptimeAsync, getMaxMemoryAsync, isRootedExperimentalAsync, isSideLoadingEnabledAsync, getPlatformFeaturesAsync, hasPlatformFeatureAsync, } from './device';
2
- export { DeviceType } from './types';
1
+ export { isDevice, brand, manufacturer, modelId, modelName, designName, productName, deviceType, deviceYearClass, totalMemory, supportedCpuArchitectures, osName, osVersion, osBuildId, osInternalBuildId, osBuildFingerprint, platformApiLevel, deviceName, getDeviceTypeAsync, getUptimeAsync, getMaxMemoryAsync, isRootedExperimentalAsync, isSideLoadingEnabledAsync, getPlatformFeaturesAsync, hasPlatformFeatureAsync, } from './device.js';
2
+ export { DeviceType } from './types.js';
@@ -1,4 +1,4 @@
1
1
  import { requireNativeModule } from 'expo-modules-core';
2
- import { DeviceType } from './types';
2
+ import { DeviceType } from './types.js';
3
3
  const EXPO_DEVICE_MODULE_NAME = 'ExpoDevice';
4
4
  export const expoDevice = requireNativeModule(EXPO_DEVICE_MODULE_NAME);
@@ -3,4 +3,4 @@
3
3
  // (unlike @symbiote-native/sensors) — there is nothing for a hook to wrap, so this is a plain
4
4
  // re-export, mirroring how @symbiote-native/local-auth's React entry passes its own free
5
5
  // functions through untouched.
6
- export * from '../core';
6
+ export * from '../core/index.js';
@@ -1,4 +1,4 @@
1
1
  // @symbiote-native/device/vue: the Vue entry over the framework-agnostic core. Same reasoning as
2
2
  // the React entry — no per-instance state or event stream to wire onto Vue's reactivity, so this
3
3
  // is a plain re-export.
4
- export * from '../core';
4
+ export * from '../core/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/device",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "expo-device wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Device brand/model/OS constants, uptime, max-memory, root/jailbreak detection, side-loading detection, and platform-feature queries.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,7 +40,10 @@
40
40
  "src",
41
41
  "build",
42
42
  "build-ngc",
43
- "native-link.json"
43
+ "native-link.json",
44
+ "!src/**/*.test.*",
45
+ "!src/**/*.spec.*",
46
+ "!src/**/*.detox.*"
44
47
  ],
45
48
  "publishConfig": {
46
49
  "access": "public"
@@ -56,7 +59,7 @@
56
59
  "react": ">=19.0.0",
57
60
  "react-native": ">=0.86",
58
61
  "vue": ">=3.5.0",
59
- "@symbiote-native/angular": "0.6.1",
62
+ "@symbiote-native/angular": "0.6.2",
60
63
  "@symbiote-native/react": "0.2.8",
61
64
  "@symbiote-native/vue": "0.3.8"
62
65
  },
@@ -92,11 +95,11 @@
92
95
  "@vue/runtime-core": "^3.5.13",
93
96
  "react": "19.2.3",
94
97
  "typescript": "~6.0.0",
95
- "@symbiote-native/test-utils": "0.1.6",
96
- "@symbiote-native/angular": "0.6.1",
98
+ "@symbiote-native/angular": "0.6.2",
97
99
  "@symbiote-native/engine": "0.1.7",
98
- "@symbiote-native/vue": "0.3.8",
99
- "@symbiote-native/react": "0.2.8"
100
+ "@symbiote-native/react": "0.2.8",
101
+ "@symbiote-native/test-utils": "0.1.6",
102
+ "@symbiote-native/vue": "0.3.8"
100
103
  },
101
104
  "scripts": {
102
105
  "typecheck": "tsc --build",
@@ -1,202 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const FAKE_NATIVE_DEVICE = {
4
- isDevice: true,
5
- brand: 'Apple',
6
- manufacturer: 'Apple',
7
- modelId: 'iPhone14,2',
8
- modelName: 'iPhone 13 Pro',
9
- designName: null,
10
- productName: null,
11
- deviceType: 1,
12
- deviceYearClass: 2_021,
13
- totalMemory: 6_442_450_944,
14
- supportedCpuArchitectures: ['arm64'],
15
- osName: 'iOS',
16
- osVersion: '17.4',
17
- osBuildId: '21E219',
18
- osInternalBuildId: '21E219',
19
- osBuildFingerprint: null,
20
- platformApiLevel: null,
21
- deviceName: "Andrey's iPhone",
22
- getDeviceTypeAsync: vi.fn(async () => 1),
23
- getUptimeAsync: vi.fn(async () => 4_371_054),
24
- getMaxMemoryAsync: vi.fn(async () => 402_653_184),
25
- isRootedExperimentalAsync: vi.fn(async () => false),
26
- isSideLoadingEnabledAsync: vi.fn(async () => false),
27
- getPlatformFeaturesAsync: vi.fn(async () => ['android.hardware.sensor.accelerometer']),
28
- hasPlatformFeatureAsync: vi.fn(async () => true),
29
- };
30
-
31
- // The real ExpoDevice native module only exists on device — resolving it via
32
- // requireNativeModule() at import time would throw in this headless test run, so the
33
- // module-lookup file is faked in place of expo-modules-core's runtime resolution, the same
34
- // pattern packages/local-auth/src/core/local-authentication.test.ts uses.
35
- vi.mock('./native-module', () => ({
36
- expoDevice: FAKE_NATIVE_DEVICE,
37
- }));
38
-
39
- // expo-modules-core's real entry transitively imports 'react-native' for Platform/
40
- // TurboModuleRegistry, whose Flow-typed source Vitest's Oxc transform can't parse — same fake
41
- // packages/local-auth/src/core/local-authentication.test.ts uses.
42
- vi.mock('expo-modules-core', () => ({
43
- UnavailabilityError: class UnavailabilityError extends Error {
44
- constructor(moduleName: string, propertyName: string) {
45
- super(`${propertyName} is not available on ${moduleName}`);
46
- }
47
- },
48
- }));
49
-
50
- const {
51
- isDevice,
52
- brand,
53
- manufacturer,
54
- modelId,
55
- modelName,
56
- designName,
57
- productName,
58
- deviceType,
59
- deviceYearClass,
60
- totalMemory,
61
- supportedCpuArchitectures,
62
- osName,
63
- osVersion,
64
- osBuildId,
65
- osInternalBuildId,
66
- osBuildFingerprint,
67
- platformApiLevel,
68
- deviceName,
69
- getDeviceTypeAsync,
70
- getUptimeAsync,
71
- getMaxMemoryAsync,
72
- isRootedExperimentalAsync,
73
- isSideLoadingEnabledAsync,
74
- getPlatformFeaturesAsync,
75
- hasPlatformFeatureAsync,
76
- } = await import('./device');
77
-
78
- afterEach(() => {
79
- vi.clearAllMocks();
80
- });
81
-
82
- describe('eagerly resolved constants', () => {
83
- it('read straight off the native module at import time', () => {
84
- expect(isDevice).toBe(true);
85
- expect(brand).toBe('Apple');
86
- expect(manufacturer).toBe('Apple');
87
- expect(modelId).toBe('iPhone14,2');
88
- expect(modelName).toBe('iPhone 13 Pro');
89
- expect(designName).toBeNull();
90
- expect(productName).toBeNull();
91
- expect(deviceType).toBe(1);
92
- expect(deviceYearClass).toBe(2_021);
93
- expect(totalMemory).toBe(6_442_450_944);
94
- expect(supportedCpuArchitectures).toEqual(['arm64']);
95
- expect(osName).toBe('iOS');
96
- expect(osVersion).toBe('17.4');
97
- expect(osBuildId).toBe('21E219');
98
- expect(osInternalBuildId).toBe('21E219');
99
- expect(osBuildFingerprint).toBeNull();
100
- expect(platformApiLevel).toBeNull();
101
- expect(deviceName).toBe("Andrey's iPhone");
102
- });
103
- });
104
-
105
- describe('getDeviceTypeAsync', () => {
106
- it('delegates to the native module', async () => {
107
- await expect(getDeviceTypeAsync()).resolves.toBe(1);
108
- });
109
-
110
- it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
111
- const { getDeviceTypeAsync: native } = FAKE_NATIVE_DEVICE;
112
- // @ts-expect-error -- simulating a platform where the native module has no such method
113
- FAKE_NATIVE_DEVICE.getDeviceTypeAsync = undefined;
114
-
115
- await expect(getDeviceTypeAsync()).rejects.toThrow(
116
- 'getDeviceTypeAsync is not available on expo-device',
117
- );
118
-
119
- FAKE_NATIVE_DEVICE.getDeviceTypeAsync = native;
120
- });
121
- });
122
-
123
- describe('getUptimeAsync', () => {
124
- it('delegates to the native module', async () => {
125
- await expect(getUptimeAsync()).resolves.toBe(4_371_054);
126
- });
127
- });
128
-
129
- describe('getMaxMemoryAsync', () => {
130
- it('delegates to the native module', async () => {
131
- await expect(getMaxMemoryAsync()).resolves.toBe(402_653_184);
132
- });
133
-
134
- it('normalizes the -1 unlimited sentinel to Number.MAX_SAFE_INTEGER', async () => {
135
- const { getMaxMemoryAsync: native } = FAKE_NATIVE_DEVICE;
136
- FAKE_NATIVE_DEVICE.getMaxMemoryAsync = vi.fn(async () => -1);
137
-
138
- await expect(getMaxMemoryAsync()).resolves.toBe(Number.MAX_SAFE_INTEGER);
139
-
140
- FAKE_NATIVE_DEVICE.getMaxMemoryAsync = native;
141
- });
142
-
143
- it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
144
- const { getMaxMemoryAsync: native } = FAKE_NATIVE_DEVICE;
145
- // @ts-expect-error -- simulating a platform where the native module has no such method
146
- FAKE_NATIVE_DEVICE.getMaxMemoryAsync = undefined;
147
-
148
- await expect(getMaxMemoryAsync()).rejects.toThrow(
149
- 'getMaxMemoryAsync is not available on expo-device',
150
- );
151
-
152
- FAKE_NATIVE_DEVICE.getMaxMemoryAsync = native;
153
- });
154
- });
155
-
156
- describe('isRootedExperimentalAsync', () => {
157
- it('delegates to the native module', async () => {
158
- await expect(isRootedExperimentalAsync()).resolves.toBe(false);
159
- });
160
- });
161
-
162
- describe('isSideLoadingEnabledAsync', () => {
163
- it('delegates to the native module', async () => {
164
- await expect(isSideLoadingEnabledAsync()).resolves.toBe(false);
165
- });
166
- });
167
-
168
- describe('getPlatformFeaturesAsync', () => {
169
- it('delegates to the native module', async () => {
170
- await expect(getPlatformFeaturesAsync()).resolves.toEqual([
171
- 'android.hardware.sensor.accelerometer',
172
- ]);
173
- });
174
-
175
- it('resolves an empty array instead of throwing when the native method is absent', async () => {
176
- const { getPlatformFeaturesAsync: native } = FAKE_NATIVE_DEVICE;
177
- // @ts-expect-error -- simulating iOS, where getPlatformFeaturesAsync has no native implementation
178
- FAKE_NATIVE_DEVICE.getPlatformFeaturesAsync = undefined;
179
-
180
- await expect(getPlatformFeaturesAsync()).resolves.toEqual([]);
181
-
182
- FAKE_NATIVE_DEVICE.getPlatformFeaturesAsync = native;
183
- });
184
- });
185
-
186
- describe('hasPlatformFeatureAsync', () => {
187
- it('delegates to the native module', async () => {
188
- await expect(hasPlatformFeatureAsync('android.hardware.sensor.accelerometer')).resolves.toBe(
189
- true,
190
- );
191
- });
192
-
193
- it('resolves false instead of throwing when the native method is absent', async () => {
194
- const { hasPlatformFeatureAsync: native } = FAKE_NATIVE_DEVICE;
195
- // @ts-expect-error -- simulating iOS, where hasPlatformFeatureAsync has no native implementation
196
- FAKE_NATIVE_DEVICE.hasPlatformFeatureAsync = undefined;
197
-
198
- await expect(hasPlatformFeatureAsync('any')).resolves.toBe(false);
199
-
200
- FAKE_NATIVE_DEVICE.hasPlatformFeatureAsync = native;
201
- });
202
- });