@symbiote-native/device 0.0.1 → 0.2.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 +3 -3
- package/build/angular/index.js +1 -1
- package/build/core/device.js +2 -2
- package/build/core/index.js +2 -2
- package/build/core/native-module.js +1 -1
- package/build/react/index.js +1 -1
- package/build/svelte/index.d.ts +1 -0
- package/build/svelte/index.js +6 -0
- package/build/vue/index.js +1 -1
- package/package.json +30 -13
- package/src/svelte/index.ts +6 -0
- package/src/core/device.test.ts +0 -202
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
|
|
190
|
-
`examples/expo
|
|
191
|
-
|
|
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
|
|
package/build/angular/index.js
CHANGED
|
@@ -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';
|
package/build/core/device.js
CHANGED
|
@@ -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
|
/**
|
package/build/core/index.js
CHANGED
|
@@ -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';
|
package/build/react/index.js
CHANGED
|
@@ -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';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../core';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/device/svelte: the Svelte entry over the framework-agnostic core. Upstream
|
|
2
|
+
// ships eager constants and free async functions, no per-instance state and no event stream
|
|
3
|
+
// (unlike @symbiote-native/sensors) — there is nothing for a rune to wrap, so this is a plain
|
|
4
|
+
// re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes — never React's
|
|
5
|
+
// `hooks` or Vue's `composables`), and this package has none to fill.
|
|
6
|
+
export * from '../core/index.js';
|
package/build/vue/index.js
CHANGED
|
@@ -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
|
|
3
|
+
"version": "0.2.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": {
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"types": "./build/react/index.d.ts",
|
|
31
31
|
"default": "./build/react/index.js"
|
|
32
32
|
},
|
|
33
|
+
"./svelte": {
|
|
34
|
+
"types": "./build/svelte/index.d.ts",
|
|
35
|
+
"default": "./build/svelte/index.js"
|
|
36
|
+
},
|
|
33
37
|
"./angular": {
|
|
34
38
|
"types": "./build-ngc/angular/index.d.ts",
|
|
35
39
|
"react-native": "./build-ngc/angular/index.js",
|
|
@@ -40,7 +44,10 @@
|
|
|
40
44
|
"src",
|
|
41
45
|
"build",
|
|
42
46
|
"build-ngc",
|
|
43
|
-
"native-link.json"
|
|
47
|
+
"native-link.json",
|
|
48
|
+
"!src/**/*.test.*",
|
|
49
|
+
"!src/**/*.spec.*",
|
|
50
|
+
"!src/**/*.detox.*"
|
|
44
51
|
],
|
|
45
52
|
"publishConfig": {
|
|
46
53
|
"access": "public"
|
|
@@ -50,15 +57,17 @@
|
|
|
50
57
|
"expo-modules-core": "57.0.5"
|
|
51
58
|
},
|
|
52
59
|
"peerDependencies": {
|
|
60
|
+
"@symbiote-native/angular": ">=0.6.2",
|
|
53
61
|
"@symbiote-native/engine": ">=0.1.7",
|
|
62
|
+
"@symbiote-native/react": ">=0.2.8",
|
|
63
|
+
"@symbiote-native/svelte": ">=0.1.0",
|
|
64
|
+
"@symbiote-native/vue": ">=0.3.8",
|
|
54
65
|
"@angular/core": ">=20",
|
|
55
66
|
"@vue/runtime-core": "^3.5.13",
|
|
56
67
|
"react": ">=19.0.0",
|
|
57
68
|
"react-native": ">=0.86",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"@symbiote-native/react": "0.2.8",
|
|
61
|
-
"@symbiote-native/vue": "0.3.8"
|
|
69
|
+
"svelte": ">=5.56.0",
|
|
70
|
+
"vue": ">=3.5.0"
|
|
62
71
|
},
|
|
63
72
|
"peerDependenciesMeta": {
|
|
64
73
|
"@symbiote-native/angular": {
|
|
@@ -67,6 +76,9 @@
|
|
|
67
76
|
"@symbiote-native/react": {
|
|
68
77
|
"optional": true
|
|
69
78
|
},
|
|
79
|
+
"@symbiote-native/svelte": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
70
82
|
"@symbiote-native/vue": {
|
|
71
83
|
"optional": true
|
|
72
84
|
},
|
|
@@ -79,24 +91,29 @@
|
|
|
79
91
|
"react": {
|
|
80
92
|
"optional": true
|
|
81
93
|
},
|
|
94
|
+
"svelte": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
82
97
|
"vue": {
|
|
83
98
|
"optional": true
|
|
84
99
|
}
|
|
85
100
|
},
|
|
86
101
|
"devDependencies": {
|
|
87
|
-
"@angular/compiler": "
|
|
88
|
-
"@angular/compiler-cli": "
|
|
89
|
-
"@angular/core": "
|
|
102
|
+
"@angular/compiler": "~22.0.8",
|
|
103
|
+
"@angular/compiler-cli": "~22.0.8",
|
|
104
|
+
"@angular/core": "~22.0.8",
|
|
90
105
|
"@types/node": "^26.0.0",
|
|
91
106
|
"@types/react": "^19.2.0",
|
|
92
107
|
"@vue/runtime-core": "^3.5.13",
|
|
93
108
|
"react": "19.2.3",
|
|
109
|
+
"svelte": "^5.56.0",
|
|
94
110
|
"typescript": "~6.0.0",
|
|
111
|
+
"@symbiote-native/angular": "0.7.0",
|
|
112
|
+
"@symbiote-native/engine": "0.2.0",
|
|
113
|
+
"@symbiote-native/svelte": "0.2.0",
|
|
114
|
+
"@symbiote-native/react": "0.3.0",
|
|
95
115
|
"@symbiote-native/test-utils": "0.1.6",
|
|
96
|
-
"@symbiote-native/
|
|
97
|
-
"@symbiote-native/engine": "0.1.7",
|
|
98
|
-
"@symbiote-native/vue": "0.3.8",
|
|
99
|
-
"@symbiote-native/react": "0.2.8"
|
|
116
|
+
"@symbiote-native/vue": "0.4.0"
|
|
100
117
|
},
|
|
101
118
|
"scripts": {
|
|
102
119
|
"typecheck": "tsc --build",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @symbiote-native/device/svelte: the Svelte entry over the framework-agnostic core. Upstream
|
|
2
|
+
// ships eager constants and free async functions, no per-instance state and no event stream
|
|
3
|
+
// (unlike @symbiote-native/sensors) — there is nothing for a rune to wrap, so this is a plain
|
|
4
|
+
// re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes — never React's
|
|
5
|
+
// `hooks` or Vue's `composables`), and this package has none to fill.
|
|
6
|
+
export * from '../core';
|
package/src/core/device.test.ts
DELETED
|
@@ -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
|
-
});
|