@moldable-ai/ui 0.2.17 → 0.2.19
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/components/chat/chat-panel.d.ts +18 -12
- package/dist/components/chat/chat-panel.d.ts.map +1 -1
- package/dist/components/chat/chat-panel.js +73 -19
- package/dist/components/chat/index.d.ts +2 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +2 -1
- package/dist/components/chat/model-effort-power-effects.d.ts +7 -0
- package/dist/components/chat/model-effort-power-effects.d.ts.map +1 -0
- package/dist/components/chat/model-effort-power-effects.js +54 -0
- package/dist/components/chat/model-effort-selector.d.ts +26 -0
- package/dist/components/chat/model-effort-selector.d.ts.map +1 -0
- package/dist/components/chat/model-effort-selector.js +179 -0
- package/dist/components/chat/model-effort-ultra-effects.d.ts +5 -0
- package/dist/components/chat/model-effort-ultra-effects.d.ts.map +1 -0
- package/dist/components/chat/model-effort-ultra-effects.js +56 -0
- package/dist/components/chat/tool-handlers.d.ts.map +1 -1
- package/dist/components/hardware/bluetooth-panel-helpers.d.ts +9 -0
- package/dist/components/hardware/bluetooth-panel-helpers.d.ts.map +1 -0
- package/dist/components/hardware/bluetooth-panel-helpers.js +37 -0
- package/dist/components/hardware/bluetooth-panel.d.ts +56 -0
- package/dist/components/hardware/bluetooth-panel.d.ts.map +1 -0
- package/dist/components/hardware/bluetooth-panel.js +470 -0
- package/dist/components/hardware/camera-preview.d.ts +39 -0
- package/dist/components/hardware/camera-preview.d.ts.map +1 -0
- package/dist/components/hardware/camera-preview.js +228 -0
- package/dist/components/hardware/capability-badge.d.ts +23 -0
- package/dist/components/hardware/capability-badge.d.ts.map +1 -0
- package/dist/components/hardware/capability-badge.js +39 -0
- package/dist/components/hardware/capability-matrix.d.ts +14 -0
- package/dist/components/hardware/capability-matrix.d.ts.map +1 -0
- package/dist/components/hardware/capability-matrix.js +97 -0
- package/dist/components/hardware/clipboard-button.d.ts +26 -0
- package/dist/components/hardware/clipboard-button.d.ts.map +1 -0
- package/dist/components/hardware/clipboard-button.js +114 -0
- package/dist/components/hardware/device-io-controls.d.ts +4 -0
- package/dist/components/hardware/device-io-controls.d.ts.map +1 -0
- package/dist/components/hardware/device-io-controls.js +28 -0
- package/dist/components/hardware/device-list.d.ts +20 -0
- package/dist/components/hardware/device-list.d.ts.map +1 -0
- package/dist/components/hardware/device-list.js +14 -0
- package/dist/components/hardware/display-map.d.ts +23 -0
- package/dist/components/hardware/display-map.d.ts.map +1 -0
- package/dist/components/hardware/display-map.js +114 -0
- package/dist/components/hardware/haptic-button.d.ts +26 -0
- package/dist/components/hardware/haptic-button.d.ts.map +1 -0
- package/dist/components/hardware/haptic-button.js +94 -0
- package/dist/components/hardware/hardware-panel.d.ts +11 -0
- package/dist/components/hardware/hardware-panel.d.ts.map +1 -0
- package/dist/components/hardware/hardware-panel.js +28 -0
- package/dist/components/hardware/hid-panel.d.ts +40 -0
- package/dist/components/hardware/hid-panel.d.ts.map +1 -0
- package/dist/components/hardware/hid-panel.js +373 -0
- package/dist/components/hardware/index.d.ts +27 -0
- package/dist/components/hardware/index.d.ts.map +1 -0
- package/dist/components/hardware/index.js +26 -0
- package/dist/components/hardware/live-indicator.d.ts +15 -0
- package/dist/components/hardware/live-indicator.d.ts.map +1 -0
- package/dist/components/hardware/live-indicator.js +26 -0
- package/dist/components/hardware/local-auth-button.d.ts +33 -0
- package/dist/components/hardware/local-auth-button.d.ts.map +1 -0
- package/dist/components/hardware/local-auth-button.js +115 -0
- package/dist/components/hardware/location-panel.d.ts +29 -0
- package/dist/components/hardware/location-panel.d.ts.map +1 -0
- package/dist/components/hardware/location-panel.js +154 -0
- package/dist/components/hardware/microphone-meter.d.ts +35 -0
- package/dist/components/hardware/microphone-meter.d.ts.map +1 -0
- package/dist/components/hardware/microphone-meter.js +244 -0
- package/dist/components/hardware/midi-monitor.d.ts +54 -0
- package/dist/components/hardware/midi-monitor.d.ts.map +1 -0
- package/dist/components/hardware/midi-monitor.js +534 -0
- package/dist/components/hardware/notification-panel.d.ts +24 -0
- package/dist/components/hardware/notification-panel.d.ts.map +1 -0
- package/dist/components/hardware/notification-panel.js +124 -0
- package/dist/components/hardware/power-blocker-controller.d.ts +19 -0
- package/dist/components/hardware/power-blocker-controller.d.ts.map +1 -0
- package/dist/components/hardware/power-blocker-controller.js +83 -0
- package/dist/components/hardware/power-panel.d.ts +67 -0
- package/dist/components/hardware/power-panel.d.ts.map +1 -0
- package/dist/components/hardware/power-panel.js +309 -0
- package/dist/components/hardware/readout.d.ts +10 -0
- package/dist/components/hardware/readout.d.ts.map +1 -0
- package/dist/components/hardware/readout.js +11 -0
- package/dist/components/hardware/screen-share-preview.d.ts +36 -0
- package/dist/components/hardware/screen-share-preview.d.ts.map +1 -0
- package/dist/components/hardware/screen-share-preview.js +191 -0
- package/dist/components/hardware/secure-storage-panel.d.ts +36 -0
- package/dist/components/hardware/secure-storage-panel.d.ts.map +1 -0
- package/dist/components/hardware/secure-storage-panel.js +193 -0
- package/dist/components/hardware/serial-console.d.ts +64 -0
- package/dist/components/hardware/serial-console.d.ts.map +1 -0
- package/dist/components/hardware/serial-console.js +399 -0
- package/dist/components/hardware/shortcut-recorder.d.ts +35 -0
- package/dist/components/hardware/shortcut-recorder.d.ts.map +1 -0
- package/dist/components/hardware/shortcut-recorder.js +217 -0
- package/dist/components/hardware/stream-log.d.ts +35 -0
- package/dist/components/hardware/stream-log.d.ts.map +1 -0
- package/dist/components/hardware/stream-log.js +65 -0
- package/dist/components/hardware/system-audio-monitor.d.ts +34 -0
- package/dist/components/hardware/system-audio-monitor.d.ts.map +1 -0
- package/dist/components/hardware/system-audio-monitor.js +275 -0
- package/dist/components/hardware/usb-panel.d.ts +54 -0
- package/dist/components/hardware/usb-panel.d.ts.map +1 -0
- package/dist/components/hardware/usb-panel.js +363 -0
- package/dist/components/hardware/use-capability.d.ts +20 -0
- package/dist/components/hardware/use-capability.d.ts.map +1 -0
- package/dist/components/hardware/use-capability.js +61 -0
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/slider.d.ts +4 -1
- package/dist/components/ui/slider.d.ts.map +1 -1
- package/dist/components/ui/slider.js +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/lib/commands.d.ts +59 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +97 -0
- package/dist/lib/native-hardware/bluetooth-access.d.ts +14 -0
- package/dist/lib/native-hardware/bluetooth-access.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-access.js +40 -0
- package/dist/lib/native-hardware/bluetooth-connection.d.ts +19 -0
- package/dist/lib/native-hardware/bluetooth-connection.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-connection.js +88 -0
- package/dist/lib/native-hardware/bluetooth-gatt.d.ts +7 -0
- package/dist/lib/native-hardware/bluetooth-gatt.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-gatt.js +77 -0
- package/dist/lib/native-hardware/bluetooth-internal.d.ts +10 -0
- package/dist/lib/native-hardware/bluetooth-internal.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-internal.js +105 -0
- package/dist/lib/native-hardware/bluetooth-native-fallback.d.ts +33 -0
- package/dist/lib/native-hardware/bluetooth-native-fallback.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-native-fallback.js +214 -0
- package/dist/lib/native-hardware/bluetooth-types.d.ts +85 -0
- package/dist/lib/native-hardware/bluetooth-types.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth-types.js +2 -0
- package/dist/lib/native-hardware/bluetooth.d.ts +7 -0
- package/dist/lib/native-hardware/bluetooth.d.ts.map +1 -0
- package/dist/lib/native-hardware/bluetooth.js +4 -0
- package/dist/lib/native-hardware/bridge.d.ts +23 -0
- package/dist/lib/native-hardware/bridge.d.ts.map +1 -0
- package/dist/lib/native-hardware/bridge.js +266 -0
- package/dist/lib/native-hardware/capabilities.d.ts +7 -0
- package/dist/lib/native-hardware/capabilities.d.ts.map +1 -0
- package/dist/lib/native-hardware/capabilities.js +95 -0
- package/dist/lib/native-hardware/clipboard.d.ts +11 -0
- package/dist/lib/native-hardware/clipboard.d.ts.map +1 -0
- package/dist/lib/native-hardware/clipboard.js +55 -0
- package/dist/lib/native-hardware/device-io-errors.d.ts +11 -0
- package/dist/lib/native-hardware/device-io-errors.d.ts.map +1 -0
- package/dist/lib/native-hardware/device-io-errors.js +82 -0
- package/dist/lib/native-hardware/displays.d.ts +38 -0
- package/dist/lib/native-hardware/displays.d.ts.map +1 -0
- package/dist/lib/native-hardware/displays.js +12 -0
- package/dist/lib/native-hardware/global-shortcuts.d.ts +86 -0
- package/dist/lib/native-hardware/global-shortcuts.d.ts.map +1 -0
- package/dist/lib/native-hardware/global-shortcuts.js +351 -0
- package/dist/lib/native-hardware/haptics.d.ts +25 -0
- package/dist/lib/native-hardware/haptics.d.ts.map +1 -0
- package/dist/lib/native-hardware/haptics.js +18 -0
- package/dist/lib/native-hardware/hid-native.d.ts +26 -0
- package/dist/lib/native-hardware/hid-native.d.ts.map +1 -0
- package/dist/lib/native-hardware/hid-native.js +229 -0
- package/dist/lib/native-hardware/hid.d.ts +52 -0
- package/dist/lib/native-hardware/hid.d.ts.map +1 -0
- package/dist/lib/native-hardware/hid.js +160 -0
- package/dist/lib/native-hardware/index.d.ts +38 -0
- package/dist/lib/native-hardware/index.d.ts.map +1 -0
- package/dist/lib/native-hardware/index.js +20 -0
- package/dist/lib/native-hardware/local-authentication.d.ts +32 -0
- package/dist/lib/native-hardware/local-authentication.d.ts.map +1 -0
- package/dist/lib/native-hardware/local-authentication.js +46 -0
- package/dist/lib/native-hardware/location.d.ts +49 -0
- package/dist/lib/native-hardware/location.d.ts.map +1 -0
- package/dist/lib/native-hardware/location.js +114 -0
- package/dist/lib/native-hardware/media-permissions.d.ts +28 -0
- package/dist/lib/native-hardware/media-permissions.d.ts.map +1 -0
- package/dist/lib/native-hardware/media-permissions.js +37 -0
- package/dist/lib/native-hardware/media.d.ts +17 -0
- package/dist/lib/native-hardware/media.d.ts.map +1 -0
- package/dist/lib/native-hardware/media.js +99 -0
- package/dist/lib/native-hardware/midi-access.d.ts +13 -0
- package/dist/lib/native-hardware/midi-access.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-access.js +66 -0
- package/dist/lib/native-hardware/midi-internal.d.ts +12 -0
- package/dist/lib/native-hardware/midi-internal.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-internal.js +88 -0
- package/dist/lib/native-hardware/midi-native-fallback.d.ts +27 -0
- package/dist/lib/native-hardware/midi-native-fallback.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-native-fallback.js +127 -0
- package/dist/lib/native-hardware/midi-ports.d.ts +6 -0
- package/dist/lib/native-hardware/midi-ports.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-ports.js +115 -0
- package/dist/lib/native-hardware/midi-types.d.ts +25 -0
- package/dist/lib/native-hardware/midi-types.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi-types.js +1 -0
- package/dist/lib/native-hardware/midi.d.ts +6 -0
- package/dist/lib/native-hardware/midi.d.ts.map +1 -0
- package/dist/lib/native-hardware/midi.js +3 -0
- package/dist/lib/native-hardware/native-device-io.d.ts +28 -0
- package/dist/lib/native-hardware/native-device-io.d.ts.map +1 -0
- package/dist/lib/native-hardware/native-device-io.js +91 -0
- package/dist/lib/native-hardware/native-device-lifecycle.d.ts +13 -0
- package/dist/lib/native-hardware/native-device-lifecycle.d.ts.map +1 -0
- package/dist/lib/native-hardware/native-device-lifecycle.js +40 -0
- package/dist/lib/native-hardware/notifications.d.ts +38 -0
- package/dist/lib/native-hardware/notifications.d.ts.map +1 -0
- package/dist/lib/native-hardware/notifications.js +50 -0
- package/dist/lib/native-hardware/power-session.d.ts +113 -0
- package/dist/lib/native-hardware/power-session.d.ts.map +1 -0
- package/dist/lib/native-hardware/power-session.js +264 -0
- package/dist/lib/native-hardware/secure-storage.d.ts +36 -0
- package/dist/lib/native-hardware/secure-storage.d.ts.map +1 -0
- package/dist/lib/native-hardware/secure-storage.js +64 -0
- package/dist/lib/native-hardware/serial-native.d.ts +23 -0
- package/dist/lib/native-hardware/serial-native.d.ts.map +1 -0
- package/dist/lib/native-hardware/serial-native.js +181 -0
- package/dist/lib/native-hardware/serial.d.ts +76 -0
- package/dist/lib/native-hardware/serial.d.ts.map +1 -0
- package/dist/lib/native-hardware/serial.js +212 -0
- package/dist/lib/native-hardware/system-audio.d.ts +127 -0
- package/dist/lib/native-hardware/system-audio.d.ts.map +1 -0
- package/dist/lib/native-hardware/system-audio.js +330 -0
- package/dist/lib/native-hardware/types.d.ts +62 -0
- package/dist/lib/native-hardware/types.d.ts.map +1 -0
- package/dist/lib/native-hardware/types.js +27 -0
- package/dist/lib/native-hardware/usb-native.d.ts +39 -0
- package/dist/lib/native-hardware/usb-native.d.ts.map +1 -0
- package/dist/lib/native-hardware/usb-native.js +217 -0
- package/dist/lib/native-hardware/usb.d.ts +85 -0
- package/dist/lib/native-hardware/usb.d.ts.map +1 -0
- package/dist/lib/native-hardware/usb.js +222 -0
- package/package.json +1 -1
- package/src/styles/index.css +377 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { isMoldableBluetoothSupported, requestMoldableBluetoothDevice, } from './bluetooth-access';
|
|
2
|
+
import { callNativeHardwareBridge, } from './bridge';
|
|
3
|
+
const REQUEST_TYPE = 'moldable:native-bluetooth';
|
|
4
|
+
const RESULT_TYPE = 'moldable:native-bluetooth-result';
|
|
5
|
+
const EVENT_TYPE = 'moldable:native-bluetooth-event';
|
|
6
|
+
function isNativeConnectionResult(value) {
|
|
7
|
+
if (typeof value !== 'object' || value === null)
|
|
8
|
+
return false;
|
|
9
|
+
const connection = value;
|
|
10
|
+
return (typeof connection.connectionId === 'string' &&
|
|
11
|
+
connection.connectionId.length > 0 &&
|
|
12
|
+
(connection.name === null || typeof connection.name === 'string'));
|
|
13
|
+
}
|
|
14
|
+
function uuid(value) {
|
|
15
|
+
if (typeof value === 'number' &&
|
|
16
|
+
Number.isInteger(value) &&
|
|
17
|
+
value >= 0 &&
|
|
18
|
+
value <= 0xffff) {
|
|
19
|
+
return value.toString(16).padStart(4, '0');
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === 'string' && /^[0-9a-fA-F-]{4,64}$/.test(value))
|
|
22
|
+
return value.toLowerCase();
|
|
23
|
+
throw new TypeError('The native Bluetooth fallback requires numeric or UUID service identifiers.');
|
|
24
|
+
}
|
|
25
|
+
function dataBytes(source) {
|
|
26
|
+
const bytes = source instanceof ArrayBuffer
|
|
27
|
+
? new Uint8Array(source)
|
|
28
|
+
: new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
29
|
+
if (bytes.byteLength > 512)
|
|
30
|
+
throw new TypeError('Bluetooth values are limited to 512 bytes.');
|
|
31
|
+
return Array.from(bytes);
|
|
32
|
+
}
|
|
33
|
+
function nativeRequest(connectionId, action, payload, options = {}) {
|
|
34
|
+
return callNativeHardwareBridge({ type: REQUEST_TYPE, action, connectionId, ...payload }, RESULT_TYPE, options);
|
|
35
|
+
}
|
|
36
|
+
function nativeDevice(connection) {
|
|
37
|
+
let disconnected = false;
|
|
38
|
+
const events = new EventTarget();
|
|
39
|
+
const activeSubscriptions = new Set();
|
|
40
|
+
const transitionDisconnected = () => {
|
|
41
|
+
if (disconnected)
|
|
42
|
+
return;
|
|
43
|
+
disconnected = true;
|
|
44
|
+
window.removeEventListener('message', onLifecycleMessage);
|
|
45
|
+
for (const cleanup of [...activeSubscriptions]) {
|
|
46
|
+
void cleanup().catch(() => undefined);
|
|
47
|
+
}
|
|
48
|
+
events.dispatchEvent(new Event('gattserverdisconnected'));
|
|
49
|
+
};
|
|
50
|
+
function onLifecycleMessage(event) {
|
|
51
|
+
if (event.source !== window.parent ||
|
|
52
|
+
typeof event.data !== 'object' ||
|
|
53
|
+
!event.data)
|
|
54
|
+
return;
|
|
55
|
+
const message = event.data;
|
|
56
|
+
if (message.type !== EVENT_TYPE ||
|
|
57
|
+
typeof message.event !== 'object' ||
|
|
58
|
+
!message.event)
|
|
59
|
+
return;
|
|
60
|
+
const value = message.event;
|
|
61
|
+
if (value.connectionId === connection.connectionId &&
|
|
62
|
+
(value.kind === 'disconnected' || value.kind === 'adapterUnavailable')) {
|
|
63
|
+
transitionDisconnected();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
window.addEventListener('message', onLifecycleMessage);
|
|
67
|
+
const assertConnected = () => {
|
|
68
|
+
if (disconnected) {
|
|
69
|
+
throw new DOMException('The Bluetooth device is disconnected.', 'InvalidStateError');
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
id: connection.connectionId,
|
|
74
|
+
name: connection.name,
|
|
75
|
+
get connected() {
|
|
76
|
+
return !disconnected;
|
|
77
|
+
},
|
|
78
|
+
addEventListener(type, listener, options) {
|
|
79
|
+
events.addEventListener(type, listener, options);
|
|
80
|
+
},
|
|
81
|
+
removeEventListener(type, listener, options) {
|
|
82
|
+
events.removeEventListener(type, listener, options);
|
|
83
|
+
},
|
|
84
|
+
async getServices(options) {
|
|
85
|
+
assertConnected();
|
|
86
|
+
return nativeRequest(connection.connectionId, 'getServices', {}, options);
|
|
87
|
+
},
|
|
88
|
+
async read(serviceUuid, characteristicUuid, options) {
|
|
89
|
+
assertConnected();
|
|
90
|
+
const data = await nativeRequest(connection.connectionId, 'read', {
|
|
91
|
+
serviceUuid: uuid(serviceUuid),
|
|
92
|
+
characteristicUuid: uuid(characteristicUuid),
|
|
93
|
+
}, options);
|
|
94
|
+
if (!Array.isArray(data) ||
|
|
95
|
+
!data.every((byte) => Number.isInteger(byte))) {
|
|
96
|
+
throw new TypeError('The desktop returned an invalid Bluetooth value.');
|
|
97
|
+
}
|
|
98
|
+
return new DataView(Uint8Array.from(data).buffer);
|
|
99
|
+
},
|
|
100
|
+
async write(serviceUuid, characteristicUuid, data, options) {
|
|
101
|
+
assertConnected();
|
|
102
|
+
await nativeRequest(connection.connectionId, 'write', {
|
|
103
|
+
serviceUuid: uuid(serviceUuid),
|
|
104
|
+
characteristicUuid: uuid(characteristicUuid),
|
|
105
|
+
data: dataBytes(data),
|
|
106
|
+
withResponse: options?.withResponse !== false,
|
|
107
|
+
}, options);
|
|
108
|
+
},
|
|
109
|
+
async observe(serviceUuid, characteristicUuid, listener, options) {
|
|
110
|
+
assertConnected();
|
|
111
|
+
const service = uuid(serviceUuid);
|
|
112
|
+
const characteristic = uuid(characteristicUuid);
|
|
113
|
+
const onMessage = (event) => {
|
|
114
|
+
if (event.source !== window.parent ||
|
|
115
|
+
typeof event.data !== 'object' ||
|
|
116
|
+
!event.data)
|
|
117
|
+
return;
|
|
118
|
+
const message = event.data;
|
|
119
|
+
if (message.type !== EVENT_TYPE ||
|
|
120
|
+
typeof message.event !== 'object' ||
|
|
121
|
+
!message.event)
|
|
122
|
+
return;
|
|
123
|
+
const value = message.event;
|
|
124
|
+
if (value.connectionId !== connection.connectionId ||
|
|
125
|
+
value.kind !== 'notification' ||
|
|
126
|
+
value.serviceUuid !== service ||
|
|
127
|
+
value.characteristicUuid !== characteristic ||
|
|
128
|
+
!Array.isArray(value.data))
|
|
129
|
+
return;
|
|
130
|
+
if (!value.data.every((byte) => Number.isInteger(byte) &&
|
|
131
|
+
Number(byte) >= 0 &&
|
|
132
|
+
Number(byte) <= 255)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
listener(new DataView(Uint8Array.from(value.data).buffer));
|
|
136
|
+
};
|
|
137
|
+
window.addEventListener('message', onMessage);
|
|
138
|
+
try {
|
|
139
|
+
await nativeRequest(connection.connectionId, 'subscribe', { serviceUuid: service, characteristicUuid: characteristic }, options);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
window.removeEventListener('message', onMessage);
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
let cleanupPromise = null;
|
|
146
|
+
const cleanup = () => {
|
|
147
|
+
if (cleanupPromise)
|
|
148
|
+
return cleanupPromise;
|
|
149
|
+
window.removeEventListener('message', onMessage);
|
|
150
|
+
options?.signal?.removeEventListener('abort', handleAbort);
|
|
151
|
+
activeSubscriptions.delete(cleanup);
|
|
152
|
+
cleanupPromise = disconnected
|
|
153
|
+
? Promise.resolve()
|
|
154
|
+
: nativeRequest(connection.connectionId, 'unsubscribe', { serviceUuid: service, characteristicUuid: characteristic }, { timeoutMs: options?.timeoutMs }).then(() => undefined);
|
|
155
|
+
return cleanupPromise;
|
|
156
|
+
};
|
|
157
|
+
const handleAbort = () => void cleanup().catch(() => undefined);
|
|
158
|
+
activeSubscriptions.add(cleanup);
|
|
159
|
+
options?.signal?.addEventListener('abort', handleAbort, { once: true });
|
|
160
|
+
if (options?.signal?.aborted)
|
|
161
|
+
handleAbort();
|
|
162
|
+
return cleanup;
|
|
163
|
+
},
|
|
164
|
+
async disconnect(options) {
|
|
165
|
+
if (disconnected)
|
|
166
|
+
return;
|
|
167
|
+
window.removeEventListener('message', onLifecycleMessage);
|
|
168
|
+
const subscriptions = [...activeSubscriptions];
|
|
169
|
+
disconnected = true;
|
|
170
|
+
await Promise.allSettled(subscriptions.map((cleanup) => cleanup()));
|
|
171
|
+
try {
|
|
172
|
+
await nativeRequest(connection.connectionId, 'disconnect', {}, options);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
events.dispatchEvent(new Event('gattserverdisconnected'));
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Uses Web Bluetooth first, with a restrictive desktop BLE chooser fallback.
|
|
182
|
+
* The native branch mirrors physical disconnect and adapter-loss events.
|
|
183
|
+
*/
|
|
184
|
+
export async function requestMoldableBluetoothWithFallback(options, bridgeOptions = {}) {
|
|
185
|
+
if (isMoldableBluetoothSupported()) {
|
|
186
|
+
return {
|
|
187
|
+
kind: 'web-bluetooth',
|
|
188
|
+
device: await requestMoldableBluetoothDevice(options, bridgeOptions),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (!('filters' in options) || !options.filters?.length) {
|
|
192
|
+
throw new TypeError('The native Bluetooth fallback does not allow acceptAllDevices.');
|
|
193
|
+
}
|
|
194
|
+
const filters = options.filters.map((filter) => {
|
|
195
|
+
if (filter.manufacturerData?.length || filter.serviceData?.length) {
|
|
196
|
+
throw new TypeError('The native Bluetooth fallback does not support data filters yet.');
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
name: filter.name,
|
|
200
|
+
namePrefix: filter.namePrefix,
|
|
201
|
+
services: filter.services?.map(uuid),
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
const result = await callNativeHardwareBridge({
|
|
205
|
+
type: REQUEST_TYPE,
|
|
206
|
+
action: 'requestDevice',
|
|
207
|
+
filters,
|
|
208
|
+
optionalServices: options.optionalServices?.map(uuid) ?? [],
|
|
209
|
+
}, RESULT_TYPE, { timeoutMs: 120_000, ...bridgeOptions });
|
|
210
|
+
if (!isNativeConnectionResult(result)) {
|
|
211
|
+
throw new TypeError('The desktop returned an invalid Bluetooth device.');
|
|
212
|
+
}
|
|
213
|
+
return { kind: 'desktop-bridge', device: nativeDevice(result) };
|
|
214
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Local structural Web Bluetooth types for runtimes whose DOM library omits them. */
|
|
2
|
+
export type MoldableBluetoothUUID = string | number;
|
|
3
|
+
export interface MoldableBluetoothDataFilter {
|
|
4
|
+
dataPrefix?: BufferSource;
|
|
5
|
+
mask?: BufferSource;
|
|
6
|
+
}
|
|
7
|
+
export interface MoldableBluetoothManufacturerDataFilter extends MoldableBluetoothDataFilter {
|
|
8
|
+
companyIdentifier: number;
|
|
9
|
+
}
|
|
10
|
+
export interface MoldableBluetoothServiceDataFilter extends MoldableBluetoothDataFilter {
|
|
11
|
+
service: MoldableBluetoothUUID;
|
|
12
|
+
}
|
|
13
|
+
export interface MoldableBluetoothLEScanFilter {
|
|
14
|
+
name?: string;
|
|
15
|
+
namePrefix?: string;
|
|
16
|
+
services?: MoldableBluetoothUUID[];
|
|
17
|
+
manufacturerData?: MoldableBluetoothManufacturerDataFilter[];
|
|
18
|
+
serviceData?: MoldableBluetoothServiceDataFilter[];
|
|
19
|
+
}
|
|
20
|
+
export type MoldableBluetoothRequestDeviceOptions = {
|
|
21
|
+
filters: MoldableBluetoothLEScanFilter[];
|
|
22
|
+
acceptAllDevices?: false;
|
|
23
|
+
optionalServices?: MoldableBluetoothUUID[];
|
|
24
|
+
optionalManufacturerData?: number[];
|
|
25
|
+
} | {
|
|
26
|
+
filters?: never;
|
|
27
|
+
acceptAllDevices: true;
|
|
28
|
+
optionalServices?: MoldableBluetoothUUID[];
|
|
29
|
+
optionalManufacturerData?: number[];
|
|
30
|
+
};
|
|
31
|
+
export interface MoldableBluetoothRemoteGATTCharacteristic extends EventTarget {
|
|
32
|
+
readonly service: MoldableBluetoothRemoteGATTService;
|
|
33
|
+
readonly uuid: string;
|
|
34
|
+
readonly value?: DataView;
|
|
35
|
+
startNotifications(): Promise<MoldableBluetoothRemoteGATTCharacteristic>;
|
|
36
|
+
stopNotifications(): Promise<MoldableBluetoothRemoteGATTCharacteristic>;
|
|
37
|
+
readValue(): Promise<DataView>;
|
|
38
|
+
writeValue(value: BufferSource): Promise<void>;
|
|
39
|
+
writeValueWithResponse?(value: BufferSource): Promise<void>;
|
|
40
|
+
writeValueWithoutResponse?(value: BufferSource): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export interface MoldableBluetoothRemoteGATTService {
|
|
43
|
+
readonly device: MoldableBluetoothDevice;
|
|
44
|
+
readonly isPrimary: boolean;
|
|
45
|
+
readonly uuid: string;
|
|
46
|
+
getCharacteristic(characteristic: MoldableBluetoothUUID): Promise<MoldableBluetoothRemoteGATTCharacteristic>;
|
|
47
|
+
getCharacteristics(characteristic?: MoldableBluetoothUUID): Promise<MoldableBluetoothRemoteGATTCharacteristic[]>;
|
|
48
|
+
}
|
|
49
|
+
export interface MoldableBluetoothRemoteGATTServer {
|
|
50
|
+
readonly connected: boolean;
|
|
51
|
+
readonly device: MoldableBluetoothDevice;
|
|
52
|
+
connect(): Promise<MoldableBluetoothRemoteGATTServer>;
|
|
53
|
+
disconnect(): void;
|
|
54
|
+
getPrimaryService(service: MoldableBluetoothUUID): Promise<MoldableBluetoothRemoteGATTService>;
|
|
55
|
+
getPrimaryServices(service?: MoldableBluetoothUUID): Promise<MoldableBluetoothRemoteGATTService[]>;
|
|
56
|
+
}
|
|
57
|
+
export interface MoldableBluetoothDeviceEventMap {
|
|
58
|
+
gattserverdisconnected: Event;
|
|
59
|
+
}
|
|
60
|
+
export interface MoldableBluetoothDevice extends EventTarget {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly name?: string;
|
|
63
|
+
readonly gatt?: MoldableBluetoothRemoteGATTServer;
|
|
64
|
+
addEventListener<K extends keyof MoldableBluetoothDeviceEventMap>(type: K, listener: (event: MoldableBluetoothDeviceEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
|
|
65
|
+
removeEventListener<K extends keyof MoldableBluetoothDeviceEventMap>(type: K, listener: (event: MoldableBluetoothDeviceEventMap[K]) => void, options?: boolean | EventListenerOptions): void;
|
|
66
|
+
}
|
|
67
|
+
export interface MoldableBluetoothAPI {
|
|
68
|
+
getAvailability?(): Promise<boolean>;
|
|
69
|
+
requestDevice(options: MoldableBluetoothRequestDeviceOptions): Promise<MoldableBluetoothDevice>;
|
|
70
|
+
}
|
|
71
|
+
export interface MoldableBluetoothNavigator extends Navigator {
|
|
72
|
+
bluetooth?: MoldableBluetoothAPI;
|
|
73
|
+
}
|
|
74
|
+
export type MoldableBluetoothAvailability = 'available' | 'unavailable' | 'unknown';
|
|
75
|
+
export interface MoldableBluetoothRequestOptions {
|
|
76
|
+
signal?: AbortSignal;
|
|
77
|
+
}
|
|
78
|
+
export interface MoldableBluetoothConnectionOptions {
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
}
|
|
81
|
+
export interface MoldableBluetoothOperationOptions {
|
|
82
|
+
signal?: AbortSignal;
|
|
83
|
+
}
|
|
84
|
+
export type MoldableBluetoothWriteMode = 'with-response' | 'without-response' | 'automatic';
|
|
85
|
+
//# sourceMappingURL=bluetooth-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth-types.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getMoldableBluetoothAvailability, isMoldableBluetoothSupported, requestMoldableBluetoothDevice, } from './bluetooth-access';
|
|
2
|
+
export { connectMoldableBluetoothDevice, MoldableBluetoothConnection, } from './bluetooth-connection';
|
|
3
|
+
export { getMoldableBluetoothCharacteristic, observeMoldableBluetoothNotifications, readMoldableBluetoothValue, writeMoldableBluetoothValue, } from './bluetooth-gatt';
|
|
4
|
+
export type * from './bluetooth-types';
|
|
5
|
+
export { requestMoldableBluetoothWithFallback } from './bluetooth-native-fallback';
|
|
6
|
+
export type { MoldableBluetoothTransport, MoldableNativeBluetoothDevice, MoldableNativeBluetoothService, } from './bluetooth-native-fallback';
|
|
7
|
+
//# sourceMappingURL=bluetooth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bluetooth.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bluetooth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,4BAA4B,EAC5B,8BAA8B,GAC/B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,kCAAkC,EAClC,qCAAqC,EACrC,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,kBAAkB,CAAA;AACzB,mBAAmB,mBAAmB,CAAA;AACtC,OAAO,EAAE,oCAAoC,EAAE,MAAM,6BAA6B,CAAA;AAClF,YAAY,EACV,0BAA0B,EAC1B,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,6BAA6B,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getMoldableBluetoothAvailability, isMoldableBluetoothSupported, requestMoldableBluetoothDevice, } from './bluetooth-access';
|
|
2
|
+
export { connectMoldableBluetoothDevice, MoldableBluetoothConnection, } from './bluetooth-connection';
|
|
3
|
+
export { getMoldableBluetoothCharacteristic, observeMoldableBluetoothNotifications, readMoldableBluetoothValue, writeMoldableBluetoothValue, } from './bluetooth-gatt';
|
|
4
|
+
export { requestMoldableBluetoothWithFallback } from './bluetooth-native-fallback';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type NativeHardwareCapabilityManifest, type NativeHardwareErrorCode, type NativeHardwareErrorPayload } from './types';
|
|
2
|
+
export declare const NATIVE_HARDWARE_CANCEL_REQUEST_TYPE: "moldable:native-hardware-cancel";
|
|
3
|
+
export interface NativeHardwareBridgeOptions {
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
export declare class NativeHardwareBridgeError extends Error {
|
|
8
|
+
readonly code: NativeHardwareErrorCode;
|
|
9
|
+
readonly details?: unknown;
|
|
10
|
+
readonly retryable?: boolean;
|
|
11
|
+
constructor(error: NativeHardwareErrorPayload);
|
|
12
|
+
}
|
|
13
|
+
export declare function toNativeHardwareBridgeError(value: unknown, fallbackMessage?: string): NativeHardwareBridgeError;
|
|
14
|
+
/** Resolve only desktop-owned parent origins; unknown embedders fail closed. */
|
|
15
|
+
export declare function resolveNativeHardwareParentOrigin(referrer: string, immediateAncestorOrigin?: string, currentOrigin?: string, allowDevelopmentOrigin?: boolean): string | null;
|
|
16
|
+
export declare function callNativeHardwareBridge<TResult>(request: {
|
|
17
|
+
type: string;
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}, responseType: string, options?: NativeHardwareBridgeOptions): Promise<TResult>;
|
|
20
|
+
export declare function getNativeHardwareCapabilities(options?: NativeHardwareBridgeOptions): Promise<NativeHardwareCapabilityManifest>;
|
|
21
|
+
/** Public app-author API for querying the host's native capability manifest. */
|
|
22
|
+
export declare const getMoldableNativeCapabilities: typeof getNativeHardwareCapabilities;
|
|
23
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,gCAAgC,EACrC,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAChC,MAAM,SAAS,CAAA;AAGhB,eAAO,MAAM,mCAAmC,EAC9C,iCAA0C,CAAA;AAc5C,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAA;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;gBAEhB,KAAK,EAAE,0BAA0B;CAO9C;AAiBD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,EACd,eAAe,SAAoC,GAClD,yBAAyB,CAW3B;AA6FD,gFAAgF;AAChF,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,uBAAuB,SAAgC,EACvD,aAAa,SAAyB,EACtC,sBAAsB,UAAmC,GACxD,MAAM,GAAG,IAAI,CAoBf;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAC9C,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EACjD,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC,CAqIlB;AAED,wBAAsB,6BAA6B,CACjD,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,gCAAgC,CAAC,CAkB3C;AAED,gFAAgF;AAChF,eAAO,MAAM,6BAA6B,sCAAgC,CAAA"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { isNativeHardwareCapabilityManifest } from './capabilities';
|
|
2
|
+
import { NATIVE_HARDWARE_CAPABILITIES_REQUEST_TYPE, NATIVE_HARDWARE_CAPABILITIES_RESULT_TYPE, } from './types';
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
4
|
+
export const NATIVE_HARDWARE_CANCEL_REQUEST_TYPE = 'moldable:native-hardware-cancel';
|
|
5
|
+
const ERROR_CODES = new Set([
|
|
6
|
+
'invalid_request',
|
|
7
|
+
'not_in_moldable',
|
|
8
|
+
'timeout',
|
|
9
|
+
'aborted',
|
|
10
|
+
'invalid_response',
|
|
11
|
+
'unsupported',
|
|
12
|
+
'unavailable',
|
|
13
|
+
'permission_denied',
|
|
14
|
+
'cancelled',
|
|
15
|
+
'host_error',
|
|
16
|
+
]);
|
|
17
|
+
export class NativeHardwareBridgeError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
details;
|
|
20
|
+
retryable;
|
|
21
|
+
constructor(error) {
|
|
22
|
+
super(error.message);
|
|
23
|
+
this.name = 'NativeHardwareBridgeError';
|
|
24
|
+
this.code = error.code;
|
|
25
|
+
this.details = error.details;
|
|
26
|
+
this.retryable = error.retryable;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isRecord(value) {
|
|
30
|
+
return typeof value === 'object' && value !== null;
|
|
31
|
+
}
|
|
32
|
+
function isNativeHardwareErrorPayload(value) {
|
|
33
|
+
return (isRecord(value) &&
|
|
34
|
+
typeof value.code === 'string' &&
|
|
35
|
+
ERROR_CODES.has(value.code) &&
|
|
36
|
+
typeof value.message === 'string');
|
|
37
|
+
}
|
|
38
|
+
export function toNativeHardwareBridgeError(value, fallbackMessage = 'Native hardware request failed.') {
|
|
39
|
+
if (value instanceof NativeHardwareBridgeError)
|
|
40
|
+
return value;
|
|
41
|
+
if (isNativeHardwareErrorPayload(value)) {
|
|
42
|
+
return new NativeHardwareBridgeError(value);
|
|
43
|
+
}
|
|
44
|
+
return new NativeHardwareBridgeError({
|
|
45
|
+
code: 'host_error',
|
|
46
|
+
message: value instanceof Error ? value.message : fallbackMessage,
|
|
47
|
+
details: value,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function makeRequestId() {
|
|
51
|
+
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
|
52
|
+
return `native-hardware-${crypto.randomUUID()}`;
|
|
53
|
+
}
|
|
54
|
+
return `native-hardware-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
55
|
+
}
|
|
56
|
+
function hasExplicitAuthorityPort(value) {
|
|
57
|
+
const authorityStart = value.indexOf('//');
|
|
58
|
+
if (authorityStart < 0)
|
|
59
|
+
return false;
|
|
60
|
+
const authority = value
|
|
61
|
+
.slice(authorityStart + 2)
|
|
62
|
+
.split(/[/?#]/, 1)[0]
|
|
63
|
+
?.split('@')
|
|
64
|
+
.at(-1);
|
|
65
|
+
return authority?.includes(':') === true;
|
|
66
|
+
}
|
|
67
|
+
function resolveDesktopOwnedOrigin(value, allowDevelopmentOrigin) {
|
|
68
|
+
try {
|
|
69
|
+
const url = new URL(value);
|
|
70
|
+
const hasUnexpectedAuthority = url.username !== '' ||
|
|
71
|
+
url.password !== '' ||
|
|
72
|
+
hasExplicitAuthorityPort(value);
|
|
73
|
+
if (url.protocol === 'tauri:' &&
|
|
74
|
+
url.hostname === 'localhost' &&
|
|
75
|
+
!hasUnexpectedAuthority) {
|
|
76
|
+
return `${url.protocol}//${url.host}`;
|
|
77
|
+
}
|
|
78
|
+
if ((url.protocol === 'http:' || url.protocol === 'https:') &&
|
|
79
|
+
url.hostname === 'tauri.localhost' &&
|
|
80
|
+
!hasUnexpectedAuthority) {
|
|
81
|
+
return url.origin;
|
|
82
|
+
}
|
|
83
|
+
if (allowDevelopmentOrigin &&
|
|
84
|
+
url.protocol === 'http:' &&
|
|
85
|
+
(url.hostname === 'localhost' || url.hostname === '127.0.0.1') &&
|
|
86
|
+
url.port === '5173') {
|
|
87
|
+
return url.origin;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Invalid origins are never trusted.
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function readImmediateAncestorOrigin() {
|
|
96
|
+
try {
|
|
97
|
+
return window.location.ancestorOrigins?.item(0) ?? '';
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function referrerMatchesCurrentOrigin(referrer, currentOrigin) {
|
|
104
|
+
try {
|
|
105
|
+
const referrerOrigin = new URL(referrer).origin;
|
|
106
|
+
const documentOrigin = new URL(currentOrigin).origin;
|
|
107
|
+
return (referrerOrigin !== 'null' &&
|
|
108
|
+
documentOrigin !== 'null' &&
|
|
109
|
+
referrerOrigin === documentOrigin);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function allowsDesktopDevelopmentOrigin() {
|
|
116
|
+
return (import.meta.env.DEV &&
|
|
117
|
+
import.meta.env.VITE_MOLDABLE_DESKTOP_DEV_BRIDGE === 'true');
|
|
118
|
+
}
|
|
119
|
+
/** Resolve only desktop-owned parent origins; unknown embedders fail closed. */
|
|
120
|
+
export function resolveNativeHardwareParentOrigin(referrer, immediateAncestorOrigin = readImmediateAncestorOrigin(), currentOrigin = window.location.origin, allowDevelopmentOrigin = allowsDesktopDevelopmentOrigin()) {
|
|
121
|
+
const referrerValue = referrer.trim();
|
|
122
|
+
if (referrerValue) {
|
|
123
|
+
const trustedReferrer = resolveDesktopOwnedOrigin(referrerValue, allowDevelopmentOrigin);
|
|
124
|
+
if (trustedReferrer)
|
|
125
|
+
return trustedReferrer;
|
|
126
|
+
// A same-origin reload or HMR navigation can replace the parent referrer
|
|
127
|
+
// with the app's own URL. In that case the browser-owned ancestor list is
|
|
128
|
+
// still the authoritative embedding boundary. A different untrusted
|
|
129
|
+
// referrer never reaches this fallback.
|
|
130
|
+
if (!referrerMatchesCurrentOrigin(referrerValue, currentOrigin))
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return resolveDesktopOwnedOrigin(immediateAncestorOrigin.trim(), allowDevelopmentOrigin);
|
|
134
|
+
}
|
|
135
|
+
export function callNativeHardwareBridge(request, responseType, options = {}) {
|
|
136
|
+
if (typeof window === 'undefined' || window.parent === window) {
|
|
137
|
+
return Promise.reject(new NativeHardwareBridgeError({
|
|
138
|
+
code: 'not_in_moldable',
|
|
139
|
+
message: 'Native hardware APIs are only available inside Moldable.',
|
|
140
|
+
retryable: false,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
const parentOrigin = resolveNativeHardwareParentOrigin(document.referrer);
|
|
144
|
+
if (!parentOrigin) {
|
|
145
|
+
return Promise.reject(new NativeHardwareBridgeError({
|
|
146
|
+
code: 'not_in_moldable',
|
|
147
|
+
message: 'The native hardware bridge parent could not be verified.',
|
|
148
|
+
retryable: false,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
if (options.signal?.aborted) {
|
|
152
|
+
return Promise.reject(new NativeHardwareBridgeError({
|
|
153
|
+
code: 'aborted',
|
|
154
|
+
message: 'Native hardware request was aborted.',
|
|
155
|
+
retryable: false,
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const requestId = makeRequestId();
|
|
160
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
161
|
+
let settled = false;
|
|
162
|
+
const cleanup = () => {
|
|
163
|
+
window.clearTimeout(timeout);
|
|
164
|
+
window.removeEventListener('message', handleResponse);
|
|
165
|
+
options.signal?.removeEventListener('abort', handleAbort);
|
|
166
|
+
};
|
|
167
|
+
const cancelHostRequest = () => {
|
|
168
|
+
if (settled)
|
|
169
|
+
return;
|
|
170
|
+
try {
|
|
171
|
+
window.parent.postMessage({
|
|
172
|
+
type: NATIVE_HARDWARE_CANCEL_REQUEST_TYPE,
|
|
173
|
+
requestType: request.type,
|
|
174
|
+
requestId,
|
|
175
|
+
}, parentOrigin);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// The local rejection still completes if the parent disappeared.
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const fail = (error) => {
|
|
182
|
+
if (settled)
|
|
183
|
+
return;
|
|
184
|
+
settled = true;
|
|
185
|
+
cleanup();
|
|
186
|
+
reject(new NativeHardwareBridgeError(error));
|
|
187
|
+
};
|
|
188
|
+
const handleAbort = () => {
|
|
189
|
+
cancelHostRequest();
|
|
190
|
+
fail({
|
|
191
|
+
code: 'aborted',
|
|
192
|
+
message: 'Native hardware request was aborted.',
|
|
193
|
+
retryable: false,
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
const handleResponse = (event) => {
|
|
197
|
+
if (event.source !== window.parent)
|
|
198
|
+
return;
|
|
199
|
+
if (event.origin !== parentOrigin)
|
|
200
|
+
return;
|
|
201
|
+
if (!isRecord(event.data))
|
|
202
|
+
return;
|
|
203
|
+
if (event.data.type !== responseType)
|
|
204
|
+
return;
|
|
205
|
+
if (event.data.requestId !== requestId)
|
|
206
|
+
return;
|
|
207
|
+
if (event.data.ok === true && 'result' in event.data) {
|
|
208
|
+
if (settled)
|
|
209
|
+
return;
|
|
210
|
+
settled = true;
|
|
211
|
+
cleanup();
|
|
212
|
+
resolve(event.data.result);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (event.data.ok === false &&
|
|
216
|
+
isNativeHardwareErrorPayload(event.data.error)) {
|
|
217
|
+
fail(event.data.error);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
fail({
|
|
221
|
+
code: 'invalid_response',
|
|
222
|
+
message: 'Moldable returned an invalid native hardware response.',
|
|
223
|
+
details: event.data,
|
|
224
|
+
retryable: false,
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
const timeout = window.setTimeout(() => {
|
|
228
|
+
cancelHostRequest();
|
|
229
|
+
fail({
|
|
230
|
+
code: 'timeout',
|
|
231
|
+
message: 'Native hardware request timed out.',
|
|
232
|
+
details: { timeoutMs },
|
|
233
|
+
retryable: true,
|
|
234
|
+
});
|
|
235
|
+
}, timeoutMs);
|
|
236
|
+
window.addEventListener('message', handleResponse);
|
|
237
|
+
options.signal?.addEventListener('abort', handleAbort, { once: true });
|
|
238
|
+
try {
|
|
239
|
+
window.parent.postMessage({ ...request, requestId }, parentOrigin);
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
fail({
|
|
243
|
+
code: 'host_error',
|
|
244
|
+
message: error instanceof Error
|
|
245
|
+
? error.message
|
|
246
|
+
: 'Failed to send native hardware request.',
|
|
247
|
+
details: error,
|
|
248
|
+
retryable: true,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
export async function getNativeHardwareCapabilities(options = {}) {
|
|
254
|
+
const manifest = await callNativeHardwareBridge({ type: NATIVE_HARDWARE_CAPABILITIES_REQUEST_TYPE }, NATIVE_HARDWARE_CAPABILITIES_RESULT_TYPE, options);
|
|
255
|
+
if (!isNativeHardwareCapabilityManifest(manifest)) {
|
|
256
|
+
throw new NativeHardwareBridgeError({
|
|
257
|
+
code: 'invalid_response',
|
|
258
|
+
message: 'Moldable returned an invalid native hardware manifest.',
|
|
259
|
+
details: manifest,
|
|
260
|
+
retryable: false,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return manifest;
|
|
264
|
+
}
|
|
265
|
+
/** Public app-author API for querying the host's native capability manifest. */
|
|
266
|
+
export const getMoldableNativeCapabilities = getNativeHardwareCapabilities;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type NativeHardwareCapabilityDescriptor, type NativeHardwareCapabilityId, type NativeHardwareCapabilityManifest } from './types';
|
|
2
|
+
export declare function isNativeHardwareCapabilityId(value: unknown): value is NativeHardwareCapabilityId;
|
|
3
|
+
export declare function isNativeHardwareCapabilityDescriptor(value: unknown, expectedId?: NativeHardwareCapabilityId): value is NativeHardwareCapabilityDescriptor;
|
|
4
|
+
export declare function isNativeHardwareCapabilityManifest(value: unknown): value is NativeHardwareCapabilityManifest;
|
|
5
|
+
export declare function getNativeHardwareCapability(manifest: NativeHardwareCapabilityManifest, id: NativeHardwareCapabilityId): NativeHardwareCapabilityDescriptor;
|
|
6
|
+
export declare function supportsNativeHardwareCapability(manifest: NativeHardwareCapabilityManifest, id: NativeHardwareCapabilityId): boolean;
|
|
7
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../../src/lib/native-hardware/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kCAAkC,EACvC,KAAK,0BAA0B,EAC/B,KAAK,gCAAgC,EACtC,MAAM,SAAS,CAAA;AA+ChB,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,0BAA0B,CAErC;AAED,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,UAAU,CAAC,EAAE,0BAA0B,GACtC,KAAK,IAAI,kCAAkC,CA4B7C;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,gCAAgC,CAe3C;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,gCAAgC,EAC1C,EAAE,EAAE,0BAA0B,GAC7B,kCAAkC,CAEpC;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,gCAAgC,EAC1C,EAAE,EAAE,0BAA0B,GAC7B,OAAO,CAET"}
|