@symbiote-native/sensors 0.3.0 → 1.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 (145) hide show
  1. package/README.md +61 -21
  2. package/build/angular/index.d.ts +1 -1
  3. package/build/angular/index.js +1 -1
  4. package/build/angular/services/accelerometer.service/index.js +1 -1
  5. package/build/angular/services/barometer.service/index.js +1 -1
  6. package/build/angular/services/device-motion.service/index.js +1 -1
  7. package/build/angular/services/gyroscope.service/index.js +1 -1
  8. package/build/angular/services/light-sensor.service/index.js +1 -1
  9. package/build/angular/services/magnetometer-uncalibrated.service/index.js +2 -2
  10. package/build/angular/services/magnetometer.service/index.js +1 -1
  11. package/build/angular/services/pedometer.service/index.js +1 -1
  12. package/build/core/index.d.ts +5 -5
  13. package/build/core/index.js +5 -5
  14. package/build/react/hooks/use-magnetometer-uncalibrated/index.js +1 -1
  15. package/build/react/index.d.ts +1 -1
  16. package/build/react/index.js +1 -1
  17. package/build/solid/index.d.ts +10 -0
  18. package/build/solid/index.js +17 -0
  19. package/build/solid/primitives/create-accelerometer.d.ts +4 -0
  20. package/build/solid/primitives/create-accelerometer.js +5 -0
  21. package/build/solid/primitives/create-barometer.d.ts +4 -0
  22. package/build/solid/primitives/create-barometer.js +5 -0
  23. package/build/solid/primitives/create-device-motion.d.ts +4 -0
  24. package/build/solid/primitives/create-device-motion.js +5 -0
  25. package/build/solid/primitives/create-gyroscope.d.ts +4 -0
  26. package/build/solid/primitives/create-gyroscope.js +5 -0
  27. package/build/solid/primitives/create-light-sensor.d.ts +4 -0
  28. package/build/solid/primitives/create-light-sensor.js +5 -0
  29. package/build/solid/primitives/create-magnetometer-uncalibrated.d.ts +4 -0
  30. package/build/solid/primitives/create-magnetometer-uncalibrated.js +5 -0
  31. package/build/solid/primitives/create-magnetometer.d.ts +4 -0
  32. package/build/solid/primitives/create-magnetometer.js +5 -0
  33. package/build/solid/primitives/create-pedometer.d.ts +3 -0
  34. package/build/solid/primitives/create-pedometer.js +22 -0
  35. package/build/solid/primitives/device-sensor-accessor.d.ts +9 -0
  36. package/build/solid/primitives/device-sensor-accessor.js +66 -0
  37. package/build/svelte/index.d.ts +1 -1
  38. package/build/svelte/index.js +1 -1
  39. package/build/svelte/runes/use-accelerometer.svelte.js +1 -1
  40. package/build/svelte/runes/use-barometer.svelte.js +1 -1
  41. package/build/svelte/runes/use-device-motion.svelte.js +1 -1
  42. package/build/svelte/runes/use-gyroscope.svelte.js +1 -1
  43. package/build/svelte/runes/use-light-sensor.svelte.js +1 -1
  44. package/build/svelte/runes/use-magnetometer.svelte.js +1 -1
  45. package/build/svelte/runes/use-pedometer.svelte.js +1 -1
  46. package/build/vue/composables/use-barometer/index.js +1 -1
  47. package/build/vue/composables/use-device-motion/index.js +1 -1
  48. package/build/vue/composables/use-gyroscope/index.js +1 -1
  49. package/build/vue/composables/use-light-sensor/index.js +1 -1
  50. package/build/vue/composables/use-magnetometer/index.js +1 -1
  51. package/build/vue/composables/use-pedometer/index.js +1 -1
  52. package/build/vue/index.d.ts +1 -1
  53. package/build/vue/index.js +1 -1
  54. package/build-ngc/angular/index.d.ts +1 -1
  55. package/build-ngc/angular/index.js +1 -1
  56. package/build-ngc/angular/index.js.map +1 -1
  57. package/build-ngc/angular/services/accelerometer.service/index.js +1 -1
  58. package/build-ngc/angular/services/accelerometer.service/index.js.map +1 -1
  59. package/build-ngc/angular/services/barometer.service/index.js +1 -1
  60. package/build-ngc/angular/services/barometer.service/index.js.map +1 -1
  61. package/build-ngc/angular/services/device-motion.service/index.js +1 -1
  62. package/build-ngc/angular/services/device-motion.service/index.js.map +1 -1
  63. package/build-ngc/angular/services/gyroscope.service/index.js +1 -1
  64. package/build-ngc/angular/services/gyroscope.service/index.js.map +1 -1
  65. package/build-ngc/angular/services/light-sensor.service/index.js +1 -1
  66. package/build-ngc/angular/services/light-sensor.service/index.js.map +1 -1
  67. package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js +2 -2
  68. package/build-ngc/angular/services/magnetometer-uncalibrated.service/index.js.map +1 -1
  69. package/build-ngc/angular/services/magnetometer.service/index.js +1 -1
  70. package/build-ngc/angular/services/magnetometer.service/index.js.map +1 -1
  71. package/build-ngc/angular/services/pedometer.service/index.js +1 -1
  72. package/build-ngc/angular/services/pedometer.service/index.js.map +1 -1
  73. package/build-ngc/core/barometer.js.map +1 -1
  74. package/build-ngc/core/device-motion.js.map +1 -1
  75. package/build-ngc/core/device-sensor.js.map +1 -1
  76. package/build-ngc/core/gyroscope.js.map +1 -1
  77. package/build-ngc/core/index.d.ts +5 -5
  78. package/build-ngc/core/index.js +5 -5
  79. package/build-ngc/core/index.js.map +1 -1
  80. package/build-ngc/core/magnetometer-uncalibrated.js.map +1 -1
  81. package/build-ngc/core/native/exponent-barometer.js.map +1 -1
  82. package/build-ngc/core/native/exponent-device-motion.js.map +1 -1
  83. package/build-ngc/core/native/exponent-gyroscope.js.map +1 -1
  84. package/build-ngc/core/native/exponent-light-sensor/index.ios.js.map +1 -1
  85. package/build-ngc/core/native/exponent-magnetometer-uncalibrated.js.map +1 -1
  86. package/build-ngc/core/native/exponent-pedometer.js.map +1 -1
  87. package/build-ngc/core/pedometer.js.map +1 -1
  88. package/package.json +41 -14
  89. package/src/angular/index.ts +5 -1
  90. package/src/angular/services/accelerometer.service/index.ts +11 -2
  91. package/src/angular/services/barometer.service/index.ts +11 -2
  92. package/src/angular/services/device-motion.service/index.ts +11 -2
  93. package/src/angular/services/gyroscope.service/index.ts +11 -2
  94. package/src/angular/services/light-sensor.service/index.ts +11 -2
  95. package/src/angular/services/magnetometer-uncalibrated.service/index.ts +21 -5
  96. package/src/angular/services/magnetometer.service/index.ts +11 -2
  97. package/src/angular/services/pedometer.service/index.ts +8 -1
  98. package/src/core/barometer.ts +4 -1
  99. package/src/core/device-motion.ts +12 -2
  100. package/src/core/device-sensor.ts +8 -2
  101. package/src/core/gyroscope.ts +4 -1
  102. package/src/core/index.ts +25 -5
  103. package/src/core/magnetometer-uncalibrated.ts +2 -1
  104. package/src/core/native/exponent-barometer.ts +3 -3
  105. package/src/core/native/exponent-device-motion.ts +8 -6
  106. package/src/core/native/exponent-gyroscope.ts +3 -3
  107. package/src/core/native/exponent-light-sensor/index.ios.ts +7 -6
  108. package/src/core/native/exponent-magnetometer-uncalibrated.ts +2 -1
  109. package/src/core/native/exponent-pedometer.ts +4 -1
  110. package/src/core/pedometer.ts +7 -2
  111. package/src/react/hooks/use-accelerometer/index.ts +5 -2
  112. package/src/react/hooks/use-barometer/index.ts +6 -2
  113. package/src/react/hooks/use-device-motion/index.ts +5 -2
  114. package/src/react/hooks/use-gyroscope/index.ts +6 -2
  115. package/src/react/hooks/use-light-sensor/index.ts +5 -2
  116. package/src/react/hooks/use-magnetometer/index.ts +5 -2
  117. package/src/react/hooks/use-magnetometer-uncalibrated/index.ts +6 -2
  118. package/src/react/index.ts +5 -1
  119. package/src/solid/index.ts +19 -0
  120. package/src/solid/primitives/create-accelerometer.ts +16 -0
  121. package/src/solid/primitives/create-barometer.ts +16 -0
  122. package/src/solid/primitives/create-device-motion.ts +16 -0
  123. package/src/solid/primitives/create-gyroscope.ts +16 -0
  124. package/src/solid/primitives/create-light-sensor.ts +16 -0
  125. package/src/solid/primitives/create-magnetometer-uncalibrated.ts +19 -0
  126. package/src/solid/primitives/create-magnetometer.ts +16 -0
  127. package/src/solid/primitives/create-pedometer.ts +31 -0
  128. package/src/solid/primitives/device-sensor-accessor.ts +105 -0
  129. package/src/svelte/index.ts +5 -1
  130. package/src/svelte/runes/use-accelerometer.svelte.ts +5 -1
  131. package/src/svelte/runes/use-barometer.svelte.ts +5 -1
  132. package/src/svelte/runes/use-device-motion.svelte.ts +5 -1
  133. package/src/svelte/runes/use-gyroscope.svelte.ts +5 -1
  134. package/src/svelte/runes/use-light-sensor.svelte.ts +5 -1
  135. package/src/svelte/runes/use-magnetometer-uncalibrated.svelte.ts +4 -3
  136. package/src/svelte/runes/use-magnetometer.svelte.ts +5 -1
  137. package/src/svelte/runes/use-pedometer.svelte.ts +5 -1
  138. package/src/vue/composables/use-accelerometer/index.ts +3 -1
  139. package/src/vue/composables/use-barometer/index.ts +8 -2
  140. package/src/vue/composables/use-device-motion/index.ts +8 -2
  141. package/src/vue/composables/use-gyroscope/index.ts +8 -2
  142. package/src/vue/composables/use-light-sensor/index.ts +8 -2
  143. package/src/vue/composables/use-magnetometer/index.ts +8 -2
  144. package/src/vue/composables/use-pedometer/index.ts +5 -1
  145. package/src/vue/index.ts +5 -1
