@flighthq/haptics 0.3.1-next.975.01aea94 → 0.4.0-edge.1908.751021c

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/dist/haptics.d.ts CHANGED
@@ -1,10 +1,15 @@
1
+ import type { BackendExplanation } from '@flighthq/types/contract';
1
2
  import type { HapticImpactStyle, HapticNotificationType, HapticsBackend, HapticsCapabilities } from '@flighthq/types/contract';
2
3
  export declare function cancelDeviceVibration(): boolean;
3
4
  export declare function createWebHapticsBackend(): HapticsBackend;
5
+ export declare function explainHapticsBackend(): BackendExplanation;
4
6
  export declare function getHapticsBackend(): HapticsBackend;
5
7
  export declare function getHapticsCapabilities(out: HapticsCapabilities): HapticsCapabilities;
8
+ export declare function installHapticsHostBackend(backend: HapticsBackend): void;
6
9
  export declare function isHapticsSupported(): boolean;
10
+ export declare function observeHapticsHostResult(operation: string, succeeded: boolean): void;
7
11
  export declare function prepareHaptics(): void;
12
+ export declare function resetHapticsBackendForTest(): void;
8
13
  export declare function setHapticsBackend(backend: HapticsBackend | null): void;
9
14
  export declare function triggerHapticImpact(style: HapticImpactStyle, intensity?: number): boolean;
10
15
  export declare function triggerHapticNotification(type: HapticNotificationType): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"haptics.d.ts","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAGlC,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAKD,wBAAgB,uBAAuB,IAAI,cAAc,CA8CxD;AAGD,wBAAgB,iBAAiB,IAAI,cAAc,CAGlD;AAGD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,mBAAmB,GAAG,mBAAmB,CAEpF;AAGD,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAID,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAGD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAEtE;AAID,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAIzF;AAID,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAE/E;AAGD,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAGD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEzD;AAID,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAGzE;AAOD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAC3B,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAC9B,MAAM,SAAK,GACV,OAAO,CAOT"}
1
+ {"version":3,"file":"haptics.d.ts","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAKD,wBAAgB,uBAAuB,IAAI,cAAc,CA2CxD;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,CAa1D;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CAElD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,mBAAmB,GAAG,mBAAmB,CAEpF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAMvE;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAKpF;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAKjD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzF;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAE/E;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAGzE;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAC3B,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,EAC9B,MAAM,SAAK,GACV,OAAO,CAOT"}
package/dist/haptics.js CHANGED
@@ -1,4 +1,3 @@
1
- // Cancels any in-progress device vibration. Returns false when haptics are unavailable.
2
1
  export function cancelDeviceVibration() {
3
2
  return getHapticsBackend().cancel();
4
3
  }
