123456789101112import { createCapacitorSyncTransport } from "./capacitor-sync-transport";13import { encodeSyncMessage, decodeSyncMessage } from "./sync-wire";14import type { DiscoveredPeer, SyncMessage } from "./sync-types";1516const PAIR_RESPONSE_TIMEOUT_MS = 30_000;1718export interface CapacitorPairingResult {19 publicKeyHex: string;20 deviceName: string;21}2223export async function runCapacitorPairingHandshake(24 peer: DiscoveredPeer,25 ourPrivateKey: Uint8Array,26 ourPublicKey: Uint8Array,27 ourPublicKeyHex: string,28 ourDeviceName: string,29): Promise<CapacitorPairingResult> {30 const transport = await createCapacitorSyncTransport(31 peer,32 ourPrivateKey,33 ourPublicKey,34 );3536 try {37 const responsePromise = new Promise<SyncMessage>((resolve, reject) => {38 const timer = setTimeout(() => {39 cleanup();40 reject(new Error("pairing response timeout"));41 }, PAIR_RESPONSE_TIMEOUT_MS);4243 const offFrame = transport.onFrame((frame) => {44 try {45 const message = decodeSyncMessage(frame);46 cleanup();47 resolve(message);48 } catch (err) {49 cleanup();50 reject(err instanceof Error ? err : new Error(String(err)));51 }52 });53 const offError = transport.onError((message) => {54 cleanup();55 reject(new Error(message));56 });57 const offClose = transport.onClose(() => {58 cleanup();59 reject(new Error("connection closed before pairing response"));60 });6162 function cleanup() {63 clearTimeout(timer);64 offFrame();65 offError();66 offClose();67 }68 });6970 await transport.send(71 encodeSyncMessage({72 type: "PAIR_REQUEST",73 public_key_hex: ourPublicKeyHex,74 device_name: ourDeviceName,75 }),76 );7778 const response = await responsePromise;79 if (response.type === "PAIR_ACCEPT") {80 return {81 publicKeyHex: response.public_key_hex,82 deviceName: response.device_name,83 };84 }85 if (response.type === "PAIR_REJECT") {86 throw new Error(`pair_rejected:${response.reason}`);87 }88 throw new Error(`unexpected_response:${response.type}`);89 } finally {90 try {91 transport.close();92 } catch {93 94 }95 }96}