package/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  A wrapper package for [SymbioteNative](../../README.md) that makes
4
4
  [`expo-sensors`](https://github.com/expo/expo/tree/main/packages/expo-sensors) — Accelerometer,
5
5
  Barometer, DeviceMotion, Gyroscope, LightSensor, Magnetometer, MagnetometerUncalibrated, and
6
- Pedometer — usable from **every** adapter, React, Vue, and Angular. Unlike this repo's other
7
- wrappers ([`@symbiote-native/slider`](../slider), [`@symbiote-native/navigation`](../navigation),
6
+ Pedometer — usable from **every** adapter, React, Vue, Svelte, Solid, and Angular. Unlike this
7
+ repo's other wrappers ([`@symbiote-native/slider`](../slider), [`@symbiote-native/navigation`](../navigation),
8
8
  [`@symbiote-native/splash-screen`](../splash-screen)), `expo-sensors` isn't a plain RN native
9
9
  module or view — it's built on `expo-modules-core`, so its native code autolinks straight out of
10
10
  `node_modules` via `expo-modules-autolinking`, no proxy `react-native.config.cjs`/podspec to ship.
@@ -29,12 +29,12 @@ Unlike a plain RN native module, `expo-sensors`' native code is discovered by
29
29
  into the native host app **once**, covering this package and every future `expo-modules-core`
30
30
  package with zero further changes:
31
31
 
32
- | Platform | Touches |
33
- |---|---|
34
- | iOS | `ios/Podfile` — add `use_expo_modules!` |
35
- | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
36
- | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
37
- | 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) |
32
+ | Platform | Touches |
33
+ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
35
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
36
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
37
+ | 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) |
38
38
 