@@ -8,7 +7,7 @@ export function cancelDeviceVibration() {
8
7
  export function createWebHapticsBackend() {
9
8
  return {
10
9
  cancel() {
11
- return webVibrate(0);
10
+ return _webVibrate(0, 'cancel');
12
11
  },
13
12
  capabilities(out) {
14
13
  const supported = typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function';
@@ -22,93 +21,101 @@ export function createWebHapticsBackend() {
22
21
  impact(style, intensity) {
23
22
  const base = style === 'heavy' || style === 'rigid' ? 30 : style === 'medium' ? 20 : style === 'soft' ? 25 : 10;
24
23
  const ms = intensity !== undefined ? Math.round(base * Math.max(0, Math.min(1, intensity))) : base;
25
- return webVibrate(ms);
24
+ return _webVibrate(ms, 'impact');
26
25
  },
27
26
  isSupported() {
28
27
  return typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function';
29
28
  },
30
29
  notification(type) {
31
30
  const pattern = type === 'error' ? [20, 60, 20] : type === 'warning' ? [20, 60, 20, 60] : [15, 50, 15];
32
- return webVibrate(pattern);
33
- },
34
- prepare() {
35
- // No-op on web; native backends may pre-allocate feedback generators to reduce latency.
31
+ return _webVibrate(pattern, 'notification');
36
32
  },
33
+ prepare() { },
37
34
  selection() {
38
- return webVibrate(5);
35
+ return _webVibrate(5, 'selection');
39
36
  },
40
37
  vibrate(durationMs) {
41
- return webVibrate(durationMs);
38
+ return _webVibrate(durationMs, 'vibrate');
42
39
  },
43
40
  vibratePattern(pattern) {
44
41
  if (pattern.length === 0)
45
42
  return false;
46
- return webVibrate(pattern);
43
+ return _webVibrate(pattern, 'vibratePattern');
47
44
  },
48
45
  vibrateWaveform(timings, _amplitudes, repeat) {
49
- // Web backend ignores amplitudes; falls back to vibratePattern. repeat is unsupported on web.
50
46
  if (timings.length === 0)
51
47
  return false;
52
48
  void repeat;
53
- return webVibrate(timings);
49
+ return _webVibrate(timings, 'vibrateWaveform');
54
50
  },
55
51
  };
56
52
  }
57
- // The active haptics backend, or a lazily-created web default. There is always a backend.
53
+ export function explainHapticsBackend() {
54
+ if (_custom !== null) {
55
+ return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
56
+ }
57
+ if (_host !== null) {
58
+ return {
59
+ conflict: _hostConflict,
60
+ layer: 'host',
61
+ operation: _hostObservation !== null ? _hostObservation.operation : null,
62
+ viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
63
+ };
64
+ }
65
+ return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
66
+ }
58
67
  export function getHapticsBackend() {
59
- if (_backend === null)
60
- _backend = createWebHapticsBackend();
61
- return _backend;
68
+ return _custom ?? _host ?? _sentinel;
62
69
  }
63
- // Fills out with the capabilities of the active HapticsBackend. Returns the same out object.
64
70
  export function getHapticsCapabilities(out) {
65
71
  return getHapticsBackend().capabilities(out);
66
72
  }
67
- // Returns true if the active backend reports that haptics are available on the current device.
73
+ export function installHapticsHostBackend(backend) {
74
+ if (_host !== null) {
75
+ if (_host !== backend)
76
+ _hostConflict = true;
77
+ return;
78
+ }
79
+ _host = backend;
80
+ }
68
81
  export function isHapticsSupported() {
69
82
  return getHapticsBackend().isSupported();
70
83
  }
71
- // Warm-up hint to reduce first-trigger latency (mirrors UIFeedbackGenerator.prepare). No-op on
72
- // backends that do not support pre-allocation. Always safe to call.
84
+ export function observeHapticsHostResult(operation, succeeded) {
85
+ _hostObservation = {
86
+ operation,
87
+ viability: succeeded ? 'available' : 'runtime-api-unavailable',
88
+ };
89
+ }
73
90
  export function prepareHaptics() {
74
91
  getHapticsBackend().prepare?.();
75
92
  }
76
- // Installs a native host haptics backend; pass null to fall back to the web default.
93
+ export function resetHapticsBackendForTest() {
94
+ _custom = null;
95
+ _host = null;
96
+ _hostConflict = false;
97
+ _hostObservation = null;
98
+ }
77
99
  export function setHapticsBackend(backend) {
78
- _backend = backend;
100
+ _custom = backend;
79
101
  }
80
- // Triggers a physical impact at the given style, with optional continuous intensity (0..1).
81
- // Intensity defaults to 1 when omitted. Returns false when haptics are unavailable.
82
102
  export function triggerHapticImpact(style, intensity) {
83
- // Resolve the documented default here so every backend receives a concrete intensity; a native
84
- // backend must not have to reinterpret `undefined` as full strength on its own.
85
103
  return getHapticsBackend().impact(style, intensity ?? 1);
86
104
  }
87
- // Triggers a semantic notification cue ('success' | 'warning' | 'error').
88
- // Returns false when haptics are unavailable.
89
105
  export function triggerHapticNotification(type) {
90
106
  return getHapticsBackend().notification(type);
91
107
  }
92
- // Triggers a light selection tick. Returns false when haptics are unavailable.
93
108
  export function triggerHapticSelection() {
94
109
  return getHapticsBackend().selection();
95
110
  }
96
- // Vibrates the device for the given duration in milliseconds. Returns false when unavailable or denied.
97
111
  export function vibrateDevice(durationMs) {
98
112
  return getHapticsBackend().vibrate(durationMs);
99
113
  }
100
- // Vibrates the device using a pattern array [onMs, offMs, onMs, ...] matching the Web Vibration API.
101
- // Returns false on empty pattern or when haptics are unavailable.
102
114
  export function vibrateDevicePattern(pattern) {
103
115
  if (pattern.length === 0)
104
116
  return false;
105
117
  return getHapticsBackend().vibratePattern(pattern);
106
118
  }
107
- // Vibrates using an amplitude-aware waveform (maps to Android VibrationEffect.createWaveform).
108
- // timings and amplitudes must have equal length; amplitudes are 0..255 (per Android convention).
109
- // repeat is the index into timings at which to loop, or -1 (default) for no repeat.
110
- // Backends that lack native waveform support fall back to vibratePattern(timings).
111
- // Returns false on empty timings or when haptics are unavailable.
112
119
  export function vibrateDeviceWaveform(timings, amplitudes, repeat = -1) {
113
120
  const backend = getHapticsBackend();
114
121
  if (timings.length === 0)
@@ -118,15 +125,54 @@ export function vibrateDeviceWaveform(timings, amplitudes, repeat = -1) {
118
125
  }
119
126
  return backend.vibratePattern(timings);
120
127
  }
121
- let _backend = null;
122
- function webVibrate(pattern) {
128
+ let _custom = null;
129
+ let _host = null;
130
+ let _hostConflict = false;
131
+ let _hostObservation = null;
132
+ const _sentinel = {
133
+ cancel() {
134
+ return false;
135
+ },
136
+ capabilities(out) {
137
+ out.amplitudeControl = false;
138
+ out.customEvents = false;
139
+ out.intensity = false;
140
+ out.patterns = false;
141
+ out.supported = false;
142
+ return out;
143
+ },
144
+ impact() {
145
+ return false;
146
+ },
147
+ isSupported() {
148
+ return false;
149
+ },
150
+ notification() {
151
+ return false;
152
+ },
153
+ prepare() { },
154
+ selection() {
155
+ return false;
156
+ },
157
+ vibrate() {
158
+ return false;
159
+ },
160
+ vibratePattern() {
161
+ return false;
162
+ },
163
+ };
164
+ function _webVibrate(pattern, operation) {
123
165
  if (typeof navigator === 'undefined' || !('vibrate' in navigator) || typeof navigator.vibrate !== 'function') {
166
+ observeHapticsHostResult(operation, false);
124
167
  return false;
125
168
  }
126
169
  try {
127
- return navigator.vibrate(pattern);
170
+ const result = navigator.vibrate(pattern);
171
+ observeHapticsHostResult(operation, result);
172
+ return result;
128
173
  }
129
174
  catch {
175
+ observeHapticsHostResult(operation, false);
130
176
  return false;
131
177
  }
132
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"haptics.js","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"haptics.js","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
1
+ export { explainHapticsBackend, cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
1
+ export { explainHapticsBackend, cancelDeviceVibration, getHapticsCapabilities, isHapticsSupported, prepareHaptics, triggerHapticImpact, triggerHapticNotification, triggerHapticSelection, vibrateDevice, vibrateDevicePattern, vibrateDeviceWaveform, } from './contract';
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/haptics",
3
- "version": "0.3.1-next.975.01aea94",
3
+ "version": "0.4.0-edge.1908.751021c",
4
4
  "author": "Joshua Granick and other contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,7 +38,7 @@
38
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@flighthq/types": "0.3.1-next.975.01aea94"
41
+ "@flighthq/types": "0.4.0-edge.1908.751021c"
42
42
  },
43
43
  "devDependencies": {
44
44
  "typescript": "^5.3.0"
@@ -19,6 +19,10 @@ import {
19
19
  vibrateDevice,
20
20
  vibrateDevicePattern,
21
21
  vibrateDeviceWaveform,
22
+ explainHapticsBackend,
23
+ installHapticsHostBackend,
24
+ observeHapticsHostResult,
25
+ resetHapticsBackendForTest,
22
26
  } from './haptics';
23
27
 
24
28
  function makeCapabilities(overrides: Partial<HapticsCapabilities> = {}): HapticsCapabilities {
@@ -155,6 +159,34 @@ describe('createWebHapticsBackend', () => {
155
159
  });
156
160
  });
157
161
 
162
+ describe('explainHapticsBackend', () => {
163
+ afterEach(() => resetHapticsBackendForTest());
164
+
165
+ it('reports host-not-enabled when no backend is installed', () => {
166
+ resetHapticsBackendForTest();
167
+ const explanation = explainHapticsBackend();
168
+ expect(explanation.layer).toBe('host-not-enabled');
169
+ expect(explanation.conflict).toBe(false);
170
+ expect(explanation.viability).toBe('unobserved');
171
+ });
172
+
173
+ it('reports custom layer when a custom backend is set', () => {
174
+ setHapticsBackend(fakeBackend());
175
+ expect(explainHapticsBackend().layer).toBe('custom');
176
+ });
177
+
178
+ it('reports host layer when a host backend is installed', () => {
179
+ installHapticsHostBackend(fakeBackend());
180
+ expect(explainHapticsBackend().layer).toBe('host');
181
+ });
182
+
183
+ it('reports conflict when two different host backends are installed', () => {
184
+ installHapticsHostBackend(fakeBackend());
185
+ installHapticsHostBackend(fakeBackend());
186
+ expect(explainHapticsBackend().conflict).toBe(true);
187
+ });
188
+ });
189
+
158
190
  describe('getHapticsBackend', () => {
159
191
  it('falls back to a web backend', () => {
160
192
  expect(getHapticsBackend()).not.toBeNull();
@@ -188,6 +220,25 @@ describe('getHapticsCapabilities', () => {
188
220
  });
189
221
  });
190
222
 
223
+ describe('installHapticsHostBackend', () => {
224
+ afterEach(() => resetHapticsBackendForTest());
225
+
226
+ it('installs a host backend that getHapticsBackend returns', () => {
227
+ const backend = fakeBackend();
228
+ installHapticsHostBackend(backend);
229
+ expect(getHapticsBackend()).toBe(backend);
230
+ });
231
+
232
+ it('is first-host-wins: a second different backend sets conflict', () => {
233
+ const first = fakeBackend();
234
+ const second = fakeBackend();
235
+ installHapticsHostBackend(first);
236
+ installHapticsHostBackend(second);
237
+ expect(getHapticsBackend()).toBe(first);
238
+ expect(explainHapticsBackend().conflict).toBe(true);
239
+ });
240
+ });
241
+
191
242
  describe('isHapticsSupported', () => {
192
243
  it('returns true from fake backend', () => {
193
244
  setHapticsBackend(fakeBackend());
@@ -199,6 +250,24 @@ describe('isHapticsSupported', () => {
199
250
  });
200
251
  });
201
252
 
253
+ describe('observeHapticsHostResult', () => {
254
+ afterEach(() => resetHapticsBackendForTest());
255
+
256
+ it('records a successful observation', () => {
257
+ installHapticsHostBackend(fakeBackend());
258
+ observeHapticsHostResult('cancel', true);
259
+ const explanation = explainHapticsBackend();
260
+ expect(explanation.operation).toBe('cancel');
261
+ expect(explanation.viability).toBe('available');
262
+ });
263
+
264
+ it('records a failed observation', () => {
265
+ installHapticsHostBackend(fakeBackend());
266
+ observeHapticsHostResult('cancel', false);
267
+ expect(explainHapticsBackend().viability).toBe('runtime-api-unavailable');
268
+ });
269
+ });
270
+
202
271
  describe('prepareHaptics', () => {
203
272
  it('calls prepare on backends that provide it', () => {
204
273
  const backend = fakeBackend();
@@ -216,6 +285,18 @@ describe('prepareHaptics', () => {
216
285
  });
217
286
  });
218
287
 
288
+ describe('resetHapticsBackendForTest', () => {
289
+ it('clears all backend slots', () => {
290
+ setHapticsBackend(fakeBackend());
291
+ installHapticsHostBackend(fakeBackend());
292
+ observeHapticsHostResult('cancel', true);
293
+ resetHapticsBackendForTest();
294
+ expect(explainHapticsBackend().layer).toBe('host-not-enabled');
295
+ expect(explainHapticsBackend().conflict).toBe(false);
296
+ expect(explainHapticsBackend().viability).toBe('unobserved');
297
+ });
298
+ });
299
+
219
300
  describe('setHapticsBackend', () => {
220
301
  it('clears back to the web fallback when passed null', () => {
221
302
  setHapticsBackend(fakeBackend());