@symbiote-native/device 3.0.1 → 3.0.3
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 +41 -29
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @symbiote-native/device
|
|
2
2
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
-
[`expo-device`](https://github.com/expo/expo/tree/main/packages/expo-device)
|
|
5
|
-
|
|
6
|
-
root/jailbreak detection, side-loading detection, and platform-feature queries
|
|
4
|
+
[`expo-device`](https://github.com/expo/expo/tree/main/packages/expo-device):
|
|
5
|
+
physical device information: brand/model/OS constants, uptime, max-memory,
|
|
6
|
+
root/jailbreak detection, side-loading detection, and platform-feature queries - usable from
|
|
7
7
|
**every** adapter, React, Vue, Svelte, Solid, and Angular, not just React. Like
|
|
8
8
|
[`@symbiote-native/local-auth`](../local-auth) and unlike this repo's stateful Expo wrapper
|
|
9
9
|
([`@symbiote-native/sensors`](../sensors), an `EventEmitter` + live-subscription surface), every
|
|
10
10
|
export here is either an eagerly-resolved constant or a one-shot async call with no per-instance
|
|
11
|
-
state, so there is no hook/composable/service to wrap
|
|
11
|
+
state, so there is no hook/composable/service to wrap - the React, Vue, and Angular entry points
|
|
12
12
|
are plain re-exports of the same `core`.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
@@ -25,39 +25,39 @@ npx @symbiote-native/cli new my-app --device
|
|
|
25
25
|
npx @symbiote-native/cli add --device
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Either way: installs `@symbiote-native/device` and wires the native autolinking automatically
|
|
28
|
+
Either way: installs `@symbiote-native/device` and wires the native autolinking automatically - see
|
|
29
29
|
[`@symbiote-native/cli`](../cli).
|
|
30
30
|
|
|
31
31
|
<details>
|
|
32
|
-
<summary>Manual install (no CLI
|
|
32
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
npm install @symbiote-native/device
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
`expo-device` and `expo-modules-core` come along as regular dependencies, pinned to exact
|
|
39
|
-
versions
|
|
39
|
+
versions - never install either yourself, and never add the `expo` meta-package to your project
|
|
40
40
|
(it bundles its own Metro/Babel pipeline, which conflicts with this project's own).
|
|
41
41
|
|
|
42
42
|
## Required one-time step: native autolinking wiring
|
|
43
43
|
|
|
44
44
|
Unlike a plain RN native module, `expo-device`'s native code is discovered by
|
|
45
|
-
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism
|
|
45
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism - this needs wiring
|
|
46
46
|
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
47
47
|
package with zero further changes:
|
|
48
48
|
|
|
49
49
|
| Platform | Touches |
|
|
50
50
|
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
-
| iOS | `ios/Podfile`
|
|
52
|
-
| iOS | `AppDelegate.swift`
|
|
53
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
54
|
-
| Android | `MainApplication.kt`
|
|
51
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
52
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
53
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
54
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
55
55
|
|
|
56
56
|
Full mechanics live in the `symbiote-expo-native-module` project skill. Reference
|
|
57
57
|
implementation: `examples/expo-react/ios/Podfile` and
|
|
58
58
|
`examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt`.
|
|
59
59
|
|
|
60
|
-
`expo-device` needs no runtime permission on either platform
|
|
60
|
+
`expo-device` needs no runtime permission on either platform - every constant and function here
|
|
61
61
|
reads plain system/build information, nothing gated by a permission prompt.
|
|
62
62
|
|
|
63
63
|
</details>
|
|
@@ -73,11 +73,11 @@ src/core/ Eager constants (isDevice, brand, manufacturer, modelId, modelName
|
|
|
73
73
|
isSideLoadingEnabledAsync / getPlatformFeaturesAsync / hasPlatformFeatureAsync,
|
|
74
74
|
and the DeviceType enum. native-module.ts resolves the native module via
|
|
75
75
|
expo-modules-core's requireNativeModule.
|
|
76
|
-
src/angular/ @symbiote-native/device/angular
|
|
76
|
+
src/angular/ @symbiote-native/device/angular - export * from '../core'
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
`./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto
|
|
80
|
-
`src/core/`
|
|
80
|
+
`src/core/` - no physical per-framework file, since there's nothing to subscribe to or clean up.
|
|
81
81
|
`./angular` stays a physical file/subpath since Angular ships through a separate `ngc`/AOT build
|
|
82
82
|
(`build-ngc/`).
|
|
83
83
|
|
|
@@ -201,7 +201,7 @@ onMounted(() => {
|
|
|
201
201
|
```
|
|
202
202
|
|
|
203
203
|
```tsx
|
|
204
|
-
// Solid
|
|
204
|
+
// Solid - a component body runs once, so the two async values need a signal; the eager
|
|
205
205
|
// constants (brand, modelName, ...) are plain reads with nothing to subscribe to.
|
|
206
206
|
import { createSignal, onMount } from 'solid-js';
|
|
207
207
|
import {
|
|
@@ -296,21 +296,21 @@ export class DeviceScreen {
|
|
|
296
296
|
}
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
-
There's no per-instance service to `inject()` in the Angular case
|
|
299
|
+
There's no per-instance service to `inject()` in the Angular case - every constant/function is a
|
|
300
300
|
plain export off the core package, read straight in the constructor or class-field initializer.
|
|
301
|
-
These snippets mirror the real canary demo screens
|
|
301
|
+
These snippets mirror the real canary demo screens - `examples/expo-react/screens/DeviceScreen.tsx`,
|
|
302
302
|
`examples/expo-vue-sfc/screens/DeviceScreen.vue`, `examples/expo-vue-tsx/screens/DeviceScreen.tsx`,
|
|
303
303
|
`examples/expo-svelte/screens/DeviceScreen.svelte`, `examples/expo-solid/screens/DeviceScreen.tsx`,
|
|
304
304
|
`examples/expo-angular/src/screens/DeviceScreen.ts`.
|
|
305
305
|
|
|
306
306
|
## API
|
|
307
307
|
|
|
308
|
-
Eagerly-resolved constants, plus a handful of one-shot async functions
|
|
309
|
-
per-instance state
|
|
308
|
+
Eagerly-resolved constants, plus a handful of one-shot async functions - no event stream, no
|
|
309
|
+
per-instance state - so the React/Vue/Svelte/Solid/Angular entry points above are plain
|
|
310
310
|
re-exports of `core` with nothing adapter-specific to add.
|
|
311
311
|
|
|
312
312
|
```ts
|
|
313
|
-
// Constants
|
|
313
|
+
// Constants - resolved once, at import time, straight off the native module:
|
|
314
314
|
isDevice: boolean
|
|
315
315
|
brand: string | null
|
|
316
316
|
manufacturer: string | null
|
|
@@ -340,12 +340,12 @@ getPlatformFeaturesAsync(): Promise<string[]> // Android only; [] elsewhe
|
|
|
340
340
|
hasPlatformFeatureAsync(feature: string): Promise<boolean> // Android only; false elsewhere, never throws
|
|
341
341
|
```
|
|
342
342
|
|
|
343
|
-
Plus `DeviceType` (`UNKNOWN`/`PHONE`/`TABLET`/`DESKTOP`/`TV`)
|
|
343
|
+
Plus `DeviceType` (`UNKNOWN`/`PHONE`/`TABLET`/`DESKTOP`/`TV`) - ported from upstream's
|
|
344
344
|
`Device.types.ts`.
|
|
345
345
|
|
|
346
346
|
```ts
|
|
347
347
|
import { getDeviceTypeAsync, isDevice } from '@symbiote-native/device';
|
|
348
|
-
// or the framework-scoped entry points
|
|
348
|
+
// or the framework-scoped entry points - identical surface, re-exported verbatim:
|
|
349
349
|
import { getDeviceTypeAsync } from '@symbiote-native/device/react';
|
|
350
350
|
import { getDeviceTypeAsync } from '@symbiote-native/device/vue';
|
|
351
351
|
import { getDeviceTypeAsync } from '@symbiote-native/device/svelte';
|
|
@@ -357,26 +357,38 @@ import { getDeviceTypeAsync } from '@symbiote-native/device/angular';
|
|
|
357
357
|
|
|
358
358
|
- **Every function except `getPlatformFeaturesAsync`/`hasPlatformFeatureAsync` throws an
|
|
359
359
|
`UnavailabilityError` when the native method is missing.** Those two are the deliberate
|
|
360
|
-
exceptions
|
|
360
|
+
exceptions - they resolve to `[]`/`false` instead, matching upstream, since a platform-feature
|
|
361
361
|
query on a platform with no such concept (iOS) is a normal "no" answer, not an error.
|
|
362
362
|
- **`getMaxMemoryAsync`'s `-1` native sentinel means "no inherent limit"** and is normalized to
|
|
363
|
-
`Number.MAX_SAFE_INTEGER` before it reaches your code
|
|
364
|
-
- **`isRootedExperimentalAsync` is a best-effort check, not a guarantee**
|
|
363
|
+
`Number.MAX_SAFE_INTEGER` before it reaches your code - you never see the raw `-1`.
|
|
364
|
+
- **`isRootedExperimentalAsync` is a best-effort check, not a guarantee** - root/jailbreak
|
|
365
365
|
detection bypasses exist on both platforms; a `false` result does not prove the device is
|
|
366
366
|
unmodified.
|
|
367
367
|
|
|
368
|
+
## Common questions
|
|
369
|
+
|
|
370
|
+
- **`deviceName` is "iPhone" on iOS 16+.** Needs the `user-assigned-device-name` entitlement.
|
|
371
|
+
- **`modelName` / `deviceName` is `null`.** Not determinable on that device.
|
|
372
|
+
- **`isDevice` is `true` on the iOS Simulator.** Reported upstream; do not rely on it.
|
|
373
|
+
- **`getDeviceTypeAsync` says `TABLET` on a phone.** Reported on some Android models.
|
|
374
|
+
|
|
375
|
+
Sources: [Expo docs: Device](https://docs.expo.dev/versions/latest/sdk/device/),
|
|
376
|
+
[expo/expo#19639](https://github.com/expo/expo/issues/19639),
|
|
377
|
+
[expo/expo#19869](https://github.com/expo/expo/issues/19869),
|
|
378
|
+
[expo/expo#11409](https://github.com/expo/expo/issues/11409).
|
|
379
|
+
|
|
368
380
|
## Test it
|
|
369
381
|
|
|
370
|
-
No Fabric/Descriptor angle at all
|
|
382
|
+
No Fabric/Descriptor angle at all - every export here is a pure constant or async-function
|
|
371
383
|
surface, never a view or per-instance state. Tests inject a fake native-module object in place of
|
|
372
|
-
the real `requireNativeModule` resolution (`src/core/device.test.ts`, `vitest`)
|
|
384
|
+
the real `requireNativeModule` resolution (`src/core/device.test.ts`, `vitest`) - no
|
|
373
385
|
`installFabric()`, no ViewConfig. Native rendering itself is verified on-device (see the parent
|
|
374
386
|
[README](../../README.md) for the project's testing model).
|
|
375
387
|
|
|
376
388
|
Native autolinking wiring for `expo-modules-core` packages is already done in all six Expo
|
|
377
389
|
canary apps (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`,
|
|
378
390
|
`examples/expo-svelte`, `examples/expo-solid`, `examples/expo-angular`) via
|
|
379
|
-
`@symbiote-native/local-auth`/`@symbiote-native/sensors`
|
|
391
|
+
`@symbiote-native/local-auth`/`@symbiote-native/sensors` - this
|
|
380
392
|
package reuses that same wiring with zero further app-side changes, since
|
|
381
393
|
`expo-modules-autolinking` discovers any `expo-modules-core` package already present in
|
|
382
394
|
`node_modules`. A dedicated `DeviceScreen` demo has not been wired into those canaries yet.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/device",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "expo-device wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, 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": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"expo-device": "57.0.1",
|
|
73
|
-
"expo-modules-core": "57.0.
|
|
73
|
+
"expo-modules-core": "57.0.20"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@angular/core": ">=20",
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"solid-js": ">=1.9.0",
|
|
81
81
|
"svelte": ">=5.56.0",
|
|
82
82
|
"vue": ">=3.5.0",
|
|
83
|
-
"@symbiote-native/angular": "^3.
|
|
84
|
-
"@symbiote-native/engine": "^1.
|
|
85
|
-
"@symbiote-native/react": "^3.0
|
|
86
|
-
"@symbiote-native/solid": "^3.0
|
|
87
|
-
"@symbiote-native/svelte": "^3.0
|
|
88
|
-
"@symbiote-native/vue": "^3.0
|
|
83
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
84
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
85
|
+
"@symbiote-native/react": "^3.2.0",
|
|
86
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
87
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
88
|
+
"@symbiote-native/vue": "^3.2.0"
|
|
89
89
|
},
|
|
90
90
|
"peerDependenciesMeta": {
|
|
91
91
|
"@symbiote-native/angular": {
|
|
@@ -133,13 +133,13 @@
|
|
|
133
133
|
"solid-js": "^1.9.14",
|
|
134
134
|
"svelte": "^5.56.0",
|
|
135
135
|
"typescript": "~6.0.0",
|
|
136
|
-
"@symbiote-native/angular": "3.
|
|
137
|
-
"@symbiote-native/engine": "1.
|
|
138
|
-
"@symbiote-native/react": "3.0
|
|
139
|
-
"@symbiote-native/solid": "3.0
|
|
140
|
-
"@symbiote-native/svelte": "3.0
|
|
141
|
-
"@symbiote-native/test-utils": "0.4.
|
|
142
|
-
"@symbiote-native/vue": "3.0
|
|
136
|
+
"@symbiote-native/angular": "3.2.0",
|
|
137
|
+
"@symbiote-native/engine": "1.5.0",
|
|
138
|
+
"@symbiote-native/react": "3.2.0",
|
|
139
|
+
"@symbiote-native/solid": "3.1.0",
|
|
140
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
141
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
142
|
+
"@symbiote-native/vue": "3.2.0"
|
|
143
143
|
},
|
|
144
144
|
"scripts": {
|
|
145
145
|
"typecheck": "tsc --build",
|