39
39
  Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
40
40
  peer-dependency exclusion list, per-sensor permission strings — live in the
@@ -55,12 +55,16 @@ src/core/ DeviceSensor base class + one class per sensor (Accelerome
55
55
  module by name through expo-modules-core's requireNativeModule.
56
56
  src/react/hooks/ @symbiote-native/sensors/react — useAccelerometer, useBarometer, ...
57
57
  src/vue/composables/ @symbiote-native/sensors/vue — useAccelerometer, useBarometer, ... (same names)
58
+ src/svelte/runes/ @symbiote-native/sensors/svelte — useAccelerometer, useBarometer, ... (same names)
59
+ src/solid/primitives/ @symbiote-native/sensors/solid — createAccelerometer, createBarometer, ...
58
60
  src/angular/services/ @symbiote-native/sensors/angular — AccelerometerService, BarometerService, ...
59
61
  ```
60
62
 
61
- Each adapter's hook/composable/service is a thin lifecycle wrapper (subscribe on mount, unsubscribe
62
- on unmount) over the same `core` singleton — the subscription, permission, and update-interval
63
- logic is written once and shared by all three.
63
+ Each adapter's hook/composable/rune/primitive/service is a thin lifecycle wrapper (subscribe on
64
+ mount, unsubscribe on unmount) over the same `core` singleton — the subscription, permission, and
65
+ update-interval logic is written once and shared by all of them. Solid is the one adapter that
66
+ renames: its ecosystem calls a composable reactive function a **primitive** and reserves `use*` for
67
+ consuming something that already exists, so `useAccelerometer` is `createAccelerometer` there.
64
68
 
65
69
  ## Use it
66
70
 
@@ -72,7 +76,12 @@ import { useAccelerometer } from '@symbiote-native/sensors/react';
72
76
 
73
77
  function SensorsScreen() {
74
78
  const accelerometer = useAccelerometer();
75
- return <Text>{accelerometer && `x ${accelerometer.x} · y ${accelerometer.y} · z ${accelerometer.z}`}</Text>;
79
+ return (
80
+ <Text>
81
+ {accelerometer &&
82
+ `x ${accelerometer.x} · y ${accelerometer.y} · z ${accelerometer.z}`}
83
+ </Text>
84
+ );
76
85
  }
77
86
  ```
78
87
 
@@ -84,34 +93,65 @@ import { useAccelerometer } from '@symbiote-native/sensors/vue';
84
93
  const accelerometer = useAccelerometer();
85
94
  </script>
86
95
  <template>
87
- <Text>{{ accelerometer && `x ${accelerometer.x} · y ${accelerometer.y} · z ${accelerometer.z}` }}</Text>
96
+ <Text>{{
97
+ accelerometer &&
98
+ `x ${accelerometer.x} · y ${accelerometer.y} · z ${accelerometer.z}`
99
+ }}</Text>
88
100
  </template>
89
101
  ```
90
102
 
103
+ ```tsx
104
+ // Solid — a primitive returning an Accessor, read as `accelerometer()`. `Show` must be imported
105
+ // explicitly (see .claude/rules/solid-descriptor-bridge.md), and hands its child an accessor.
106
+ import { Show } from 'solid-js';
107
+ import { createAccelerometer } from '@symbiote-native/sensors/solid';
108
+
109
+ function SensorsScreen() {
110
+ const accelerometer = createAccelerometer();
111
+ return (
112
+ <Text>
113
+ <Show when={accelerometer()}>
114
+ {reading => `x ${reading().x} · y ${reading().y} · z ${reading().z}`}
115
+ </Show>
116
+ </Text>
117
+ );
118
+ }
119
+ ```
120
+
91
121
  ```ts
92
122
  // Angular
93
123
  import { Component, inject } from '@angular/core';
94
124
  import { AccelerometerService } from '@symbiote-native/sensors/angular';
95
125
 
96
- @Component({ /* ... */ })
126
+ @Component({/* ... */})
97
127
  export class SensorsScreen {
98
128
  readonly accelerometer = inject(AccelerometerService).connect();
99
129
  }
100
130
  ```
101
131
 
102
- All three examples above mirror the real canary demo screens —
132
+ The examples above mirror the real canary demo screens —
103
133
  `examples/expo-react/screens/SensorsScreen.tsx`, `examples/expo-vue-sfc/screens/SensorsScreen.vue`,
104
134
  `examples/expo-vue-tsx/screens/SensorsScreen.tsx`, `examples/expo-angular/src/screens/SensorsScreen.ts`.
105
135
 
106
- Every hook/composable/`connect()` takes an optional `updateIntervalMs` and returns `null` until the
107
- first native reading arrives — check `isAvailableAsync()` separately if you need to distinguish
108
- "not available on this device" from "no reading yet" (see [Notes](#notes) below).
136
+ Every hook/composable/rune/primitive/`connect()` takes an optional `updateIntervalMs` and returns
137
+ `null` until the first native reading arrives — check `isAvailableAsync()` separately if you need
138
+ to distinguish "not available on this device" from "no reading yet" (see [Notes](#notes) below).
139
+
140
+ On Solid `updateIntervalMs` accepts an **accessor** as well as a number
141
+ (`createAccelerometer(() => intervalMs())`). A Solid component body runs once, so a plain number is
142
+ read once and pins the sample rate for the primitive's whole life; pass the accessor whenever the
143
+ rate is derived from state and it re-applies on every change — without re-subscribing, since the
144
+ native listener is per-sensor and unaffected by the rate.
109
145
 
110
146
  ### Pedometer — free functions, no shared instance
111
147
 
112
148
  ```ts
113
- import { watchStepCount, getStepCountAsync, isAvailableAsync } from '@symbiote-native/sensors';
114
- import { usePedometer } from '@symbiote-native/sensors/react'; // or /vue, /angular's PedometerService
149
+ import {
150
+ watchStepCount,
151
+ getStepCountAsync,
152
+ isAvailableAsync,
153
+ } from '@symbiote-native/sensors';
154
+ import { usePedometer } from '@symbiote-native/sensors/react'; // or /vue, /svelte, /solid's createPedometer, /angular's PedometerService
115
155
 
116
156
  const pedometer = usePedometer(); // { steps: number } | null, live-subscribed
117
157
  const { steps } = await getStepCountAsync(startDate, endDate); // one-shot, iOS only in practice
@@ -134,5 +174,5 @@ const { steps } = await getStepCountAsync(startDate, endDate); // one-shot, iOS
134
174
 
135
175
  No Fabric/Descriptor angle at all — a sensor is a pure `EventEmitter` + async-function surface,
136
176
  never a view. Tests inject a fake native-module object in place of the real
137
- `requireNativeModule` resolution (`src/core/**/*.test.ts`, `src/{react,vue,angular}/**/*.test.{ts,tsx}`),
177
+ `requireNativeModule` resolution (`src/core/**/*.test.ts`, `src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`),
138
178
  the same pattern `expo-sensors` itself uses upstream — no `installFabric()`, no ViewConfig.
@@ -3,7 +3,7 @@ export type { IAccelerometerMeasurement } from '../core';
3
3
  export { BarometerService } from './services/barometer.service';
4
4
  export type { IBarometerMeasurement } from '../core';
5
5
  export { DeviceMotionService } from './services/device-motion.service';
6
- export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement } from '../core';
6
+ export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement, } from '../core';
7
7
  export { GyroscopeService } from './services/gyroscope.service';
