yaver-feedback-react-native 0.9.3 → 0.9.4

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.
@@ -97,6 +97,13 @@ class ShakeDetector {
97
97
  * want the extra native surface get the Dev-menu path only.
98
98
  */
99
99
  subscribeAccelerometer(onShake) {
100
+ // expo-sensors exposes an Accelerometer facade on web, but browsers have
101
+ // no native shake gesture and some versions construct a NativeEventEmitter
102
+ // backed by a module without addListener/removeListeners. Subscribing then
103
+ // crashes the host app's React commit phase. Preserve the SDK's explicit
104
+ // controls on web and skip only this impossible sensor transport.
105
+ if (react_native_1.Platform.OS === 'web')
106
+ return;
100
107
  let Accelerometer = null;
101
108
  try {
102
109
  // Optional peer dep — if it isn't installed, we simply skip this path.
@@ -113,23 +120,30 @@ class ShakeDetector {
113
120
  catch {
114
121
  // Some platforms reject zero-value intervals; fall through with defaults
115
122
  }
116
- this.accelSub = Accelerometer.addListener(({ x, y, z }) => {
117
- // Magnitude of acceleration vector (in g). Subtract 1 so a stationary
118
- // device reports ~0 rather than the 1g of gravity.
119
- const mag = Math.sqrt(x * x + y * y + z * z);
120
- if (mag - 1 < ACCEL_THRESHOLD_G - 1)
121
- return;
122
- const now = Date.now();
123
- this.peakTimestamps.push(now);
124
- // Drop peaks that fell out of the rolling window.
125
- while (this.peakTimestamps.length > 0 &&
126
- now - this.peakTimestamps[0] > ACCEL_WINDOW_MS) {
127
- this.peakTimestamps.shift();
128
- }
129
- if (this.peakTimestamps.length >= ACCEL_MIN_HITS) {
130
- this.fire(onShake);
131
- }
132
- });
123
+ try {
124
+ this.accelSub = Accelerometer.addListener(({ x, y, z }) => {
125
+ // Magnitude of acceleration vector (in g). Subtract 1 so a stationary
126
+ // device reports ~0 rather than the 1g of gravity.
127
+ const mag = Math.sqrt(x * x + y * y + z * z);
128
+ if (mag - 1 < ACCEL_THRESHOLD_G - 1)
129
+ return;
130
+ const now = Date.now();
131
+ this.peakTimestamps.push(now);
132
+ // Drop peaks that fell out of the rolling window.
133
+ while (this.peakTimestamps.length > 0 &&
134
+ now - this.peakTimestamps[0] > ACCEL_WINDOW_MS) {
135
+ this.peakTimestamps.shift();
136
+ }
137
+ if (this.peakTimestamps.length >= ACCEL_MIN_HITS) {
138
+ this.fire(onShake);
139
+ }
140
+ });
141
+ }
142
+ catch {
143
+ // A host may include expo-sensors without linking its native module.
144
+ // Optional shake support must never take down the host application.
145
+ this.accelSub = null;
146
+ }
133
147
  }
134
148
  }
135
149
  exports.ShakeDetector = ShakeDetector;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ // Browsers cannot provide a native shake gesture. expo-sensors still exports
3
+ // an Accelerometer facade there, and older SDK builds attempted to subscribe
4
+ // to it, crashing the host React tree through NativeEventEmitter.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const sensorAddListener = jest.fn(() => ({ remove: jest.fn() }));
7
+ const devEventAddListener = jest.fn(() => ({ remove: jest.fn() }));
8
+ jest.mock('react-native', () => ({
9
+ Platform: { OS: 'web' },
10
+ NativeModules: {},
11
+ DeviceEventEmitter: { addListener: devEventAddListener },
12
+ }));
13
+ jest.mock('expo-sensors', () => ({
14
+ Accelerometer: {
15
+ setUpdateInterval: jest.fn(),
16
+ addListener: sensorAddListener,
17
+ },
18
+ }), { virtual: true });
19
+ const ShakeDetector_1 = require("../ShakeDetector");
20
+ it('keeps explicit SDK controls alive without touching the native sensor on web', () => {
21
+ const detector = new ShakeDetector_1.ShakeDetector();
22
+ expect(() => detector.start(jest.fn())).not.toThrow();
23
+ expect(sensorAddListener).not.toHaveBeenCalled();
24
+ // The ordinary DeviceEventEmitter lane remains harmless and removable.
25
+ expect(devEventAddListener).toHaveBeenCalledTimes(1);
26
+ expect(() => detector.stop()).not.toThrow();
27
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaver-feedback-react-native",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Visual feedback SDK for Yaver — bug reports, screen recording, voice vibe coding, and local-first developer workflows",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -96,6 +96,13 @@ export class ShakeDetector {
96
96
  * want the extra native surface get the Dev-menu path only.
97
97
  */
98
98
  private subscribeAccelerometer(onShake: () => void): void {
99
+ // expo-sensors exposes an Accelerometer facade on web, but browsers have
100
+ // no native shake gesture and some versions construct a NativeEventEmitter
101
+ // backed by a module without addListener/removeListeners. Subscribing then
102
+ // crashes the host app's React commit phase. Preserve the SDK's explicit
103
+ // controls on web and skip only this impossible sensor transport.
104
+ if (Platform.OS === 'web') return;
105
+
99
106
  let Accelerometer: {
100
107
  setUpdateInterval: (ms: number) => void;
101
108
  addListener: (cb: (d: { x: number; y: number; z: number }) => void) => {
@@ -116,24 +123,30 @@ export class ShakeDetector {
116
123
  // Some platforms reject zero-value intervals; fall through with defaults
117
124
  }
118
125
 
119
- this.accelSub = Accelerometer.addListener(({ x, y, z }) => {
120
- // Magnitude of acceleration vector (in g). Subtract 1 so a stationary
121
- // device reports ~0 rather than the 1g of gravity.
122
- const mag = Math.sqrt(x * x + y * y + z * z);
123
- if (mag - 1 < ACCEL_THRESHOLD_G - 1) return;
126
+ try {
127
+ this.accelSub = Accelerometer.addListener(({ x, y, z }) => {
128
+ // Magnitude of acceleration vector (in g). Subtract 1 so a stationary
129
+ // device reports ~0 rather than the 1g of gravity.
130
+ const mag = Math.sqrt(x * x + y * y + z * z);
131
+ if (mag - 1 < ACCEL_THRESHOLD_G - 1) return;
124
132
 
125
- const now = Date.now();
126
- this.peakTimestamps.push(now);
127
- // Drop peaks that fell out of the rolling window.
128
- while (
129
- this.peakTimestamps.length > 0 &&
130
- now - this.peakTimestamps[0] > ACCEL_WINDOW_MS
131
- ) {
132
- this.peakTimestamps.shift();
133
- }
134
- if (this.peakTimestamps.length >= ACCEL_MIN_HITS) {
135
- this.fire(onShake);
136
- }
137
- });
133
+ const now = Date.now();
134
+ this.peakTimestamps.push(now);
135
+ // Drop peaks that fell out of the rolling window.
136
+ while (
137
+ this.peakTimestamps.length > 0 &&
138
+ now - this.peakTimestamps[0] > ACCEL_WINDOW_MS
139
+ ) {
140
+ this.peakTimestamps.shift();
141
+ }
142
+ if (this.peakTimestamps.length >= ACCEL_MIN_HITS) {
143
+ this.fire(onShake);
144
+ }
145
+ });
146
+ } catch {
147
+ // A host may include expo-sensors without linking its native module.
148
+ // Optional shake support must never take down the host application.
149
+ this.accelSub = null;
150
+ }
138
151
  }
139
152
  }
@@ -0,0 +1,30 @@
1
+ // Browsers cannot provide a native shake gesture. expo-sensors still exports
2
+ // an Accelerometer facade there, and older SDK builds attempted to subscribe
3
+ // to it, crashing the host React tree through NativeEventEmitter.
4
+
5
+ const sensorAddListener = jest.fn(() => ({ remove: jest.fn() }));
6
+ const devEventAddListener = jest.fn(() => ({ remove: jest.fn() }));
7
+
8
+ jest.mock('react-native', () => ({
9
+ Platform: { OS: 'web' },
10
+ NativeModules: {},
11
+ DeviceEventEmitter: { addListener: devEventAddListener },
12
+ }));
13
+
14
+ jest.mock('expo-sensors', () => ({
15
+ Accelerometer: {
16
+ setUpdateInterval: jest.fn(),
17
+ addListener: sensorAddListener,
18
+ },
19
+ }), { virtual: true });
20
+
21
+ import { ShakeDetector } from '../ShakeDetector';
22
+
23
+ it('keeps explicit SDK controls alive without touching the native sensor on web', () => {
24
+ const detector = new ShakeDetector();
25
+ expect(() => detector.start(jest.fn())).not.toThrow();
26
+ expect(sensorAddListener).not.toHaveBeenCalled();
27
+ // The ordinary DeviceEventEmitter lane remains harmless and removable.
28
+ expect(devEventAddListener).toHaveBeenCalledTimes(1);
29
+ expect(() => detector.stop()).not.toThrow();
30
+ });