@kelnishi/satmouse-client 0.15.0 → 0.15.2
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/core/index.cjs +52 -24
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.js +52 -24
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +52 -24
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +52 -24
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/react/index.d.cts +0 -294
- package/dist/react/index.d.ts +0 -294
package/dist/core/index.cjs
CHANGED
|
@@ -232,9 +232,6 @@ var WebRTCAdapter = class {
|
|
|
232
232
|
onError = null;
|
|
233
233
|
pc = null;
|
|
234
234
|
signalingUrl;
|
|
235
|
-
/**
|
|
236
|
-
* @param signalingUrl — HTTP endpoint for SDP exchange (e.g., "http://127.0.0.1:18945/rtc/offer")
|
|
237
|
-
*/
|
|
238
235
|
constructor(signalingUrl) {
|
|
239
236
|
this.signalingUrl = signalingUrl;
|
|
240
237
|
}
|
|
@@ -242,18 +239,14 @@ var WebRTCAdapter = class {
|
|
|
242
239
|
if (typeof globalThis.RTCPeerConnection === "undefined") {
|
|
243
240
|
throw new Error("RTCPeerConnection not available");
|
|
244
241
|
}
|
|
245
|
-
this.pc = new RTCPeerConnection({
|
|
246
|
-
iceServers: []
|
|
247
|
-
// Local network, no STUN/TURN
|
|
248
|
-
});
|
|
242
|
+
this.pc = new RTCPeerConnection({ iceServers: [] });
|
|
249
243
|
this.pc.ondatachannel = (event) => {
|
|
250
244
|
const channel = event.channel;
|
|
251
245
|
if (channel.label === "spatial") {
|
|
252
246
|
channel.binaryType = "arraybuffer";
|
|
253
247
|
channel.onmessage = (e) => {
|
|
254
248
|
if (e.data instanceof ArrayBuffer && e.data.byteLength >= 20) {
|
|
255
|
-
|
|
256
|
-
this.onSpatialData?.(decoded);
|
|
249
|
+
this.onSpatialData?.(decodeBinaryFrame(e.data));
|
|
257
250
|
}
|
|
258
251
|
};
|
|
259
252
|
} else if (channel.label === "reliable") {
|
|
@@ -277,23 +270,16 @@ var WebRTCAdapter = class {
|
|
|
277
270
|
const offer = await this.pc.createOffer();
|
|
278
271
|
await this.pc.setLocalDescription(offer);
|
|
279
272
|
await this.waitForICE();
|
|
280
|
-
const
|
|
281
|
-
method: "POST",
|
|
282
|
-
body: this.pc.localDescription.sdp,
|
|
283
|
-
headers: { "Content-Type": "application/sdp" }
|
|
284
|
-
});
|
|
285
|
-
if (!response.ok) {
|
|
286
|
-
throw new Error(`Signaling failed: ${response.status}`);
|
|
287
|
-
}
|
|
288
|
-
const answerSdp = await response.text();
|
|
273
|
+
const answerSdp = await this.exchangeSDP(this.pc.localDescription.sdp);
|
|
289
274
|
await this.pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
|
|
290
275
|
await new Promise((resolve, reject) => {
|
|
291
276
|
const timeout = setTimeout(() => reject(new Error("WebRTC connection timeout")), 1e4);
|
|
292
277
|
const check = () => {
|
|
293
|
-
|
|
278
|
+
const state = this.pc?.connectionState;
|
|
279
|
+
if (state === "connected") {
|
|
294
280
|
clearTimeout(timeout);
|
|
295
281
|
resolve();
|
|
296
|
-
} else if (
|
|
282
|
+
} else if (state === "failed") {
|
|
297
283
|
clearTimeout(timeout);
|
|
298
284
|
reject(new Error("WebRTC connection failed"));
|
|
299
285
|
}
|
|
@@ -301,9 +287,7 @@ var WebRTCAdapter = class {
|
|
|
301
287
|
this.pc.onconnectionstatechange = () => {
|
|
302
288
|
check();
|
|
303
289
|
const state = this.pc?.connectionState;
|
|
304
|
-
if (state === "disconnected" || state === "failed" || state === "closed")
|
|
305
|
-
this.onClose?.();
|
|
306
|
-
}
|
|
290
|
+
if (state === "disconnected" || state === "failed" || state === "closed") this.onClose?.();
|
|
307
291
|
};
|
|
308
292
|
check();
|
|
309
293
|
});
|
|
@@ -315,6 +299,44 @@ var WebRTCAdapter = class {
|
|
|
315
299
|
}
|
|
316
300
|
this.pc = null;
|
|
317
301
|
}
|
|
302
|
+
/** Exchange SDP: try fetch POST, fall back to popup window */
|
|
303
|
+
async exchangeSDP(offerSdp) {
|
|
304
|
+
try {
|
|
305
|
+
const res = await fetch(this.signalingUrl, {
|
|
306
|
+
method: "POST",
|
|
307
|
+
body: offerSdp,
|
|
308
|
+
headers: { "Content-Type": "application/sdp" }
|
|
309
|
+
});
|
|
310
|
+
if (res.ok) return await res.text();
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
return this.exchangeSDPViaPopup(offerSdp);
|
|
314
|
+
}
|
|
315
|
+
exchangeSDPViaPopup(offerSdp) {
|
|
316
|
+
return new Promise((resolve, reject) => {
|
|
317
|
+
const offerB64 = btoa(offerSdp);
|
|
318
|
+
const baseUrl = this.signalingUrl.replace("/rtc/offer", "/rtc/connect-popup");
|
|
319
|
+
const url = `${baseUrl}?offer=${encodeURIComponent(offerB64)}`;
|
|
320
|
+
const popup = globalThis.open(url, "satmouse-rtc", "width=1,height=1,left=-100,top=-100");
|
|
321
|
+
if (!popup) {
|
|
322
|
+
reject(new Error("Popup blocked \u2014 user interaction required"));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const timeout = setTimeout(() => {
|
|
326
|
+
popup.close();
|
|
327
|
+
reject(new Error("WebRTC signaling timeout"));
|
|
328
|
+
}, 1e4);
|
|
329
|
+
const onMessage = (event) => {
|
|
330
|
+
if (event.data?.type === "satmouse-rtc-answer") {
|
|
331
|
+
clearTimeout(timeout);
|
|
332
|
+
globalThis.removeEventListener("message", onMessage);
|
|
333
|
+
popup.close();
|
|
334
|
+
resolve(event.data.answer);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
globalThis.addEventListener("message", onMessage);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
318
340
|
waitForICE() {
|
|
319
341
|
return new Promise((resolve) => {
|
|
320
342
|
if (!this.pc) return resolve();
|
|
@@ -475,12 +497,18 @@ var SatMouseConnection = class extends chunkI5MEZZOT_cjs.TypedEmitter {
|
|
|
475
497
|
if (!this.intentionalClose) this.scheduleReconnect();
|
|
476
498
|
};
|
|
477
499
|
try {
|
|
478
|
-
await
|
|
500
|
+
await Promise.race([
|
|
501
|
+
adapter.connect(),
|
|
502
|
+
new Promise(
|
|
503
|
+
(_, reject) => setTimeout(() => reject(new Error(`${adapter.protocol} connection timeout`)), 5e3)
|
|
504
|
+
)
|
|
505
|
+
]);
|
|
479
506
|
this.transport = adapter;
|
|
480
507
|
this.retryCount = 0;
|
|
481
508
|
this.setState("connected", adapter.protocol);
|
|
482
509
|
return true;
|
|
483
510
|
} catch (err) {
|
|
511
|
+
adapter.close();
|
|
484
512
|
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
485
513
|
return false;
|
|
486
514
|
}
|
package/dist/core/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/discovery.ts","../../src/core/decode.ts","../../src/core/transports/webtransport.ts","../../src/core/transports/websocket.ts","../../src/core/transports/webrtc.ts","../../src/core/connection.ts","../../src/core/launch.ts"],"names":["TypedEmitter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ThingDescription } from \"./types.js\";\n\nexport interface ResolvedEndpoints {\n webtransport?: { url: string; certHash?: string };\n websocket?: { url: string };\n deviceInfoUrl?: string;\n}\n\n/** Fetch the WoT Thing Description from the bridge */\nexport async function fetchThingDescription(tdUrl: string): Promise<ThingDescription> {\n const res = await globalThis.fetch(tdUrl);\n if (!res.ok) throw new Error(`Failed to fetch TD: HTTP ${res.status}`);\n return res.json() as Promise<ThingDescription>;\n}\n\n/** Extract WebTransport, WebSocket, and device info endpoints from a Thing Description */\nexport function resolveEndpoints(td: ThingDescription): ResolvedEndpoints {\n const result: ResolvedEndpoints = {};\n\n const spatialForms = td.events?.spatialData?.forms ?? [];\n\n const wtForm = spatialForms.find((f) => f.subprotocol === \"webtransport\");\n if (wtForm) {\n result.webtransport = {\n url: wtForm.href,\n certHash: td[\"satmouse:certHash\"],\n };\n }\n\n const wsForm = spatialForms.find((f) => f.subprotocol === \"websocket\");\n if (wsForm) {\n result.websocket = { url: wsForm.href };\n }\n\n const deviceForm = td.properties?.deviceInfo?.forms?.[0];\n if (deviceForm) {\n result.deviceInfoUrl = deviceForm.href;\n }\n\n return result;\n}\n","import type { SpatialData, ButtonEvent } from \"./types.js\";\n\n/** Decode 24-byte binary spatial data datagram (WebTransport or raw binary) */\nexport function decodeBinaryFrame(buffer: ArrayBuffer | Uint8Array): SpatialData {\n const len = buffer instanceof ArrayBuffer ? buffer.byteLength : buffer.byteLength;\n if (len < 20) {\n throw new RangeError(`Spatial frame too short: expected ≥20 bytes, got ${len}`);\n }\n const ab = buffer instanceof ArrayBuffer ? buffer : buffer.buffer;\n const offset = buffer instanceof Uint8Array ? buffer.byteOffset : 0;\n const view = new DataView(ab, offset);\n return {\n translation: {\n x: view.getInt16(8, true),\n y: view.getInt16(10, true),\n z: view.getInt16(12, true),\n },\n rotation: {\n x: view.getInt16(14, true),\n y: view.getInt16(16, true),\n z: view.getInt16(18, true),\n },\n timestamp: view.getFloat64(0, true),\n };\n}\n\n/** Decode WebSocket binary frame (1-byte type prefix + 24-byte payload) */\nexport function decodeWsBinaryFrame(\n buffer: ArrayBuffer | Uint8Array,\n): { type: \"spatialData\"; data: SpatialData } | { type: \"buttonEvent\"; data: ButtonEvent } | null {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n if (bytes.length < 1) return null;\n\n const typePrefix = bytes[0];\n if (typePrefix === 0x01 && bytes.length >= 25) {\n return { type: \"spatialData\", data: decodeBinaryFrame(bytes.subarray(1, 25)) };\n }\n if (typePrefix === 0x02) {\n const json = new TextDecoder().decode(bytes.subarray(1));\n return { type: \"buttonEvent\", data: JSON.parse(json) as ButtonEvent };\n }\n return null;\n}\n\n/** Decode length-prefixed JSON button events from a WebTransport stream chunk */\nexport function decodeButtonStream(\n buffer: Uint8Array<ArrayBufferLike>,\n): { events: ButtonEvent[]; remainder: Uint8Array<ArrayBufferLike> } {\n const events: ButtonEvent[] = [];\n let pos = 0;\n\n while (pos + 4 <= buffer.length) {\n const view = new DataView(buffer.buffer, buffer.byteOffset + pos);\n const len = view.getUint32(0, true);\n // Guard against absurd lengths\n if (len > 65536 || pos + 4 + len > buffer.length) break;\n const json = new TextDecoder().decode(buffer.subarray(pos + 4, pos + 4 + len));\n try {\n const event = JSON.parse(json);\n if (typeof event.button === \"number\" && typeof event.pressed === \"boolean\") {\n events.push(event as ButtonEvent);\n }\n } catch {\n // Skip malformed JSON frames\n }\n pos += 4 + len;\n }\n\n return { events, remainder: buffer.subarray(pos) };\n}\n","import type { Transport } from \"./transport.js\";\nimport type { SpatialData, ButtonEvent } from \"../types.js\";\nimport { decodeBinaryFrame, decodeButtonStream } from \"../decode.js\";\n\nexport class WebTransportAdapter implements Transport {\n readonly protocol = \"webtransport\" as const;\n\n onSpatialData: ((data: SpatialData) => void) | null = null;\n onButtonEvent: ((data: ButtonEvent) => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n private transport: any = null;\n private url: string;\n private certHash?: string;\n\n constructor(url: string, certHash?: string) {\n this.url = url;\n this.certHash = certHash;\n }\n\n async connect(): Promise<void> {\n if (typeof globalThis.WebTransport === \"undefined\") {\n throw new Error(\"WebTransport is not available in this environment\");\n }\n\n const options: any = {};\n if (this.certHash) {\n options.serverCertificateHashes = [\n {\n algorithm: \"sha-256\",\n value: Uint8Array.from(atob(this.certHash), (c) => c.charCodeAt(0)),\n },\n ];\n }\n\n this.transport = new (globalThis as any).WebTransport(this.url, options);\n await this.transport.ready;\n\n this.readDatagrams();\n this.readStreams();\n\n this.transport.closed\n .then(() => this.onClose?.())\n .catch(() => this.onClose?.());\n }\n\n close(): void {\n try {\n this.transport?.close();\n } catch {}\n this.transport = null;\n }\n\n private async readDatagrams(): Promise<void> {\n const reader = this.transport.datagrams.readable.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n this.onSpatialData?.(decodeBinaryFrame(value));\n }\n } catch {\n // Transport closed\n }\n }\n\n private async readStreams(): Promise<void> {\n const reader = this.transport.incomingUnidirectionalStreams.getReader();\n try {\n while (true) {\n const { value: stream, done } = await reader.read();\n if (done) break;\n this.readButtonStream(stream);\n }\n } catch {\n // Transport closed\n }\n }\n\n private async readButtonStream(stream: any): Promise<void> {\n const reader = stream.getReader();\n let buffer: Uint8Array<ArrayBufferLike> = new Uint8Array(0);\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n const newBuf = new Uint8Array(buffer.length + value.length);\n newBuf.set(buffer);\n newBuf.set(value, buffer.length);\n\n const { events, remainder } = decodeButtonStream(newBuf);\n for (const event of events) {\n this.onButtonEvent?.(event);\n }\n buffer = remainder;\n }\n } catch {\n // Stream closed\n }\n }\n}\n","import type { Transport } from \"./transport.js\";\nimport type { SpatialData, ButtonEvent, DeviceInfo } from \"../types.js\";\nimport { decodeWsBinaryFrame } from \"../decode.js\";\n\nexport class WebSocketAdapter implements Transport {\n readonly protocol = \"websocket\" as const;\n\n onSpatialData: ((data: SpatialData) => void) | null = null;\n onButtonEvent: ((data: ButtonEvent) => void) | null = null;\n onDeviceStatus: ((event: \"connected\" | \"disconnected\", device: DeviceInfo) => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n private ws: WebSocket | null = null;\n private url: string;\n private subprotocol: string;\n\n constructor(url: string, subprotocol: string = \"satmouse-json\") {\n this.url = url;\n this.subprotocol = subprotocol;\n }\n\n async connect(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.ws = new globalThis.WebSocket(this.url, this.subprotocol);\n if (this.subprotocol === \"satmouse-binary\") {\n this.ws.binaryType = \"arraybuffer\";\n }\n\n this.ws.onopen = () => resolve();\n\n this.ws.onerror = () => {\n reject(new Error(`WebSocket connection failed: ${this.url}`));\n };\n\n this.ws.onmessage = (event: MessageEvent) => {\n if (this.subprotocol === \"satmouse-binary\" && event.data instanceof ArrayBuffer) {\n const decoded = decodeWsBinaryFrame(event.data);\n if (decoded?.type === \"spatialData\") this.onSpatialData?.(decoded.data);\n else if (decoded?.type === \"buttonEvent\") this.onButtonEvent?.(decoded.data);\n } else if (typeof event.data === \"string\") {\n try {\n const msg = JSON.parse(event.data);\n if (msg.type === \"spatialData\") this.onSpatialData?.(msg.data);\n else if (msg.type === \"buttonEvent\") this.onButtonEvent?.(msg.data);\n else if (msg.type === \"deviceStatus\") {\n this.onDeviceStatus?.(msg.data.event, msg.data.device);\n }\n } catch {\n // Ignore malformed messages\n }\n }\n };\n\n this.ws.onclose = () => this.onClose?.();\n });\n }\n\n close(): void {\n try {\n this.ws?.close();\n } catch {}\n this.ws = null;\n }\n}\n","import type { Transport } from \"./transport.js\";\nimport type { SpatialData, ButtonEvent, DeviceInfo } from \"../types.js\";\nimport { decodeBinaryFrame } from \"../decode.js\";\n\n/**\n * WebRTC data channel transport adapter.\n *\n * Connects to the bridge via SDP offer/answer exchange. Receives spatial\n * data on an unreliable \"spatial\" channel and button/status events on\n * a reliable \"reliable\" channel.\n *\n * No trusted certs needed — WebRTC uses DTLS with self-signed certs.\n */\nexport class WebRTCAdapter implements Transport {\n readonly protocol = \"webrtc\" as const;\n\n onSpatialData: ((data: SpatialData) => void) | null = null;\n onButtonEvent: ((data: ButtonEvent) => void) | null = null;\n onDeviceStatus: ((event: \"connected\" | \"disconnected\", device: DeviceInfo) => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n private pc: RTCPeerConnection | null = null;\n private signalingUrl: string;\n\n /**\n * @param signalingUrl — HTTP endpoint for SDP exchange (e.g., \"http://127.0.0.1:18945/rtc/offer\")\n */\n constructor(signalingUrl: string) {\n this.signalingUrl = signalingUrl;\n }\n\n async connect(): Promise<void> {\n if (typeof globalThis.RTCPeerConnection === \"undefined\") {\n throw new Error(\"RTCPeerConnection not available\");\n }\n\n this.pc = new RTCPeerConnection({\n iceServers: [], // Local network, no STUN/TURN\n });\n\n // Listen for data channels created by the bridge\n this.pc.ondatachannel = (event) => {\n const channel = event.channel;\n if (channel.label === \"spatial\") {\n channel.binaryType = \"arraybuffer\";\n channel.onmessage = (e) => {\n if (e.data instanceof ArrayBuffer && e.data.byteLength >= 20) {\n const decoded = decodeBinaryFrame(e.data);\n this.onSpatialData?.(decoded);\n }\n };\n } else if (channel.label === \"reliable\") {\n channel.onmessage = (e) => {\n try {\n const text = typeof e.data === \"string\" ? e.data : new TextDecoder().decode(e.data);\n const msg = JSON.parse(text);\n if (msg.type === \"buttonEvent\") this.onButtonEvent?.(msg.data);\n else if (msg.type === \"deviceStatus\") this.onDeviceStatus?.(msg.data.event, msg.data.device);\n } catch {}\n };\n }\n };\n\n this.pc.onconnectionstatechange = () => {\n const state = this.pc?.connectionState;\n if (state === \"disconnected\" || state === \"failed\" || state === \"closed\") {\n this.onClose?.();\n }\n };\n\n // Create offer\n const offer = await this.pc.createOffer();\n await this.pc.setLocalDescription(offer);\n\n // Wait for ICE gathering to complete (or timeout)\n await this.waitForICE();\n\n // Send offer to bridge, get answer\n const response = await fetch(this.signalingUrl, {\n method: \"POST\",\n body: this.pc.localDescription!.sdp,\n headers: { \"Content-Type\": \"application/sdp\" },\n });\n\n if (!response.ok) {\n throw new Error(`Signaling failed: ${response.status}`);\n }\n\n const answerSdp = await response.text();\n await this.pc.setRemoteDescription({ type: \"answer\", sdp: answerSdp });\n\n // Wait for connection to establish\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => reject(new Error(\"WebRTC connection timeout\")), 10000);\n const check = () => {\n if (this.pc?.connectionState === \"connected\") {\n clearTimeout(timeout);\n resolve();\n } else if (this.pc?.connectionState === \"failed\") {\n clearTimeout(timeout);\n reject(new Error(\"WebRTC connection failed\"));\n }\n };\n this.pc!.onconnectionstatechange = () => {\n check();\n // Re-attach close handler\n const state = this.pc?.connectionState;\n if (state === \"disconnected\" || state === \"failed\" || state === \"closed\") {\n this.onClose?.();\n }\n };\n check(); // May already be connected\n });\n }\n\n close(): void {\n try { this.pc?.close(); } catch {}\n this.pc = null;\n }\n\n private waitForICE(): Promise<void> {\n return new Promise((resolve) => {\n if (!this.pc) return resolve();\n if (this.pc.iceGatheringState === \"complete\") return resolve();\n const timeout = setTimeout(resolve, 2000); // Don't wait too long for local ICE\n this.pc.onicegatheringstatechange = () => {\n if (this.pc?.iceGatheringState === \"complete\") {\n clearTimeout(timeout);\n resolve();\n }\n };\n });\n }\n}\n","import { TypedEmitter } from \"./emitter.js\";\nimport { fetchThingDescription, resolveEndpoints } from \"./discovery.js\";\nimport { WebTransportAdapter } from \"./transports/webtransport.js\";\nimport { WebSocketAdapter } from \"./transports/websocket.js\";\nimport { WebRTCAdapter } from \"./transports/webrtc.js\";\nimport type { Transport } from \"./transports/transport.js\";\nimport type {\n ConnectOptions,\n ConnectionState,\n DeviceInfo,\n SatMouseEvents,\n TransportProtocol,\n} from \"./types.js\";\n\n/**\n * Build a satmouse:// connect URI from connection parameters.\n */\nexport function buildSatMouseUri(host = \"localhost\", wsPort = 18945, wtPort = 18946): string {\n return `satmouse://connect?host=${encodeURIComponent(host)}&wsPort=${wsPort}&wtPort=${wtPort}`;\n}\n\n/**\n * Parse a satmouse:// URI into connection parameters.\n *\n * Format: satmouse://connect?host=<ip>&wsPort=<port>&wtPort=<port>\n * All query params are optional. Defaults: host=localhost, wsPort=4444, wtPort=4443.\n */\nexport function parseSatMouseUri(uri: string): { tdUrl: string; wsUrl: string; wtUrl: string } {\n const url = new URL(uri);\n const host = url.searchParams.get(\"host\") ?? \"localhost\";\n const wsPort = url.searchParams.get(\"wsPort\") ?? \"18945\";\n const wtPort = url.searchParams.get(\"wtPort\") ?? \"18946\";\n return {\n tdUrl: `http://${host}:${wsPort}/td.json`,\n wsUrl: `ws://${host}:${wsPort}/spatial`,\n wtUrl: `https://${host}:${wtPort}`,\n };\n}\n\nconst DEFAULT_OPTIONS: Required<\n Pick<ConnectOptions, \"transports\" | \"reconnectDelay\" | \"maxRetries\" | \"wsSubprotocol\">\n> = {\n transports: [\"webtransport\", \"webrtc\", \"websocket\"],\n reconnectDelay: 2000,\n maxRetries: 3,\n wsSubprotocol: \"satmouse-json\",\n};\n\n/**\n * Core connection to a SatMouse bridge.\n *\n * Handles discovery (via WoT Thing Description), transport negotiation\n * (WebTransport with WebSocket fallback), event dispatch, and auto-reconnect.\n */\nexport class SatMouseConnection extends TypedEmitter<SatMouseEvents> {\n private options: ConnectOptions & typeof DEFAULT_OPTIONS;\n private transport: Transport | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private intentionalClose = false;\n private deviceInfoUrl: string | null = null;\n private retryCount = 0;\n\n private _state: ConnectionState = \"disconnected\";\n private _protocol: TransportProtocol = \"none\";\n\n get state(): ConnectionState {\n return this._state;\n }\n get protocol(): TransportProtocol {\n return this._protocol;\n }\n\n constructor(options?: ConnectOptions) {\n super();\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n async connect(): Promise<void> {\n this.intentionalClose = false;\n this.setState(\"connecting\", \"none\");\n\n // Resolve endpoints — satmouse:// URI takes priority\n let wtUrl = this.options.wtUrl;\n let wsUrl = this.options.wsUrl;\n let certHash = this.options.certHash;\n\n if (this.options.uri) {\n const parsed = parseSatMouseUri(this.options.uri);\n wtUrl = wtUrl ?? parsed.wtUrl;\n wsUrl = wsUrl ?? parsed.wsUrl;\n this.options.tdUrl = this.options.tdUrl ?? parsed.tdUrl;\n }\n\n if (!wtUrl && !wsUrl) {\n const tdUrl = this.options.tdUrl;\n // Use 127.0.0.1 (not localhost) — Safari treats the loopback IP as a\n // \"Potentially Trustworthy Origin\" more reliably than the hostname.\n // Try HTTPS first (works from HTTPS pages), fall back to HTTP.\n const tdUrls = tdUrl\n ? [tdUrl]\n : [\n \"https://127.0.0.1:18947/td.json\",\n \"http://127.0.0.1:18945/td.json\",\n ];\n\n let resolved = false;\n for (const url of tdUrls) {\n try {\n const td = await fetchThingDescription(url);\n const endpoints = resolveEndpoints(td);\n wtUrl = endpoints.webtransport?.url;\n wsUrl = endpoints.websocket?.url;\n certHash = certHash ?? endpoints.webtransport?.certHash;\n this.deviceInfoUrl = endpoints.deviceInfoUrl ?? null;\n resolved = true;\n break;\n } catch {\n // Try next URL\n }\n }\n if (!resolved) {\n this.emit(\"error\", new Error(\"Failed to fetch Thing Description\"));\n // On HTTPS pages, ws:// is blocked as mixed content.\n // Try wss:// on the HTTPS port; fall back to ws:// for HTTP pages.\n const isSecurePage = typeof globalThis.location !== \"undefined\" && globalThis.location.protocol === \"https:\";\n wsUrl = isSecurePage\n ? \"wss://127.0.0.1:18947/spatial\"\n : \"ws://127.0.0.1:18945/spatial\";\n }\n }\n\n // Try transports in preference order\n for (const proto of this.options.transports) {\n if (proto === \"webtransport\" && wtUrl) {\n try {\n if (typeof globalThis.WebTransport === \"undefined\") continue;\n const adapter = new WebTransportAdapter(wtUrl, certHash);\n if (await this.tryTransport(adapter)) return;\n } catch {\n continue;\n }\n }\n if (proto === \"webrtc\") {\n try {\n if (typeof globalThis.RTCPeerConnection === \"undefined\") continue;\n // Use the rtcUrl option, or derive from wsUrl host\n const rtcUrl = this.options.rtcUrl ?? `http://127.0.0.1:18945/rtc/offer`;\n const adapter = new WebRTCAdapter(rtcUrl);\n if (await this.tryTransport(adapter)) return;\n } catch {\n continue;\n }\n }\n if (proto === \"websocket\" && wsUrl) {\n try {\n const adapter = new WebSocketAdapter(wsUrl, this.options.wsSubprotocol);\n if (await this.tryTransport(adapter)) return;\n } catch {\n continue;\n }\n }\n }\n\n this.setState(\"disconnected\", \"none\");\n this.scheduleReconnect();\n }\n\n /** Reset retry count and reconnect. Use after \"failed\" state. */\n retry(): void {\n this.retryCount = 0;\n this.intentionalClose = false;\n this.connect();\n }\n\n disconnect(): void {\n this.intentionalClose = true;\n this.clearReconnect();\n this.transport?.close();\n this.transport = null;\n this.setState(\"disconnected\", \"none\");\n }\n\n async fetchDeviceInfo(): Promise<DeviceInfo[]> {\n if (!this.deviceInfoUrl) return [];\n const res = await globalThis.fetch(this.deviceInfoUrl);\n if (!res.ok) return [];\n const data = await res.json();\n return data.devices ?? [];\n }\n\n private async tryTransport(adapter: Transport): Promise<boolean> {\n adapter.onSpatialData = (data) => this.emit(\"spatialData\", data);\n adapter.onButtonEvent = (data) => this.emit(\"buttonEvent\", data);\n adapter.onError = (err) => this.emit(\"error\", err);\n\n if (\"onDeviceStatus\" in adapter) {\n (adapter as any).onDeviceStatus = (event: \"connected\" | \"disconnected\", device: DeviceInfo) => {\n this.emit(\"deviceStatus\", event, device);\n };\n }\n\n adapter.onClose = () => {\n this.transport = null;\n this.setState(\"disconnected\", \"none\");\n if (!this.intentionalClose) this.scheduleReconnect();\n };\n\n try {\n await adapter.connect();\n this.transport = adapter;\n this.retryCount = 0;\n this.setState(\"connected\", adapter.protocol);\n return true;\n } catch (err) {\n this.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\n return false;\n }\n }\n\n private setState(state: ConnectionState, protocol: TransportProtocol): void {\n if (this._state === state && this._protocol === protocol) return;\n this._state = state;\n this._protocol = protocol;\n this.emit(\"stateChange\", state, protocol);\n }\n\n private scheduleReconnect(): void {\n if (this.options.reconnectDelay <= 0 || this.intentionalClose) return;\n\n this.retryCount++;\n console.log(`[SatMouse] Reconnect attempt ${this.retryCount}/${this.options.maxRetries}`);\n if (this.retryCount > this.options.maxRetries) {\n console.log(\"[SatMouse] Max retries exceeded, giving up\");\n this.setState(\"failed\", \"none\");\n return;\n }\n\n this.clearReconnect();\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, this.options.reconnectDelay);\n }\n\n private clearReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n}\n","const SCHEME_URL = \"satmouse://launch\";\nconst PROJECT_URL = \"https://github.com/kelnishi/SatMouse/releases/latest\";\n\n/** Result of a URI scheme negotiation */\nexport interface NegotiateResult {\n ip: string;\n wsPort: number;\n wtPort: number;\n httpsPort: number;\n certHash?: string;\n challenge?: string;\n}\n\n/**\n * Trigger the satmouse://negotiate URI scheme for discovery when direct\n * HTTP/HTTPS fetch is blocked (Safari LNA).\n *\n * The bridge intercepts the URI, then redirects back to your origin with\n * connection details as query parameters. Your app handles the callback\n * route and passes the result to SatMouseConnection.\n *\n * @param origin - Your app's origin (e.g., \"https://kelcite.app\")\n * @param callbackPath - Path the bridge redirects to (default: \"/satmouse-handshake\")\n * @param challenge - Optional challenge token for verification\n */\nexport function negotiateViaSatMouse(origin: string, callbackPath = \"/satmouse-handshake\", challenge?: string): void {\n const params = new URLSearchParams({ origin, callback: callbackPath });\n if (challenge) params.set(\"challenge\", challenge);\n globalThis.location.href = `satmouse://negotiate?${params}`;\n}\n\n/**\n * Parse the negotiate callback URL parameters (called on your callback route).\n * Returns connection details or null if the params are missing.\n */\nexport function parseNegotiateCallback(searchParams: URLSearchParams): NegotiateResult | null {\n const ip = searchParams.get(\"ip\");\n const wsPort = searchParams.get(\"wsPort\");\n if (!ip || !wsPort) return null;\n return {\n ip,\n wsPort: parseInt(wsPort, 10),\n wtPort: parseInt(searchParams.get(\"wtPort\") ?? \"18946\", 10),\n httpsPort: parseInt(searchParams.get(\"httpsPort\") ?? \"18947\", 10),\n certHash: searchParams.get(\"certHash\") ?? undefined,\n challenge: searchParams.get(\"challenge\") ?? undefined,\n };\n}\n\nexport interface LaunchOptions {\n /** URL scheme to open. Default: \"satmouse://launch\" */\n schemeUrl?: string;\n /** Fallback URL if the app is not installed. Default: GitHub releases page */\n fallbackUrl?: string;\n /** Timeout in ms before assuming the app is not installed. Default: 2500 */\n timeout?: number;\n}\n\n/**\n * Attempt to launch SatMouse via the `satmouse://` URL scheme.\n *\n * If the app is installed and registered, the OS opens it. If not,\n * navigates to the fallback URL (project releases page) after a timeout.\n *\n * Returns true if the scheme likely opened, false if it fell back.\n */\nexport function launchSatMouse(options?: LaunchOptions): Promise<boolean> {\n const schemeUrl = options?.schemeUrl ?? SCHEME_URL;\n const timeout = options?.timeout ?? 2500;\n\n // Validate fallback URL — only allow http/https to prevent javascript: or data: injection\n let fallbackUrl = PROJECT_URL;\n if (options?.fallbackUrl) {\n try {\n const parsed = new URL(options.fallbackUrl);\n if (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") {\n fallbackUrl = options.fallbackUrl;\n }\n } catch {}\n }\n\n return new Promise((resolve) => {\n // Track if we leave the page (scheme handler opened the app)\n let launched = false;\n\n const onBlur = () => {\n launched = true;\n };\n globalThis.addEventListener(\"blur\", onBlur);\n\n // Use a hidden iframe to trigger the scheme without navigating away\n const iframe = document.createElement(\"iframe\");\n iframe.style.display = \"none\";\n iframe.src = schemeUrl;\n document.body.appendChild(iframe);\n\n setTimeout(() => {\n globalThis.removeEventListener(\"blur\", onBlur);\n document.body.removeChild(iframe);\n\n if (launched || document.hidden) {\n resolve(true);\n } else {\n // App not installed — redirect to project page\n globalThis.location.href = fallbackUrl;\n resolve(false);\n }\n }, timeout);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/discovery.ts","../../src/core/decode.ts","../../src/core/transports/webtransport.ts","../../src/core/transports/websocket.ts","../../src/core/transports/webrtc.ts","../../src/core/connection.ts","../../src/core/launch.ts"],"names":["TypedEmitter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ThingDescription } from \"./types.js\";\n\nexport interface ResolvedEndpoints {\n webtransport?: { url: string; certHash?: string };\n websocket?: { url: string };\n deviceInfoUrl?: string;\n}\n\n/** Fetch the WoT Thing Description from the bridge */\nexport async function fetchThingDescription(tdUrl: string): Promise<ThingDescription> {\n const res = await globalThis.fetch(tdUrl);\n if (!res.ok) throw new Error(`Failed to fetch TD: HTTP ${res.status}`);\n return res.json() as Promise<ThingDescription>;\n}\n\n/** Extract WebTransport, WebSocket, and device info endpoints from a Thing Description */\nexport function resolveEndpoints(td: ThingDescription): ResolvedEndpoints {\n const result: ResolvedEndpoints = {};\n\n const spatialForms = td.events?.spatialData?.forms ?? [];\n\n const wtForm = spatialForms.find((f) => f.subprotocol === \"webtransport\");\n if (wtForm) {\n result.webtransport = {\n url: wtForm.href,\n certHash: td[\"satmouse:certHash\"],\n };\n }\n\n const wsForm = spatialForms.find((f) => f.subprotocol === \"websocket\");\n if (wsForm) {\n result.websocket = { url: wsForm.href };\n }\n\n const deviceForm = td.properties?.deviceInfo?.forms?.[0];\n if (deviceForm) {\n result.deviceInfoUrl = deviceForm.href;\n }\n\n return result;\n}\n","import type { SpatialData, ButtonEvent } from \"./types.js\";\n\n/** Decode 24-byte binary spatial data datagram (WebTransport or raw binary) */\nexport function decodeBinaryFrame(buffer: ArrayBuffer | Uint8Array): SpatialData {\n const len = buffer instanceof ArrayBuffer ? buffer.byteLength : buffer.byteLength;\n if (len < 20) {\n throw new RangeError(`Spatial frame too short: expected ≥20 bytes, got ${len}`);\n }\n const ab = buffer instanceof ArrayBuffer ? buffer : buffer.buffer;\n const offset = buffer instanceof Uint8Array ? buffer.byteOffset : 0;\n const view = new DataView(ab, offset);\n return {\n translation: {\n x: view.getInt16(8, true),\n y: view.getInt16(10, true),\n z: view.getInt16(12, true),\n },\n rotation: {\n x: view.getInt16(14, true),\n y: view.getInt16(16, true),\n z: view.getInt16(18, true),\n },\n timestamp: view.getFloat64(0, true),\n };\n}\n\n/** Decode WebSocket binary frame (1-byte type prefix + 24-byte payload) */\nexport function decodeWsBinaryFrame(\n buffer: ArrayBuffer | Uint8Array,\n): { type: \"spatialData\"; data: SpatialData } | { type: \"buttonEvent\"; data: ButtonEvent } | null {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n if (bytes.length < 1) return null;\n\n const typePrefix = bytes[0];\n if (typePrefix === 0x01 && bytes.length >= 25) {\n return { type: \"spatialData\", data: decodeBinaryFrame(bytes.subarray(1, 25)) };\n }\n if (typePrefix === 0x02) {\n const json = new TextDecoder().decode(bytes.subarray(1));\n return { type: \"buttonEvent\", data: JSON.parse(json) as ButtonEvent };\n }\n return null;\n}\n\n/** Decode length-prefixed JSON button events from a WebTransport stream chunk */\nexport function decodeButtonStream(\n buffer: Uint8Array<ArrayBufferLike>,\n): { events: ButtonEvent[]; remainder: Uint8Array<ArrayBufferLike> } {\n const events: ButtonEvent[] = [];\n let pos = 0;\n\n while (pos + 4 <= buffer.length) {\n const view = new DataView(buffer.buffer, buffer.byteOffset + pos);\n const len = view.getUint32(0, true);\n // Guard against absurd lengths\n if (len > 65536 || pos + 4 + len > buffer.length) break;\n const json = new TextDecoder().decode(buffer.subarray(pos + 4, pos + 4 + len));\n try {\n const event = JSON.parse(json);\n if (typeof event.button === \"number\" && typeof event.pressed === \"boolean\") {\n events.push(event as ButtonEvent);\n }\n } catch {\n // Skip malformed JSON frames\n }\n pos += 4 + len;\n }\n\n return { events, remainder: buffer.subarray(pos) };\n}\n","import type { Transport } from \"./transport.js\";\nimport type { SpatialData, ButtonEvent } from \"../types.js\";\nimport { decodeBinaryFrame, decodeButtonStream } from \"../decode.js\";\n\nexport class WebTransportAdapter implements Transport {\n readonly protocol = \"webtransport\" as const;\n\n onSpatialData: ((data: SpatialData) => void) | null = null;\n onButtonEvent: ((data: ButtonEvent) => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n private transport: any = null;\n private url: string;\n private certHash?: string;\n\n constructor(url: string, certHash?: string) {\n this.url = url;\n this.certHash = certHash;\n }\n\n async connect(): Promise<void> {\n if (typeof globalThis.WebTransport === \"undefined\") {\n throw new Error(\"WebTransport is not available in this environment\");\n }\n\n const options: any = {};\n if (this.certHash) {\n options.serverCertificateHashes = [\n {\n algorithm: \"sha-256\",\n value: Uint8Array.from(atob(this.certHash), (c) => c.charCodeAt(0)),\n },\n ];\n }\n\n this.transport = new (globalThis as any).WebTransport(this.url, options);\n await this.transport.ready;\n\n this.readDatagrams();\n this.readStreams();\n\n this.transport.closed\n .then(() => this.onClose?.())\n .catch(() => this.onClose?.());\n }\n\n close(): void {\n try {\n this.transport?.close();\n } catch {}\n this.transport = null;\n }\n\n private async readDatagrams(): Promise<void> {\n const reader = this.transport.datagrams.readable.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n this.onSpatialData?.(decodeBinaryFrame(value));\n }\n } catch {\n // Transport closed\n }\n }\n\n private async readStreams(): Promise<void> {\n const reader = this.transport.incomingUnidirectionalStreams.getReader();\n try {\n while (true) {\n const { value: stream, done } = await reader.read();\n if (done) break;\n this.readButtonStream(stream);\n }\n } catch {\n // Transport closed\n }\n }\n\n private async readButtonStream(stream: any): Promise<void> {\n const reader = stream.getReader();\n let buffer: Uint8Array<ArrayBufferLike> = new Uint8Array(0);\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n const newBuf = new Uint8Array(buffer.length + value.length);\n newBuf.set(buffer);\n newBuf.set(value, buffer.length);\n\n const { events, remainder } = decodeButtonStream(newBuf);\n for (const event of events) {\n this.onButtonEvent?.(event);\n }\n buffer = remainder;\n }\n } catch {\n // Stream closed\n }\n }\n}\n","import type { Transport } from \"./transport.js\";\nimport type { SpatialData, ButtonEvent, DeviceInfo } from \"../types.js\";\nimport { decodeWsBinaryFrame } from \"../decode.js\";\n\nexport class WebSocketAdapter implements Transport {\n readonly protocol = \"websocket\" as const;\n\n onSpatialData: ((data: SpatialData) => void) | null = null;\n onButtonEvent: ((data: ButtonEvent) => void) | null = null;\n onDeviceStatus: ((event: \"connected\" | \"disconnected\", device: DeviceInfo) => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n private ws: WebSocket | null = null;\n private url: string;\n private subprotocol: string;\n\n constructor(url: string, subprotocol: string = \"satmouse-json\") {\n this.url = url;\n this.subprotocol = subprotocol;\n }\n\n async connect(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.ws = new globalThis.WebSocket(this.url, this.subprotocol);\n if (this.subprotocol === \"satmouse-binary\") {\n this.ws.binaryType = \"arraybuffer\";\n }\n\n this.ws.onopen = () => resolve();\n\n this.ws.onerror = () => {\n reject(new Error(`WebSocket connection failed: ${this.url}`));\n };\n\n this.ws.onmessage = (event: MessageEvent) => {\n if (this.subprotocol === \"satmouse-binary\" && event.data instanceof ArrayBuffer) {\n const decoded = decodeWsBinaryFrame(event.data);\n if (decoded?.type === \"spatialData\") this.onSpatialData?.(decoded.data);\n else if (decoded?.type === \"buttonEvent\") this.onButtonEvent?.(decoded.data);\n } else if (typeof event.data === \"string\") {\n try {\n const msg = JSON.parse(event.data);\n if (msg.type === \"spatialData\") this.onSpatialData?.(msg.data);\n else if (msg.type === \"buttonEvent\") this.onButtonEvent?.(msg.data);\n else if (msg.type === \"deviceStatus\") {\n this.onDeviceStatus?.(msg.data.event, msg.data.device);\n }\n } catch {\n // Ignore malformed messages\n }\n }\n };\n\n this.ws.onclose = () => this.onClose?.();\n });\n }\n\n close(): void {\n try {\n this.ws?.close();\n } catch {}\n this.ws = null;\n }\n}\n","import type { Transport } from \"./transport.js\";\nimport type { SpatialData, ButtonEvent, DeviceInfo } from \"../types.js\";\nimport { decodeBinaryFrame } from \"../decode.js\";\n\n/**\n * WebRTC data channel transport adapter.\n *\n * Connects to the bridge via SDP offer/answer exchange. Receives spatial\n * data on an unreliable \"spatial\" channel and button/status events on\n * a reliable \"reliable\" channel.\n *\n * No trusted certs needed — WebRTC uses DTLS with self-signed certs.\n *\n * Signaling modes:\n * - fetch POST to /rtc/offer (works from HTTP pages or same-origin)\n * - popup window to /rtc/connect (works from HTTPS pages where fetch is blocked)\n */\nexport class WebRTCAdapter implements Transport {\n readonly protocol = \"webrtc\" as const;\n\n onSpatialData: ((data: SpatialData) => void) | null = null;\n onButtonEvent: ((data: ButtonEvent) => void) | null = null;\n onDeviceStatus: ((event: \"connected\" | \"disconnected\", device: DeviceInfo) => void) | null = null;\n onClose: (() => void) | null = null;\n onError: ((error: Error) => void) | null = null;\n\n private pc: RTCPeerConnection | null = null;\n private signalingUrl: string;\n\n constructor(signalingUrl: string) {\n this.signalingUrl = signalingUrl;\n }\n\n async connect(): Promise<void> {\n if (typeof globalThis.RTCPeerConnection === \"undefined\") {\n throw new Error(\"RTCPeerConnection not available\");\n }\n\n this.pc = new RTCPeerConnection({ iceServers: [] });\n\n this.pc.ondatachannel = (event) => {\n const channel = event.channel;\n if (channel.label === \"spatial\") {\n channel.binaryType = \"arraybuffer\";\n channel.onmessage = (e) => {\n if (e.data instanceof ArrayBuffer && e.data.byteLength >= 20) {\n this.onSpatialData?.(decodeBinaryFrame(e.data));\n }\n };\n } else if (channel.label === \"reliable\") {\n channel.onmessage = (e) => {\n try {\n const text = typeof e.data === \"string\" ? e.data : new TextDecoder().decode(e.data);\n const msg = JSON.parse(text);\n if (msg.type === \"buttonEvent\") this.onButtonEvent?.(msg.data);\n else if (msg.type === \"deviceStatus\") this.onDeviceStatus?.(msg.data.event, msg.data.device);\n } catch {}\n };\n }\n };\n\n this.pc.onconnectionstatechange = () => {\n const state = this.pc?.connectionState;\n if (state === \"disconnected\" || state === \"failed\" || state === \"closed\") {\n this.onClose?.();\n }\n };\n\n const offer = await this.pc.createOffer();\n await this.pc.setLocalDescription(offer);\n await this.waitForICE();\n\n // Try fetch first, fall back to popup signaling\n const answerSdp = await this.exchangeSDP(this.pc.localDescription!.sdp);\n await this.pc.setRemoteDescription({ type: \"answer\", sdp: answerSdp });\n\n // Wait for connection\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => reject(new Error(\"WebRTC connection timeout\")), 10000);\n const check = () => {\n const state = this.pc?.connectionState;\n if (state === \"connected\") { clearTimeout(timeout); resolve(); }\n else if (state === \"failed\") { clearTimeout(timeout); reject(new Error(\"WebRTC connection failed\")); }\n };\n this.pc!.onconnectionstatechange = () => {\n check();\n const state = this.pc?.connectionState;\n if (state === \"disconnected\" || state === \"failed\" || state === \"closed\") this.onClose?.();\n };\n check();\n });\n }\n\n close(): void {\n try { this.pc?.close(); } catch {}\n this.pc = null;\n }\n\n /** Exchange SDP: try fetch POST, fall back to popup window */\n private async exchangeSDP(offerSdp: string): Promise<string> {\n // Try direct fetch first\n try {\n const res = await fetch(this.signalingUrl, {\n method: \"POST\",\n body: offerSdp,\n headers: { \"Content-Type\": \"application/sdp\" },\n });\n if (res.ok) return await res.text();\n } catch {\n // Fetch blocked (mixed content) — fall through to popup\n }\n\n // Popup signaling: open a small window to the bridge's /rtc/connect endpoint\n // The bridge processes the offer and posts the answer back via window.opener.postMessage\n return this.exchangeSDPViaPopup(offerSdp);\n }\n\n private exchangeSDPViaPopup(offerSdp: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const offerB64 = btoa(offerSdp);\n // Build URL — use /rtc/connect which returns HTML that postMessages the answer back\n const baseUrl = this.signalingUrl.replace(\"/rtc/offer\", \"/rtc/connect-popup\");\n const url = `${baseUrl}?offer=${encodeURIComponent(offerB64)}`;\n\n const popup = globalThis.open(url, \"satmouse-rtc\", \"width=1,height=1,left=-100,top=-100\");\n if (!popup) {\n reject(new Error(\"Popup blocked — user interaction required\"));\n return;\n }\n\n const timeout = setTimeout(() => {\n popup.close();\n reject(new Error(\"WebRTC signaling timeout\"));\n }, 10000);\n\n const onMessage = (event: MessageEvent) => {\n if (event.data?.type === \"satmouse-rtc-answer\") {\n clearTimeout(timeout);\n globalThis.removeEventListener(\"message\", onMessage);\n popup.close();\n resolve(event.data.answer);\n }\n };\n globalThis.addEventListener(\"message\", onMessage);\n });\n }\n\n private waitForICE(): Promise<void> {\n return new Promise((resolve) => {\n if (!this.pc) return resolve();\n if (this.pc.iceGatheringState === \"complete\") return resolve();\n const timeout = setTimeout(resolve, 2000);\n this.pc.onicegatheringstatechange = () => {\n if (this.pc?.iceGatheringState === \"complete\") {\n clearTimeout(timeout);\n resolve();\n }\n };\n });\n }\n}\n","import { TypedEmitter } from \"./emitter.js\";\nimport { fetchThingDescription, resolveEndpoints } from \"./discovery.js\";\nimport { WebTransportAdapter } from \"./transports/webtransport.js\";\nimport { WebSocketAdapter } from \"./transports/websocket.js\";\nimport { WebRTCAdapter } from \"./transports/webrtc.js\";\nimport type { Transport } from \"./transports/transport.js\";\nimport type {\n ConnectOptions,\n ConnectionState,\n DeviceInfo,\n SatMouseEvents,\n TransportProtocol,\n} from \"./types.js\";\n\n/**\n * Build a satmouse:// connect URI from connection parameters.\n */\nexport function buildSatMouseUri(host = \"localhost\", wsPort = 18945, wtPort = 18946): string {\n return `satmouse://connect?host=${encodeURIComponent(host)}&wsPort=${wsPort}&wtPort=${wtPort}`;\n}\n\n/**\n * Parse a satmouse:// URI into connection parameters.\n *\n * Format: satmouse://connect?host=<ip>&wsPort=<port>&wtPort=<port>\n * All query params are optional. Defaults: host=localhost, wsPort=4444, wtPort=4443.\n */\nexport function parseSatMouseUri(uri: string): { tdUrl: string; wsUrl: string; wtUrl: string } {\n const url = new URL(uri);\n const host = url.searchParams.get(\"host\") ?? \"localhost\";\n const wsPort = url.searchParams.get(\"wsPort\") ?? \"18945\";\n const wtPort = url.searchParams.get(\"wtPort\") ?? \"18946\";\n return {\n tdUrl: `http://${host}:${wsPort}/td.json`,\n wsUrl: `ws://${host}:${wsPort}/spatial`,\n wtUrl: `https://${host}:${wtPort}`,\n };\n}\n\nconst DEFAULT_OPTIONS: Required<\n Pick<ConnectOptions, \"transports\" | \"reconnectDelay\" | \"maxRetries\" | \"wsSubprotocol\">\n> = {\n transports: [\"webtransport\", \"webrtc\", \"websocket\"],\n reconnectDelay: 2000,\n maxRetries: 3,\n wsSubprotocol: \"satmouse-json\",\n};\n\n/**\n * Core connection to a SatMouse bridge.\n *\n * Handles discovery (via WoT Thing Description), transport negotiation\n * (WebTransport with WebSocket fallback), event dispatch, and auto-reconnect.\n */\nexport class SatMouseConnection extends TypedEmitter<SatMouseEvents> {\n private options: ConnectOptions & typeof DEFAULT_OPTIONS;\n private transport: Transport | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private intentionalClose = false;\n private deviceInfoUrl: string | null = null;\n private retryCount = 0;\n\n private _state: ConnectionState = \"disconnected\";\n private _protocol: TransportProtocol = \"none\";\n\n get state(): ConnectionState {\n return this._state;\n }\n get protocol(): TransportProtocol {\n return this._protocol;\n }\n\n constructor(options?: ConnectOptions) {\n super();\n this.options = { ...DEFAULT_OPTIONS, ...options };\n }\n\n async connect(): Promise<void> {\n this.intentionalClose = false;\n this.setState(\"connecting\", \"none\");\n\n // Resolve endpoints — satmouse:// URI takes priority\n let wtUrl = this.options.wtUrl;\n let wsUrl = this.options.wsUrl;\n let certHash = this.options.certHash;\n\n if (this.options.uri) {\n const parsed = parseSatMouseUri(this.options.uri);\n wtUrl = wtUrl ?? parsed.wtUrl;\n wsUrl = wsUrl ?? parsed.wsUrl;\n this.options.tdUrl = this.options.tdUrl ?? parsed.tdUrl;\n }\n\n if (!wtUrl && !wsUrl) {\n const tdUrl = this.options.tdUrl;\n // Use 127.0.0.1 (not localhost) — Safari treats the loopback IP as a\n // \"Potentially Trustworthy Origin\" more reliably than the hostname.\n // Try HTTPS first (works from HTTPS pages), fall back to HTTP.\n const tdUrls = tdUrl\n ? [tdUrl]\n : [\n \"https://127.0.0.1:18947/td.json\",\n \"http://127.0.0.1:18945/td.json\",\n ];\n\n let resolved = false;\n for (const url of tdUrls) {\n try {\n const td = await fetchThingDescription(url);\n const endpoints = resolveEndpoints(td);\n wtUrl = endpoints.webtransport?.url;\n wsUrl = endpoints.websocket?.url;\n certHash = certHash ?? endpoints.webtransport?.certHash;\n this.deviceInfoUrl = endpoints.deviceInfoUrl ?? null;\n resolved = true;\n break;\n } catch {\n // Try next URL\n }\n }\n if (!resolved) {\n this.emit(\"error\", new Error(\"Failed to fetch Thing Description\"));\n // On HTTPS pages, ws:// is blocked as mixed content.\n // Try wss:// on the HTTPS port; fall back to ws:// for HTTP pages.\n const isSecurePage = typeof globalThis.location !== \"undefined\" && globalThis.location.protocol === \"https:\";\n wsUrl = isSecurePage\n ? \"wss://127.0.0.1:18947/spatial\"\n : \"ws://127.0.0.1:18945/spatial\";\n }\n }\n\n // Try transports in preference order\n for (const proto of this.options.transports) {\n if (proto === \"webtransport\" && wtUrl) {\n try {\n if (typeof globalThis.WebTransport === \"undefined\") continue;\n const adapter = new WebTransportAdapter(wtUrl, certHash);\n if (await this.tryTransport(adapter)) return;\n } catch {\n continue;\n }\n }\n if (proto === \"webrtc\") {\n try {\n if (typeof globalThis.RTCPeerConnection === \"undefined\") continue;\n // Use the rtcUrl option, or derive from wsUrl host\n const rtcUrl = this.options.rtcUrl ?? `http://127.0.0.1:18945/rtc/offer`;\n const adapter = new WebRTCAdapter(rtcUrl);\n if (await this.tryTransport(adapter)) return;\n } catch {\n continue;\n }\n }\n if (proto === \"websocket\" && wsUrl) {\n try {\n const adapter = new WebSocketAdapter(wsUrl, this.options.wsSubprotocol);\n if (await this.tryTransport(adapter)) return;\n } catch {\n continue;\n }\n }\n }\n\n this.setState(\"disconnected\", \"none\");\n this.scheduleReconnect();\n }\n\n /** Reset retry count and reconnect. Use after \"failed\" state. */\n retry(): void {\n this.retryCount = 0;\n this.intentionalClose = false;\n this.connect();\n }\n\n disconnect(): void {\n this.intentionalClose = true;\n this.clearReconnect();\n this.transport?.close();\n this.transport = null;\n this.setState(\"disconnected\", \"none\");\n }\n\n async fetchDeviceInfo(): Promise<DeviceInfo[]> {\n if (!this.deviceInfoUrl) return [];\n const res = await globalThis.fetch(this.deviceInfoUrl);\n if (!res.ok) return [];\n const data = await res.json();\n return data.devices ?? [];\n }\n\n private async tryTransport(adapter: Transport): Promise<boolean> {\n adapter.onSpatialData = (data) => this.emit(\"spatialData\", data);\n adapter.onButtonEvent = (data) => this.emit(\"buttonEvent\", data);\n adapter.onError = (err) => this.emit(\"error\", err);\n\n if (\"onDeviceStatus\" in adapter) {\n (adapter as any).onDeviceStatus = (event: \"connected\" | \"disconnected\", device: DeviceInfo) => {\n this.emit(\"deviceStatus\", event, device);\n };\n }\n\n adapter.onClose = () => {\n this.transport = null;\n this.setState(\"disconnected\", \"none\");\n if (!this.intentionalClose) this.scheduleReconnect();\n };\n\n try {\n // Timeout transport connection attempts to prevent hanging the fallback chain\n await Promise.race([\n adapter.connect(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`${adapter.protocol} connection timeout`)), 5000)\n ),\n ]);\n this.transport = adapter;\n this.retryCount = 0;\n this.setState(\"connected\", adapter.protocol);\n return true;\n } catch (err) {\n adapter.close();\n this.emit(\"error\", err instanceof Error ? err : new Error(String(err)));\n return false;\n }\n }\n\n private setState(state: ConnectionState, protocol: TransportProtocol): void {\n if (this._state === state && this._protocol === protocol) return;\n this._state = state;\n this._protocol = protocol;\n this.emit(\"stateChange\", state, protocol);\n }\n\n private scheduleReconnect(): void {\n if (this.options.reconnectDelay <= 0 || this.intentionalClose) return;\n\n this.retryCount++;\n console.log(`[SatMouse] Reconnect attempt ${this.retryCount}/${this.options.maxRetries}`);\n if (this.retryCount > this.options.maxRetries) {\n console.log(\"[SatMouse] Max retries exceeded, giving up\");\n this.setState(\"failed\", \"none\");\n return;\n }\n\n this.clearReconnect();\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, this.options.reconnectDelay);\n }\n\n private clearReconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n}\n","const SCHEME_URL = \"satmouse://launch\";\nconst PROJECT_URL = \"https://github.com/kelnishi/SatMouse/releases/latest\";\n\n/** Result of a URI scheme negotiation */\nexport interface NegotiateResult {\n ip: string;\n wsPort: number;\n wtPort: number;\n httpsPort: number;\n certHash?: string;\n challenge?: string;\n}\n\n/**\n * Trigger the satmouse://negotiate URI scheme for discovery when direct\n * HTTP/HTTPS fetch is blocked (Safari LNA).\n *\n * The bridge intercepts the URI, then redirects back to your origin with\n * connection details as query parameters. Your app handles the callback\n * route and passes the result to SatMouseConnection.\n *\n * @param origin - Your app's origin (e.g., \"https://kelcite.app\")\n * @param callbackPath - Path the bridge redirects to (default: \"/satmouse-handshake\")\n * @param challenge - Optional challenge token for verification\n */\nexport function negotiateViaSatMouse(origin: string, callbackPath = \"/satmouse-handshake\", challenge?: string): void {\n const params = new URLSearchParams({ origin, callback: callbackPath });\n if (challenge) params.set(\"challenge\", challenge);\n globalThis.location.href = `satmouse://negotiate?${params}`;\n}\n\n/**\n * Parse the negotiate callback URL parameters (called on your callback route).\n * Returns connection details or null if the params are missing.\n */\nexport function parseNegotiateCallback(searchParams: URLSearchParams): NegotiateResult | null {\n const ip = searchParams.get(\"ip\");\n const wsPort = searchParams.get(\"wsPort\");\n if (!ip || !wsPort) return null;\n return {\n ip,\n wsPort: parseInt(wsPort, 10),\n wtPort: parseInt(searchParams.get(\"wtPort\") ?? \"18946\", 10),\n httpsPort: parseInt(searchParams.get(\"httpsPort\") ?? \"18947\", 10),\n certHash: searchParams.get(\"certHash\") ?? undefined,\n challenge: searchParams.get(\"challenge\") ?? undefined,\n };\n}\n\nexport interface LaunchOptions {\n /** URL scheme to open. Default: \"satmouse://launch\" */\n schemeUrl?: string;\n /** Fallback URL if the app is not installed. Default: GitHub releases page */\n fallbackUrl?: string;\n /** Timeout in ms before assuming the app is not installed. Default: 2500 */\n timeout?: number;\n}\n\n/**\n * Attempt to launch SatMouse via the `satmouse://` URL scheme.\n *\n * If the app is installed and registered, the OS opens it. If not,\n * navigates to the fallback URL (project releases page) after a timeout.\n *\n * Returns true if the scheme likely opened, false if it fell back.\n */\nexport function launchSatMouse(options?: LaunchOptions): Promise<boolean> {\n const schemeUrl = options?.schemeUrl ?? SCHEME_URL;\n const timeout = options?.timeout ?? 2500;\n\n // Validate fallback URL — only allow http/https to prevent javascript: or data: injection\n let fallbackUrl = PROJECT_URL;\n if (options?.fallbackUrl) {\n try {\n const parsed = new URL(options.fallbackUrl);\n if (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") {\n fallbackUrl = options.fallbackUrl;\n }\n } catch {}\n }\n\n return new Promise((resolve) => {\n // Track if we leave the page (scheme handler opened the app)\n let launched = false;\n\n const onBlur = () => {\n launched = true;\n };\n globalThis.addEventListener(\"blur\", onBlur);\n\n // Use a hidden iframe to trigger the scheme without navigating away\n const iframe = document.createElement(\"iframe\");\n iframe.style.display = \"none\";\n iframe.src = schemeUrl;\n document.body.appendChild(iframe);\n\n setTimeout(() => {\n globalThis.removeEventListener(\"blur\", onBlur);\n document.body.removeChild(iframe);\n\n if (launched || document.hidden) {\n resolve(true);\n } else {\n // App not installed — redirect to project page\n globalThis.location.href = fallbackUrl;\n resolve(false);\n }\n }, timeout);\n });\n}\n"]}
|
package/dist/core/index.js
CHANGED
|
@@ -231,9 +231,6 @@ var WebRTCAdapter = class {
|
|
|
231
231
|
onError = null;
|
|
232
232
|
pc = null;
|
|
233
233
|
signalingUrl;
|
|
234
|
-
/**
|
|
235
|
-
* @param signalingUrl — HTTP endpoint for SDP exchange (e.g., "http://127.0.0.1:18945/rtc/offer")
|
|
236
|
-
*/
|
|
237
234
|
constructor(signalingUrl) {
|
|
238
235
|
this.signalingUrl = signalingUrl;
|
|
239
236
|
}
|
|
@@ -241,18 +238,14 @@ var WebRTCAdapter = class {
|
|
|
241
238
|
if (typeof globalThis.RTCPeerConnection === "undefined") {
|
|
242
239
|
throw new Error("RTCPeerConnection not available");
|
|
243
240
|
}
|
|
244
|
-
this.pc = new RTCPeerConnection({
|
|
245
|
-
iceServers: []
|
|
246
|
-
// Local network, no STUN/TURN
|
|
247
|
-
});
|
|
241
|
+
this.pc = new RTCPeerConnection({ iceServers: [] });
|
|
248
242
|
this.pc.ondatachannel = (event) => {
|
|
249
243
|
const channel = event.channel;
|
|
250
244
|
if (channel.label === "spatial") {
|
|
251
245
|
channel.binaryType = "arraybuffer";
|
|
252
246
|
channel.onmessage = (e) => {
|
|
253
247
|
if (e.data instanceof ArrayBuffer && e.data.byteLength >= 20) {
|
|
254
|
-
|
|
255
|
-
this.onSpatialData?.(decoded);
|
|
248
|
+
this.onSpatialData?.(decodeBinaryFrame(e.data));
|
|
256
249
|
}
|
|
257
250
|
};
|
|
258
251
|
} else if (channel.label === "reliable") {
|
|
@@ -276,23 +269,16 @@ var WebRTCAdapter = class {
|
|
|
276
269
|
const offer = await this.pc.createOffer();
|
|
277
270
|
await this.pc.setLocalDescription(offer);
|
|
278
271
|
await this.waitForICE();
|
|
279
|
-
const
|
|
280
|
-
method: "POST",
|
|
281
|
-
body: this.pc.localDescription.sdp,
|
|
282
|
-
headers: { "Content-Type": "application/sdp" }
|
|
283
|
-
});
|
|
284
|
-
if (!response.ok) {
|
|
285
|
-
throw new Error(`Signaling failed: ${response.status}`);
|
|
286
|
-
}
|
|
287
|
-
const answerSdp = await response.text();
|
|
272
|
+
const answerSdp = await this.exchangeSDP(this.pc.localDescription.sdp);
|
|
288
273
|
await this.pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
|
|
289
274
|
await new Promise((resolve, reject) => {
|
|
290
275
|
const timeout = setTimeout(() => reject(new Error("WebRTC connection timeout")), 1e4);
|
|
291
276
|
const check = () => {
|
|
292
|
-
|
|
277
|
+
const state = this.pc?.connectionState;
|
|
278
|
+
if (state === "connected") {
|
|
293
279
|
clearTimeout(timeout);
|
|
294
280
|
resolve();
|
|
295
|
-
} else if (
|
|
281
|
+
} else if (state === "failed") {
|
|
296
282
|
clearTimeout(timeout);
|
|
297
283
|
reject(new Error("WebRTC connection failed"));
|
|
298
284
|
}
|
|
@@ -300,9 +286,7 @@ var WebRTCAdapter = class {
|
|
|
300
286
|
this.pc.onconnectionstatechange = () => {
|
|
301
287
|
check();
|
|
302
288
|
const state = this.pc?.connectionState;
|
|
303
|
-
if (state === "disconnected" || state === "failed" || state === "closed")
|
|
304
|
-
this.onClose?.();
|
|
305
|
-
}
|
|
289
|
+
if (state === "disconnected" || state === "failed" || state === "closed") this.onClose?.();
|
|
306
290
|
};
|
|
307
291
|
check();
|
|
308
292
|
});
|
|
@@ -314,6 +298,44 @@ var WebRTCAdapter = class {
|
|
|
314
298
|
}
|
|
315
299
|
this.pc = null;
|
|
316
300
|
}
|
|
301
|
+
/** Exchange SDP: try fetch POST, fall back to popup window */
|
|
302
|
+
async exchangeSDP(offerSdp) {
|
|
303
|
+
try {
|
|
304
|
+
const res = await fetch(this.signalingUrl, {
|
|
305
|
+
method: "POST",
|
|
306
|
+
body: offerSdp,
|
|
307
|
+
headers: { "Content-Type": "application/sdp" }
|
|
308
|
+
});
|
|
309
|
+
if (res.ok) return await res.text();
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
return this.exchangeSDPViaPopup(offerSdp);
|
|
313
|
+
}
|
|
314
|
+
exchangeSDPViaPopup(offerSdp) {
|
|
315
|
+
return new Promise((resolve, reject) => {
|
|
316
|
+
const offerB64 = btoa(offerSdp);
|
|
317
|
+
const baseUrl = this.signalingUrl.replace("/rtc/offer", "/rtc/connect-popup");
|
|
318
|
+
const url = `${baseUrl}?offer=${encodeURIComponent(offerB64)}`;
|
|
319
|
+
const popup = globalThis.open(url, "satmouse-rtc", "width=1,height=1,left=-100,top=-100");
|
|
320
|
+
if (!popup) {
|
|
321
|
+
reject(new Error("Popup blocked \u2014 user interaction required"));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const timeout = setTimeout(() => {
|
|
325
|
+
popup.close();
|
|
326
|
+
reject(new Error("WebRTC signaling timeout"));
|
|
327
|
+
}, 1e4);
|
|
328
|
+
const onMessage = (event) => {
|
|
329
|
+
if (event.data?.type === "satmouse-rtc-answer") {
|
|
330
|
+
clearTimeout(timeout);
|
|
331
|
+
globalThis.removeEventListener("message", onMessage);
|
|
332
|
+
popup.close();
|
|
333
|
+
resolve(event.data.answer);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
globalThis.addEventListener("message", onMessage);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
317
339
|
waitForICE() {
|
|
318
340
|
return new Promise((resolve) => {
|
|
319
341
|
if (!this.pc) return resolve();
|
|
@@ -474,12 +496,18 @@ var SatMouseConnection = class extends TypedEmitter {
|
|
|
474
496
|
if (!this.intentionalClose) this.scheduleReconnect();
|
|
475
497
|
};
|
|
476
498
|
try {
|
|
477
|
-
await
|
|
499
|
+
await Promise.race([
|
|
500
|
+
adapter.connect(),
|
|
501
|
+
new Promise(
|
|
502
|
+
(_, reject) => setTimeout(() => reject(new Error(`${adapter.protocol} connection timeout`)), 5e3)
|
|
503
|
+
)
|
|
504
|
+
]);
|
|
478
505
|
this.transport = adapter;
|
|
479
506
|
this.retryCount = 0;
|
|
480
507
|
this.setState("connected", adapter.protocol);
|
|
481
508
|
return true;
|
|
482
509
|
} catch (err) {
|
|
510
|
+
adapter.close();
|
|
483
511
|
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
484
512
|
return false;
|
|
485
513
|
}
|