8
8
  export type { IGyroscopeMeasurement } from '../core';
9
9
  export { LightSensorService } from './services/light-sensor.service';
@@ -3,7 +3,7 @@
3
3
  export { AccelerometerService } from './services/accelerometer.service/index.js';
4
4
  export { BarometerService } from './services/barometer.service/index.js';
5
5
  export { DeviceMotionService } from './services/device-motion.service/index.js';
6
- export { DeviceMotionOrientation, gravity } from '../core/index.js';
6
+ export { DeviceMotionOrientation, gravity, } from '../core/index.js';
7
7
  export { GyroscopeService } from './services/gyroscope.service/index.js';
8
8
  export { LightSensorService } from './services/light-sensor.service/index.js';
9
9
  export { MagnetometerService } from './services/magnetometer.service/index.js';
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { Accelerometer } from '../../../core/index.js';
37
37
  // Angular twin of React's `useAccelerometer` hook / Vue's `useAccelerometer` composable.
38
38
  // Angular has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { Barometer } from '../../../core/index.js';
37
37
  // Angular twin of React's `useBarometer` hook / Vue's `useBarometer` composable. Angular has no
38
38
  // per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { DeviceMotion } from '../../../core/index.js';
37
37
  // Angular twin of React's `useDeviceMotion` hook / Vue's `useDeviceMotion` composable. Angular
38
38
  // has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { Gyroscope } from '../../../core/index.js';
37
37
  // Angular twin of React's `useGyroscope` hook / Vue's `useGyroscope` composable. Angular has no
38
38
  // per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { LightSensor } from '../../../core/index.js';
37
37
  // Angular twin of React's `useLightSensor` hook / Vue's `useLightSensor` composable. Angular
38
38
  // has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
@@ -32,8 +32,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
- import { MagnetometerUncalibrated } from '../../../core/index.js';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
+ import { MagnetometerUncalibrated, } from '../../../core/index.js';
37
37
  // Angular twin of React's `useMagnetometerUncalibrated` hook / Vue's
38
38
  // `useMagnetometerUncalibrated` composable. Angular has no per-instance hook, so `connect()`
39
39
  // stands in: call it ONCE, inside an injection context (typically a component's field
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { Magnetometer } from '../../../core/index.js';
37
37
  // Angular twin of React's `useMagnetometer` hook / Vue's `useMagnetometer` composable. Angular
38
38
  // has no per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
@@ -32,7 +32,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
36
  import { watchStepCount } from '../../../core/index.js';
37
37
  // Angular twin of React's `usePedometer` hook / Vue's `usePedometer` composable. Angular has no
38
38
  // per-instance hook, so `connect()` stands in: call it ONCE, inside an injection context
@@ -1,11 +1,11 @@
1
1
  export { Accelerometer, AccelerometerSensor, type IAccelerometerMeasurement, } from './accelerometer';
2
- export { Barometer, BarometerSensor, type IBarometerMeasurement } from './barometer';
2
+ export { Barometer, BarometerSensor, type IBarometerMeasurement, } from './barometer';
3
3
  export { DeviceMotion, DeviceMotionSensor, DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement, } from './device-motion';
4
- export { Gyroscope, GyroscopeSensor, type IGyroscopeMeasurement } from './gyroscope';
5
- export { LightSensor, LightSensorSensor, type ILightSensorMeasurement } from './light-sensor';
6
- export { Magnetometer, MagnetometerSensor, type IMagnetometerMeasurement } from './magnetometer';
4
+ export { Gyroscope, GyroscopeSensor, type IGyroscopeMeasurement, } from './gyroscope';
5
+ export { LightSensor, LightSensorSensor, type ILightSensorMeasurement, } from './light-sensor';
6
+ export { Magnetometer, MagnetometerSensor, type IMagnetometerMeasurement, } from './magnetometer';
7
7
  export { MagnetometerUncalibrated, MagnetometerUncalibratedSensor, type IMagnetometerUncalibratedMeasurement, } from './magnetometer-uncalibrated';
8
8
  export { watchStepCount, getStepCountAsync, isAvailableAsync, getPermissionsAsync, requestPermissionsAsync, type IPedometerResult, type IPedometerUpdateCallback, } from './pedometer';
9
- export { DeviceSensor, type IListener, type INativeSensorModule } from './device-sensor';
9
+ export { DeviceSensor, type IListener, type INativeSensorModule, } from './device-sensor';
10
10
  export type { EventSubscription, PermissionExpiration, PermissionResponse, } from 'expo-modules-core';
11
11
  export { PermissionStatus } from 'expo-modules-core';
@@ -1,10 +1,10 @@
1
1
  export { Accelerometer, AccelerometerSensor, } from './accelerometer.js';
2
- export { Barometer, BarometerSensor } from './barometer.js';
2
+ export { Barometer, BarometerSensor, } from './barometer.js';
3
3
  export { DeviceMotion, DeviceMotionSensor, DeviceMotionOrientation, gravity, } from './device-motion.js';
4
- export { Gyroscope, GyroscopeSensor } from './gyroscope.js';
5
- export { LightSensor, LightSensorSensor } from './light-sensor.js';
6
- export { Magnetometer, MagnetometerSensor } from './magnetometer.js';
4
+ export { Gyroscope, GyroscopeSensor, } from './gyroscope.js';
5
+ export { LightSensor, LightSensorSensor, } from './light-sensor.js';
6
+ export { Magnetometer, MagnetometerSensor, } from './magnetometer.js';
7
7
  export { MagnetometerUncalibrated, MagnetometerUncalibratedSensor, } from './magnetometer-uncalibrated.js';
8
8
  export { watchStepCount, getStepCountAsync, isAvailableAsync, getPermissionsAsync, requestPermissionsAsync, } from './pedometer.js';
9
- export { DeviceSensor } from './device-sensor.js';
9
+ export { DeviceSensor, } from './device-sensor.js';
10
10
  export { PermissionStatus } from 'expo-modules-core';
@@ -1,7 +1,7 @@
1
1
  // React lifecycle wiring over the framework-agnostic MagnetometerUncalibrated singleton
2
2
  // (core/) — mirrors the lifecycle-bucket naming convention of adapters/react/src/hooks.
3
3
  import { useEffect, useState } from 'react';
4
- import { MagnetometerUncalibrated } from '../../../core/index.js';
4
+ import { MagnetometerUncalibrated, } from '../../../core/index.js';
5
5
  export function useMagnetometerUncalibrated(updateIntervalMs) {
6
6
  const [measurement, setMeasurement] = useState(null);
7
7
  useEffect(() => {
@@ -3,7 +3,7 @@ export type { IAccelerometerMeasurement } from '../core';
3
3
  export { useBarometer } from './hooks/use-barometer';
4
4
  export type { IBarometerMeasurement } from '../core';
5
5
  export { useDeviceMotion } from './hooks/use-device-motion';
6
- export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement } from '../core';
6
+ export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement, } from '../core';
7
7
  export { useGyroscope } from './hooks/use-gyroscope';
8
8
  export type { IGyroscopeMeasurement } from '../core';
9
9
  export { useLightSensor } from './hooks/use-light-sensor';
@@ -5,7 +5,7 @@
5
5
  export { useAccelerometer } from './hooks/use-accelerometer/index.js';
6
6
  export { useBarometer } from './hooks/use-barometer/index.js';
7
7
  export { useDeviceMotion } from './hooks/use-device-motion/index.js';
8
- export { DeviceMotionOrientation, gravity } from '../core/index.js';
8
+ export { DeviceMotionOrientation, gravity, } from '../core/index.js';
9
9
  export { useGyroscope } from './hooks/use-gyroscope/index.js';
10
10
  export { useLightSensor } from './hooks/use-light-sensor/index.js';
11
11
  export { useMagnetometer } from './hooks/use-magnetometer/index.js';
@@ -0,0 +1,10 @@
1
+ export { createAccelerometer } from './primitives/create-accelerometer';
2
+ export { createBarometer } from './primitives/create-barometer';
3
+ export { createDeviceMotion } from './primitives/create-device-motion';
4
+ export { createGyroscope } from './primitives/create-gyroscope';
5
+ export { createLightSensor } from './primitives/create-light-sensor';
6
+ export { createMagnetometer } from './primitives/create-magnetometer';
7
+ export { createMagnetometerUncalibrated } from './primitives/create-magnetometer-uncalibrated';
8
+ export { createPedometer } from './primitives/create-pedometer';
9
+ export type { IUpdateIntervalMs } from './primitives/device-sensor-accessor';
10
+ export * from '../core';
@@ -0,0 +1,17 @@
1
+ // @symbiote-native/sensors/solid: the Solid entry over the framework-agnostic core.
2
+ // createAccelerometer wires the Accelerometer singleton's addListener lifecycle onto Solid's own
3
+ // signal + onCleanup (primitives/create-accelerometer) — `primitives/` and `create*` are Solid's
4
+ // terms for a composable reactive function, never `hooks/`/`composables/`/`runes/`, which are
5
+ // React's, Vue's and Svelte's. Same convention as adapters/solid/src/primitives.
6
+ //
7
+ // Every primitive returns an `Accessor`, and each DeviceSensor-shaped one takes its update
8
+ // interval as a number OR an accessor — see primitives/device-sensor-accessor for why.
9
+ export { createAccelerometer } from './primitives/create-accelerometer.js';
10
+ export { createBarometer } from './primitives/create-barometer.js';
11
+ export { createDeviceMotion } from './primitives/create-device-motion.js';
12
+ export { createGyroscope } from './primitives/create-gyroscope.js';
13
+ export { createLightSensor } from './primitives/create-light-sensor.js';
14
+ export { createMagnetometer } from './primitives/create-magnetometer.js';
15
+ export { createMagnetometerUncalibrated } from './primitives/create-magnetometer-uncalibrated.js';
16
+ export { createPedometer } from './primitives/create-pedometer.js';
17
+ export * from '../core/index.js';
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type IAccelerometerMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createAccelerometer(updateIntervalMs?: IUpdateIntervalMs): Accessor<IAccelerometerMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { Accelerometer } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createAccelerometer(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(Accelerometer, updateIntervalMs);
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type IBarometerMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createBarometer(updateIntervalMs?: IUpdateIntervalMs): Accessor<IBarometerMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { Barometer } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createBarometer(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(Barometer, updateIntervalMs);
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type IDeviceMotionMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createDeviceMotion(updateIntervalMs?: IUpdateIntervalMs): Accessor<IDeviceMotionMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { DeviceMotion } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createDeviceMotion(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(DeviceMotion, updateIntervalMs);
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type IGyroscopeMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createGyroscope(updateIntervalMs?: IUpdateIntervalMs): Accessor<IGyroscopeMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { Gyroscope } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createGyroscope(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(Gyroscope, updateIntervalMs);
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type ILightSensorMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createLightSensor(updateIntervalMs?: IUpdateIntervalMs): Accessor<ILightSensorMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { LightSensor } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createLightSensor(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(LightSensor, updateIntervalMs);
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type IMagnetometerUncalibratedMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createMagnetometerUncalibrated(updateIntervalMs?: IUpdateIntervalMs): Accessor<IMagnetometerUncalibratedMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { MagnetometerUncalibrated, } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createMagnetometerUncalibrated(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(MagnetometerUncalibrated, updateIntervalMs);
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
2
+ import { type IMagnetometerMeasurement } from '../../core';
3
+ import { type IUpdateIntervalMs } from './device-sensor-accessor';
4
+ export declare function createMagnetometer(updateIntervalMs?: IUpdateIntervalMs): Accessor<IMagnetometerMeasurement | null>;
@@ -0,0 +1,5 @@
1
+ import { Magnetometer } from '../../core/index.js';
2
+ import { createDeviceSensorAccessor, } from './device-sensor-accessor.js';
3
+ export function createMagnetometer(updateIntervalMs) {
4
+ return createDeviceSensorAccessor(Magnetometer, updateIntervalMs);
5
+ }
@@ -0,0 +1,3 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import { type IPedometerResult } from '../../core';
3
+ export declare function createPedometer(): Accessor<IPedometerResult | null>;
@@ -0,0 +1,22 @@
1
+ // Solid lifecycle wiring over the framework-agnostic watchStepCount free function (core/) — the
2
+ // Solid twin of react/hooks/use-pedometer, vue/composables/use-pedometer and
3
+ // svelte/runes/use-pedometer.svelte.ts. Pedometer is the one sensor here that is not a
4
+ // DeviceSensor subclass (core/pedometer.ts's header), so it has no setUpdateInterval and takes no
5
+ // interval argument — hence its own body instead of ./device-sensor-accessor.
6
+ //
7
+ // `primitives/` and `create*`, and an ACCESSOR rather than a snapshot, for the reasons recorded in
8
+ // adapters/solid/src/primitives/create-color-scheme.ts.
9
+ import { createSignal, onCleanup } from 'solid-js';
10
+ import { watchStepCount, } from '../../core/index.js';
11
+ export function createPedometer() {
12
+ const [result, setResult] = createSignal(null);
13
+ // Subscribed from the primitive body, not from an effect: React/Vue/Svelte start listening a
14
+ // tick after their seed runs, so a step count landing in that window is lost.
15
+ const subscription = watchStepCount(next => {
16
+ setResult(next);
17
+ });
18
+ onCleanup(() => {
19
+ subscription.remove();
20
+ });
21
+ return result;
22
+ }
@@ -0,0 +1,9 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import type { EventSubscription, IListener } from '../../core';
3
+ export type IUpdateIntervalMs = number | Accessor<number | undefined> | undefined;
4
+ type ISensorHandle<Measurement> = {
5
+ addListener(listener: IListener<Measurement>): EventSubscription;
6
+ setUpdateInterval(intervalMs: number): void;
7
+ };
8
+ export declare function createDeviceSensorAccessor<Measurement>(sensor: ISensorHandle<Measurement>, updateIntervalMs?: IUpdateIntervalMs): Accessor<Measurement | null>;
9
+ export {};
@@ -0,0 +1,66 @@
1
+ // The one lifecycle body every DeviceSensor-shaped primitive in this folder delegates to
2
+ // (Accelerometer, Barometer, DeviceMotion, Gyroscope, LightSensor, Magnetometer,
3
+ // MagnetometerUncalibrated). Internal — not re-exported from ../index.ts, whose surface stays at
4
+ // parity with the react/vue/svelte entries.
5
+ //
6
+ // `primitives/` and `create*`, never `hooks/` + `use*`: Solid's ecosystem calls a composable
7
+ // reactive function a PRIMITIVE and reserves `use*` for consuming something that already exists.
8
+ // Full rationale in adapters/solid/src/primitives/create-color-scheme.ts.
9
+ //
10
+ // Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned
11
+ // measurement would pin the caller to `null` forever.
12
+ //
13
+ // ## Why the interval is `number | Accessor<number | undefined>`
14
+ //
15
+ // React re-reads `updateIntervalMs` on every render and re-runs its effect when it changes; Vue
16
+ // and Svelte read it once at subscribe time. Neither shape ports: a Solid body runs once, so a
17
+ // plain parameter freezes the sample rate at whatever the caller passed at mount, and nothing
18
+ // reports it — the caller's `intervalMs` state moves and the native rate does not. So the
19
+ // parameter accepts EITHER form (solid-primitives' `MaybeAccessor` convention): a literal for the
20
+ // common static case, keeping the call site identical to the other adapters, or an accessor when
21
+ // the rate is derived from state. The accessor form is the reactive one and is the only one that
22
+ // creates an effect.
23
+ //
24
+ // Applying it splits in two on purpose. The mount-time value is applied SYNCHRONOUSLY, before
25
+ // `addListener`, so the very first native sample already arrives at the requested rate; the
26
+ // tracking effect is then `defer`red so it fires only on a LATER change. Without the defer the
27
+ // interval would be set twice at mount, and the second call would land a tick after the first
28
+ // samples.
29
+ //
30
+ // Outside a component or `createRoot` there is no owner for `onCleanup` — Solid warns and the
31
+ // native listener lives for the process. The accessor still tracks; only the teardown is lost.
32
+ import { createEffect, createSignal, on, onCleanup, untrack, } from 'solid-js';
33
+ export function createDeviceSensorAccessor(sensor, updateIntervalMs) {
34
+ const [measurement, setMeasurement] = createSignal(null);
35
+ function applyInterval(intervalMs) {
36
+ // `!== undefined`, not truthiness: 0 is a legitimate "as fast as possible" rate, and an
37
+ // unconditional call would stomp the rate another consumer of this shared singleton set.
38
+ if (intervalMs !== undefined) {
39
+ sensor.setUpdateInterval(intervalMs);
40
+ }
41
+ }
42
+ // untracked: a primitive built inside a tracked scope must not re-run — and re-subscribe —
43
+ // just because it read the caller's interval accessor once.
44
+ applyInterval(untrack(() => typeof updateIntervalMs === 'function'
45
+ ? updateIntervalMs()
46
+ : updateIntervalMs));
47
+ // Subscribed from the primitive body, not from an effect: React/Vue/Svelte start listening a
48
+ // tick after their seed runs, so an event landing in that window is lost. Both statements here
49
+ // run in one synchronous tick and nothing can interleave.
50
+ const subscription = sensor.addListener(next => {
51
+ // The updater form, because `Measurement` is unconstrained — a bare value would be read as a
52
+ // reducer for any measurement type that could itself be callable.
53
+ setMeasurement(() => next);
54
+ });
55
+ onCleanup(() => {
56
+ subscription.remove();
57
+ });
58
+ if (typeof updateIntervalMs === 'function') {
59
+ // Only the rate changes — the native listener is per-sensor and unaffected, so re-subscribing
60
+ // (as React's interval-keyed effect does) would drop events for nothing.
61
+ createEffect(on(updateIntervalMs, intervalMs => {
62
+ applyInterval(intervalMs);
63
+ }, { defer: true }));
64
+ }
65
+ return measurement;
66
+ }
@@ -3,7 +3,7 @@ export type { IAccelerometerMeasurement } from '../core';
3
3
  export { useBarometer } from './runes/use-barometer.svelte';
4
4
  export type { IBarometerMeasurement } from '../core';
5
5
  export { useDeviceMotion } from './runes/use-device-motion.svelte';
6
- export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement } from '../core';
6
+ export { DeviceMotionOrientation, gravity, type IDeviceMotionMeasurement, } from '../core';
7
7
  export { useGyroscope } from './runes/use-gyroscope.svelte';
8
8
  export type { IGyroscopeMeasurement } from '../core';
9
9
  export { useLightSensor } from './runes/use-light-sensor.svelte';
@@ -5,7 +5,7 @@
5
5
  export { useAccelerometer } from './runes/use-accelerometer.svelte';
6
6
  export { useBarometer } from './runes/use-barometer.svelte';
7
7
  export { useDeviceMotion } from './runes/use-device-motion.svelte';
8
- export { DeviceMotionOrientation, gravity } from '../core/index.js';
8
+ export { DeviceMotionOrientation, gravity, } from '../core/index.js';
9
9
  export { useGyroscope } from './runes/use-gyroscope.svelte';
10
10
  export { useLightSensor } from './runes/use-light-sensor.svelte';
11
11
  export { useMagnetometer } from './runes/use-magnetometer.svelte';
@@ -9,7 +9,7 @@
9
9
  //
10
10
  // `updateIntervalMs` is a plain number, not a getter: it applies once at subscribe time and
11
11
  // never reacts to a later change, matching the Vue/React twins.
12
- import { Accelerometer } from '../../core/index.js';
12
+ import { Accelerometer, } from '../../core/index.js';
13
13
  export function useAccelerometer(updateIntervalMs) {
14
14
  let measurement = $state(null);
15
15
  $effect(() => {
@@ -2,7 +2,7 @@
2
2
  // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
3
  // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
4
  // rather than a getter.
5
- import { Barometer } from '../../core/index.js';
5
+ import { Barometer, } from '../../core/index.js';
6
6
  export function useBarometer(updateIntervalMs) {
7
7
  let measurement = $state(null);
8
8
  $effect(() => {
@@ -2,7 +2,7 @@
2
2
  // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
3
  // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
4
  // rather than a getter.
5
- import { DeviceMotion } from '../../core/index.js';
5
+ import { DeviceMotion, } from '../../core/index.js';
6
6
  export function useDeviceMotion(updateIntervalMs) {
7
7
  let measurement = $state(null);
8
8
  $effect(() => {
@@ -2,7 +2,7 @@
2
2
  // use-accelerometer.svelte.ts's shape exactly. See its header for why this lives in `runes/` with
3
3
  // a `.svelte.ts` extension, returns a boxed getter object, and takes a plain `updateIntervalMs`
4
4
  // rather than a getter.
5
- import { Gyroscope } from '../../core/index.js';
5
+ import { Gyroscope, } from '../../core/index.js';
6
6
  export function useGyroscope(updateIntervalMs) {
7
7
  let measurement = $state(null);
8
8
  $effect(() => {