@robota-sdk/agent-transport-webrtc 3.0.0-beta.81 → 3.0.0-beta.82
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/node/index.cjs +1 -1
- package/dist/node/index.d.cts +935 -34
- package/dist/node/index.d.cts.map +1 -1
- package/dist/node/index.d.ts +935 -34
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +13 -10
- package/src/__tests__/device-mesh.test.ts +534 -0
- package/src/__tests__/fake-datachannel.ts +80 -0
- package/src/__tests__/mesh-discovery.test.ts +724 -0
- package/src/__tests__/mesh-fixtures.ts +146 -0
- package/src/__tests__/mesh-internet.test.ts +784 -0
- package/src/__tests__/negotiated-certificate.test.ts +62 -66
- package/src/__tests__/outbound-delivery-carrier.test.ts +5 -4
- package/src/__tests__/pairing-e2e.test.ts +16 -19
- package/src/__tests__/pairing-gate.test.ts +22 -4
- package/src/__tests__/pairing-relay-in-the-middle.test.ts +76 -82
- package/src/__tests__/webrtc-implementation.test.ts +81 -0
- package/src/__tests__/webrtc-transport.test.ts +67 -109
- package/src/__tests__/ws-mesh-relay-client.test.ts +95 -0
- package/src/datachannel-loader.ts +86 -0
- package/src/device-mesh-node.ts +733 -0
- package/src/discovering-mesh-relay.ts +617 -0
- package/src/index.ts +89 -3
- package/src/mainline-dht-store.ts +174 -0
- package/src/mesh-dht.ts +329 -0
- package/src/mesh-discovery.ts +87 -0
- package/src/mesh-item-store.ts +287 -0
- package/src/mesh-lan-listener.ts +193 -0
- package/src/mesh-mdns.ts +386 -0
- package/src/mesh-peer-link.ts +588 -0
- package/src/mesh-public-relays.ts +25 -0
- package/src/mesh-records.ts +310 -0
- package/src/mesh-relay.ts +98 -0
- package/src/mesh-signal.ts +116 -0
- package/src/negotiated-certificate.ts +41 -49
- package/src/nostr-mesh-relay.ts +391 -0
- package/src/pairing-gate-options.ts +1 -1
- package/src/pairing-gate.ts +25 -4
- package/src/rtc-peer.ts +391 -0
- package/src/webrtc-delivery-lifecycle.ts +5 -2
- package/src/webrtc-transport-options.ts +8 -11
- package/src/webrtc-transport.ts +103 -72
- package/src/ws-mesh-relay-client.ts +117 -0
- package/src/ws-signaling-client.ts +1 -1
- package/src/__tests__/cve-2024-29415-reachability.test.ts +0 -80
- package/src/__tests__/dtls-fingerprint-binding.test.ts +0 -60
- package/src/__tests__/werift-loader.test.ts +0 -18
- package/src/werift-loader.ts +0 -40
package/dist/node/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("@robota-sdk/agent-transport"),l=require("@robota-sdk/agent-transport/node"),u=require("@robota-sdk/agent-remote-pairing"),d=require("node:crypto"),f=require("node:module"),p=require("ws");p=s(p,1);function m(e,t){return(0,c.createOutboundDelivery)(t=>e.send(JSON.stringify(t)),(e,n)=>t(e,n),()=>e.bufferedAmount)}const h={"sha-256":`sha256`,"sha-384":`sha384`,"sha-512":`sha512`};function g(e,t){let n=h[t.toLowerCase()];if(!n)throw Error(`unsupported DTLS fingerprint algorithm: ${t}`);return(0,d.createHash)(n).update(e).digest(`hex`).toUpperCase().match(/../g)?.join(`:`)??``}function _(e,t,n,r){let i=e.sctpTransport?.dtlsTransport;if(!i)return r(`no DTLS transport for the data channel`),()=>void 0;let a=!1,o=()=>{if(a)return;let e=i.dtls?.remoteCertificate;if(a=!0,!e){r(`the DTLS layer exposed no remote certificate`);return}try{n(g(e,t))}catch(e){r(e instanceof Error?e.message:String(e))}};if(i.state===`connected`)return o(),()=>void 0;if(i.state===`failed`||i.state===`closed`)return r(`the DTLS handshake ended ${i.state}`),()=>void 0;let s=i.onStateChange.subscribe(e=>{if(e===`connected`)o();else if(e===`failed`||e===`closed`){if(a)return;a=!0,r(`the DTLS handshake ended ${e}`)}else return;s.unSubscribe()});return()=>s.unSubscribe()}const v=e=>(0,f.createRequire)(require("url").pathToFileURL(__filename).href)(e);function y(e=v){try{return e(`werift`)}catch{throw Error(`WebRTC transport unavailable — install the optional peer dependency "werift" to use @robota-sdk/agent-transport-webrtc.`)}}function b(e,t){let n=t===`operator-approval`;return e.localPeer!==void 0&&t!==`local-proof`&&t!==`handoff-grant`&&!n?`local-proof`:e.handoffGrant!==void 0&&t!==`handoff-grant`&&!n?`handoff-grant`:e.connectionApproval!==void 0&&!n?`operator-approval`:null}function x(e){return typeof e==`object`&&!!e&&e.t===`handoff-grant`&&e.grant!==void 0}function S(e){return{admitted:!1,trust:`unproven`,reason:e}}async function C(e,t){if(t===void 0)return S(`no hand-off grant verifier is configured for this channel`);if(!x(e))return S(`expected a handoff-grant frame carrying the signed grant`);try{let n=await t.verify(e.grant);return n.admitted&&n.trust===`same-user-same-host`?S(`the grant verifier returned same-user-same-host. A cross-device grant proves the user, not the machine — see SEC-010 for what same-host requires.`):!n.admitted||t.consent===void 0||await t.consent(n)?n:S(`the person at this machine declined the transfer`)}catch(e){return S(`the grant verifier could not decide: ${e instanceof Error?e.message:`unknown error`}`)}}function w(e){return{t:`handoff-grant`,grant:e}}function T(e){return typeof e==`object`&&!!e&&e.t===`local-proof`&&typeof e.nonce==`string`}function E(e){return{admitted:!1,trust:`unproven`,reason:e}}function D(e,t){if(t===void 0)return E(`no local-peer proof is configured for this channel`);if(!T(e))return E(`expected a local-proof frame carrying the rendezvous nonce`);try{return t.redeem(e.nonce)}catch(e){return E(`the rendezvous ledger could not decide: ${e instanceof Error?e.message:`unknown error`}`)}}function O(e){return{t:`local-proof`,nonce:e}}function k(e,t){try{e.send(t)}catch{}}function A(e){try{e.close()}catch{}}function j(e,t,n){try{e?.(t,n)}catch{}}const M=Object.freeze({send:k,close:A,reportDeliveryError:j});function N(e,t,n,r,i,a=u.startPairingHandshake){let o=a({secret:t,role:n,localFingerprint:e.localFingerprint,remoteFingerprint:e.remoteFingerprint,send:t=>M.send(e.channel,JSON.stringify(t)),...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}});return o.result.then(r,i),o}function P(e,t,n,r){let i=(0,u.startHostReconnect)({hostIdentityId:t.hostIdentityId,localFingerprint:e.localFingerprint,remoteFingerprint:e.remoteFingerprint,hostPrivateKey:t.hostPrivateKey,resolveDevicePublicKey:t.resolveDevicePublicKey,send:t=>M.send(e.channel,JSON.stringify(t)),...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}});return i.result.then(n,r),i}function F(e){return(0,u.decodePairingFrame)(e).ok}function I(e){return(0,u.decodeReconnectFrame)(e).ok}function L(e){return(0,u.decodeEnrollFrame)(e).ok}function R(e,t,n){let r=e.resumeBridge;if(r)return r.attach(t=>e.channel.send(t),{awaitResume:t,onDeliveryError:n,pendingBytes:()=>e.channel.bufferedAmount}),{onSessionMessage:e=>r.onClientMessage(e),cleanup:()=>r.detach()};let{onMessage:i,cleanup:a}=(e.createHandler??c.createSessionMessageHandler)({session:e.session,deliver:m(e.channel,n),...e.personalUsageReporter?{personalUsageReporter:e.personalUsageReporter}:{},...e.usageReporter?{usageReporter:e.usageReporter}:{},...e.storedSessionUsageReporter?{storedSessionUsageReporter:e.storedSessionUsageReporter}:{},...e.surface?{surface:e.surface}:{}});return{onSessionMessage:i,cleanup:a}}var z=class{options;state;onSessionMessage;handlerCleanup;pairingController;reconnectController;pendingResult;pendingViaReconnect=!1;pendingDeviceId;pendingEnrollment;heldFrames=[];heldChars=0;approvalAbort;constructor(e){this.options=e,e.reconnect?this.state=`awaiting-mode`:(this.state=`pairing`,this.startFirstPair())}onInbound(e){if(this.state===`closed`)return;if(this.state===`accepted`){this.onSessionMessage?.(e);return}if(this.state===`operator-approval`){this.holdForApproval(e);return}let t;try{t=JSON.parse(e)}catch{return}if(this.state===`awaiting-mode`){I(t)&&t.t===`rc-hello`?(this.state=`reconnecting`,this.startReconnect(),this.reconnectController?.onFrame(t)):F(t)&&t.t===`pair-nonce`&&(this.state=`pairing`,this.startFirstPair(),this.pairingController?.onFrame(t));return}if(this.state===`pairing`){F(t)&&this.pairingController?.onFrame(t);return}if(this.state===`reconnecting`){I(t)&&this.reconnectController?.onFrame(t);return}if(this.state===`local-proof`){let e=D(t,this.options.localPeer);this.options.localPeer?.onAdmission?.(e),e.admitted?this.accept(this.pendingResult,this.pendingViaReconnect):this.rejectAndClose();return}if(this.state===`handoff-grant`){this.judgeGrantFrame(t);return}if(this.state===`enrolling`){L(t)&&this.completeEnrollment(t.spki);return}}cleanup(){this.state=`closed`,this.withdrawApproval(),this.handlerCleanup?.(),this.handlerCleanup=void 0,this.onSessionMessage=void 0}onChannelClosed(){this.state!==`accepted`&&this.state!==`closed`&&this.rejectAndClose()}withdrawApproval(){this.heldFrames=[],this.heldChars=0,this.approvalAbort?.abort(),this.approvalAbort=void 0}startFirstPair(){this.pairingController=N(this.options,this.options.secret,this.options.role,e=>this.onFirstPairAccepted(e),()=>this.rejectAndClose(),this.options.startHandshake??u.startPairingHandshake)}startReconnect(){let e=this.options.reconnect;if(!e){this.rejectAndClose();return}this.reconnectController=P(this.options,e,e=>{this.pendingDeviceId=e.deviceId,this.accept(void 0,!0)},()=>this.rejectAndClose())}onFirstPairAccepted(e){if(this.state!==`pairing`)return;this.pendingResult=e;let t=this.options.reconnect;if(!t){this.accept(e);return}this.state=`enrolling`,M.send(this.options.channel,JSON.stringify({t:`enroll-key`,spki:t.hostPublicSpki}))}completeEnrollment(e){if(this.state===`enrolling`){if(!this.options.reconnect){this.rejectAndClose();return}(async()=>{try{await(0,u.importPublicKey)(e);let t=await(0,u.deriveIdentityId)(e);if(this.state!==`enrolling`)return;this.pendingDeviceId=t,this.pendingEnrollment={deviceId:t,spki:e},this.accept(this.pendingResult)}catch{this.rejectAndClose()}})()}}accept(e,t=!1){if(this.state===`closed`||this.state===`accepted`)return;let n=b(this.options,this.state);if(n!==null){this.pendingResult=e??this.pendingResult,this.pendingViaReconnect=t,this.state=n,n===`operator-approval`&&this.askOperator();return}let r=this.pendingEnrollment;if(this.pendingEnrollment=void 0,r!==void 0)try{this.options.reconnect?.onEnroll(r.deviceId,r.spki)}catch{this.rejectAndClose();return}let i=R(this.options,t,(e,t)=>this.handleSessionDeliveryError(e,t));this.onSessionMessage=i.onSessionMessage,this.handlerCleanup=i.cleanup,this.state=`accepted`,this.options.onAccept?.(e);let a=this.heldFrames;this.heldFrames=[],this.heldChars=0;for(let e of a){if(this.state!==`accepted`)return;this.onSessionMessage?.(e)}}holdForApproval(e){this.heldChars+=e.length,this.heldFrames.push(e),(this.heldFrames.length>32||this.heldChars>262144)&&this.rejectAndClose()}async askOperator(){let e=new AbortController;this.approvalAbort=e;let t={...this.pendingDeviceId===void 0?{}:{deviceId:this.pendingDeviceId},viaReconnect:this.pendingViaReconnect,signal:e.signal},n=!1;try{n=await this.options.connectionApproval?.approve(t)===!0}catch{n=!1}this.state!==`operator-approval`||e.signal.aborted||(this.approvalAbort=void 0,n?this.accept(this.pendingResult,this.pendingViaReconnect):this.rejectAndClose())}async judgeGrantFrame(e){let t=await C(e,this.options.handoffGrant);this.state===`handoff-grant`&&(this.options.handoffGrant?.onAdmission?.(t),t.admitted?this.accept(this.pendingResult,this.pendingViaReconnect):this.rejectAndClose())}rejectAndClose(){this.state!==`closed`&&(this.state=`closed`,this.withdrawApproval(),M.close(this.options.channel),this.options.onReject?.())}handleSessionDeliveryError(e,t){this.state!==`closed`&&(this.state=`closed`,this.handlerCleanup?.(),this.handlerCleanup=void 0,this.onSessionMessage=void 0,M.reportDeliveryError(this.options.onDeliveryError,e,t),M.close(this.options.channel))}};function B(e){return Object.assign(Error(`WebRtcTransport ${e}.`),{name:`TransportLifecycleError`,code:e,transportName:`webrtc`})}var V=class{options;generation=0;paired=!1;dropped=!1;constructor(e){this.options=e}reset(e){this.generation=e,this.paired=!1,this.dropped=!1}accept(e){e===this.generation&&(this.paired=!0)}handleDrop(e){e!==this.generation||!this.paired||this.dropped||(this.dropped=!0,this.options.cleanup(),this.options.onDropped())}handleFailure(e,t,n,r){if(t!==this.generation||this.dropped)return;let i=this.paired;i&&(this.dropped=!0);try{this.options.onDeliveryError(n,r)}catch{}this.options.cleanup();try{e.close()}catch{}i&&this.options.onDropped()}},H=class{options;name=`webrtc`;lifecycle=Object.freeze({kind:`service`});defaultEnabled=!1;optionsSchema={};session;peer;unsubscribeSignal;cleanupHandler;generation=0;localFingerprint;pairingGate;answered=!1;pendingFrames=[];stopAwaitingCertificate;deliveryLifecycle;constructor(e){if(this.options=e,this.deliveryLifecycle=new V({cleanup:()=>{this.cleanupHandler?.(),this.pairingGate?.cleanup()},onDropped:()=>this.options.onDropped?.(),onDeliveryError:(e,t)=>this.options.onDeliveryError?.(e,t)}),this.options.secret&&this.options.open===!0)throw Error("WebRtcTransport: `secret` and `open: true` are contradictory. A pairing secret gates the data channel; `open` runs without a gate. Pass one.");if(this.options.secret===void 0||this.options.secret===``){if(this.options.open!==!0)throw Error('WebRtcTransport: no pairing `secret` and no explicit `open`. Pass a `secret` to gate the data channel, or `{ open: true, openReason: "…" }` to run without pairing on purpose.');(0,l.resolveAdmission)({open:!0,...this.options.openReason===void 0?{}:{openReason:this.options.openReason}})}}validateOptions(){return!0}attach(e){this.session=e}createPeer(){let{RTCPeerConnection:e}=(this.options.loadWerift??y)(),t={};return this.options.iceServers&&(t.iceServers=this.options.iceServers.map(e=>({...e}))),this.options.forceTurn&&(t.iceTransportPolicy=`relay`),new e(Object.keys(t).length>0?t:void 0)}wireSignaling(e,t,n,r){let i=this.options.signaling;e.onIceCandidate.subscribe(t=>{t&&r===this.generation&&e===this.peer&&i.send({kind:`ice`,data:t.toJSON()})});let a=Promise.resolve();this.unsubscribeSignal=i.onSignal(i=>{r===this.generation&&(a=a.then(async()=>{if(r===this.generation&&e===this.peer)if(i.kind===`answer`){if(this.answered)return;this.answered=!0;let a=this.remoteFingerprintAlgorithm(t,i.data);if(this.options.secret&&a===void 0)return;await e.setRemoteDescription(i.data),a!==void 0&&this.awaitVerifiedCertificate(e,t,n,a,r)}else i.kind===`ice`&&await e.addIceCandidate(i.data)}))})}async requireCurrentPeer(e,t){if(t!==this.generation||this.peer!==e)throw await e.close(),Error(`WebRtcTransport startup was stopped.`)}async start(){let e=this.session;if(!e)throw B(`not-attached`);if(this.peer)throw B(`already-started`);let t=++this.generation;this.deliveryLifecycle.reset(t),this.answered=!1,this.pendingFrames=[];let n=this.createPeer();this.peer=n;let r=this.options.signaling,i=n.createDataChannel(`robota-session`);this.wireChannel(i,e,t),this.wireSignaling(n,i,e,t);let a=await n.createOffer();await this.requireCurrentPeer(n,t),await n.setLocalDescription(a),await this.requireCurrentPeer(n,t),this.options.secret&&n.localDescription&&(this.localFingerprint=(0,u.extractDtlsFingerprint)(n.localDescription.sdp)),r.send({kind:`offer`,data:n.localDescription})}remoteFingerprintAlgorithm(e,t){if(!this.options.secret)return;let n=t.sdp;try{if(typeof n!=`string`)throw Error(`answer carries no SDP`);return(0,u.extractDtlsFingerprintAttribute)(n).algorithm}catch{this.failPairing(e);return}}failPairing(e){try{e.close()}catch{}this.options.onPairingFailed?.()}awaitVerifiedCertificate(e,t,n,r,i){this.stopAwaitingCertificate=_(e,r,e=>{i===this.generation&&this.startPairing(t,n,e,i)},()=>{i===this.generation&&this.failPairing(t)})}startPairing(e,t,n,r){let i=this.options.secret;if(!i||!this.localFingerprint)return;this.pairingGate=new z({channel:{send:t=>e.send(t),close:()=>void e.close()},session:t,secret:i,role:`initiator`,localFingerprint:this.localFingerprint,remoteFingerprint:n,onAccept:e=>{r===this.generation&&(this.deliveryLifecycle.accept(r),this.options.onPaired?.(e))},...this.options.onPairingFailed?{onReject:this.options.onPairingFailed}:{},...this.options.reconnect?{reconnect:this.options.reconnect}:{},...this.options.localPeer?{localPeer:this.options.localPeer}:{},...this.options.connectionApproval?{connectionApproval:this.options.connectionApproval}:{},...this.options.resumeBridge?{resumeBridge:this.options.resumeBridge}:{},...this.options.personalUsageReporter?{personalUsageReporter:this.options.personalUsageReporter}:{},...this.options.usageReporter?{usageReporter:this.options.usageReporter}:{},...this.options.storedSessionUsageReporter?{storedSessionUsageReporter:this.options.storedSessionUsageReporter}:{},surface:`remote`,onDeliveryError:(t,n)=>this.deliveryLifecycle.handleFailure(e,r,t,n)});let a=this.pairingGate;for(let e of this.pendingFrames.splice(0))a.onInbound(e)}wireChannel(e,t,n){if(this.options.secret){e.onMessage.subscribe(e=>{if(n!==this.generation)return;let t=typeof e==`string`?e:e.toString();this.pairingGate?this.pairingGate.onInbound(t):this.pendingFrames.length<16&&this.pendingFrames.push(t)}),e.stateChanged.subscribe(e=>{n===this.generation&&(e===`closed`||e===`closing`)&&(this.pairingGate?.onChannelClosed(),this.deliveryLifecycle.handleDrop(n))}),this.cleanupHandler=()=>this.pairingGate?.cleanup();return}let{onMessage:r,cleanup:i}=(0,c.createSessionMessageHandler)({session:t,deliver:m(e,(t,r)=>this.deliveryLifecycle.handleFailure(e,n,t,r)),...this.options.personalUsageReporter?{personalUsageReporter:this.options.personalUsageReporter}:{},...this.options.usageReporter?{usageReporter:this.options.usageReporter}:{},...this.options.storedSessionUsageReporter?{storedSessionUsageReporter:this.options.storedSessionUsageReporter}:{},surface:`remote`});this.cleanupHandler=i,e.onMessage.subscribe(e=>{n===this.generation&&r(typeof e==`string`?e:e.toString())})}async stop(){this.generation+=1,this.cleanupHandler?.(),this.unsubscribeSignal?.(),this.cleanupHandler=void 0,this.unsubscribeSignal=void 0,this.pairingGate=void 0,this.localFingerprint=void 0,this.answered=!1,this.pendingFrames=[],this.stopAwaitingCertificate?.(),this.stopAwaitingCertificate=void 0,this.deliveryLifecycle.reset(this.generation),this.peer&&=(await this.peer.close(),void 0),this.session=void 0}};function U(){let e=[],t=[],n=!0;function r(e,t){return{send(e){n&&queueMicrotask(()=>{for(let n of t)n(e)})},onSignal(t){return e.push(t),()=>{let n=e.indexOf(t);n>=0&&e.splice(n,1)}},close(){n=!1,e.length=0}}}return[r(e,t),r(t,e)]}const W=new Set([`offer`,`answer`,`ice`]);function G(e){return new p.default(e)}var K=class{options;socket;handlers=[];outbox=[];joined=!1;closed=!1;constructor(e){this.options=e;let t=e.createSocket??G;this.socket=t(e.url),this.socket.on(`open`,()=>this.handleOpen()),this.socket.on(`message`,e=>this.handleMessage(e)),this.socket.on(`error`,e=>this.fail(e instanceof Error?e:Error(String(e)))),this.socket.on(`close`,()=>this.handleClose())}handleOpen(){this.socket.send(JSON.stringify({type:`join`,rendezvous:this.options.rendezvous}));for(let e of this.outbox)this.writeSignal(e);this.outbox.length=0}handleMessage(e){let t;try{t=JSON.parse(typeof e==`string`?e:String(e))}catch{return}if(typeof t!=`object`||!t)return;let n=t;if(n.type===`joined`){this.joined=!0,this.options.onReady?.();return}if(n.type===`error`){this.fail(Error(`signaling relay rejected the connection: ${String(n.reason)}`));return}if(n.type===`signal`&&typeof n.kind==`string`&&W.has(n.kind)){let e={kind:n.kind,data:n.data};for(let t of this.handlers)t(e)}}handleClose(){this.closed||this.joined||this.fail(Error(`signaling socket closed before the rendezvous was joined`))}fail(e){this.options.onError?.(e)}writeSignal(e){this.socket.send(JSON.stringify({type:`signal`,kind:e.kind,data:e.data}))}send(e){this.closed||(this.socket.readyState===1?this.writeSignal(e):this.outbox.push(e))}onSignal(e){return this.handlers.push(e),()=>{let t=this.handlers.indexOf(e);t>=0&&this.handlers.splice(t,1)}}close(){this.closed=!0,this.handlers.length=0,this.outbox.length=0,this.socket.close()}};exports.WebRtcTransport=H,exports.WsSignalingClient=K,exports.createInMemorySignalingPair=U,exports.handoffGrantFrame=w,exports.loadWerift=y,exports.localProofFrame=O;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("@robota-sdk/agent-transport"),l=require("@robota-sdk/agent-transport/node"),u=require("@robota-sdk/agent-remote-pairing"),d=require("node:module"),f=require("ws");f=s(f,1);let ee=require("@robota-sdk/agent-interface-session-mobility"),p=require("node:crypto"),te=require("node:os"),ne=require("nostr-tools/pool"),m=require("nostr-tools/pure");function re(e,t){return(0,c.createOutboundDelivery)(t=>e.send(JSON.stringify(t)),(e,n)=>t(e,n),()=>e.bufferedAmount)}const ie=/^[0-9A-F]{2}(?::[0-9A-F]{2})+$/;function h(e,t,n,r){let i=!1,a=()=>{if(i)return;i=!0;let a=e.remoteFingerprint();if(a===void 0){r(`the DTLS layer exposed no remote certificate`);return}let o=a.value.toUpperCase();if(a.algorithm.toLowerCase()!==t.toLowerCase()){r(`the remote certificate was checked with ${a.algorithm}, not ${t}`);return}if(!ie.test(o)){r(`the remote certificate fingerprint is malformed`);return}n(o)},o=e=>{i||(i=!0,r(`the connection ended ${e} before DTLS completed`))};if(e.state===`connected`)return a(),()=>void 0;if(e.state===`failed`||e.state===`closed`)return o(e.state),()=>void 0;let s=e.onStateChange(e=>{if(e===`connected`)a();else if(e===`failed`||e===`closed`)o(e);else return;s()});return s}const ae=e=>(0,d.createRequire)(require("url").pathToFileURL(__filename).href)(e);function g(e=ae){try{let t=e(`node-datachannel`);if(typeof t?.PeerConnection!=`function`)throw Error(`no PeerConnection export`);return t}catch{throw Error(`WebRTC transport unavailable — the optional dependency "node-datachannel" is not installed or has no prebuilt binary for this platform (${process.platform}-${process.arch}).`)}}function _(e){try{return e?.()}catch{return}}function v(e){return e instanceof Error?e.message:String(e)}const oe={stun:3478,turn:3478,turns:5349};function se(e){let t=/^(stuns?|turns?):([^:?]+|\[[^\]]+\])(?::(\d{1,5}))?(?:\?transport=(udp|tcp))?$/i.exec(e.urls);if(!t)throw Error(`unsupported ICE server url: ${e.urls}`);let n=t[1].toLowerCase(),r=t[2].replace(/^\[|\]$/g,``),i=t[3]===void 0?oe[n]??3478:Number(t[3]);return n===`stun`||n===`stuns`?`stun:${r}:${i}`:{hostname:r,port:i,...e.username===void 0?{}:{username:e.username},...e.credential===void 0?{}:{password:e.credential},relayType:n===`turns`?`TurnTls`:t[4]?.toLowerCase()===`tcp`?`TurnTcp`:`TurnUdp`}}function ce(e){return typeof e==`string`?e:Buffer.from(e).toString(`utf8`)}var y=class{native;fault;stateValue;closeCauseValue;messageHandlers=new Set;stateHandlers=new Set;constructor(e,t){this.native=e,this.fault=t,this.stateValue=e.isOpen()?`open`:`connecting`,e.onOpen(()=>this.guard(()=>this.setState(`open`))),e.onClosed(()=>this.guard(()=>this.closedWith(`closed by the peer or the transport`))),e.onError(e=>this.guard(()=>this.closedWith(`error: ${e}`))),e.onMessage(e=>this.guard(()=>{let t=ce(e);for(let e of this.messageHandlers)e(t)}))}guard(e){try{e()}catch(e){this.closeCauseValue??=`handler error: ${v(e)}`,this.fault(e)}}closedWith(e){this.closeCauseValue??=e,this.setState(`closed`)}get closeCause(){return this.closeCauseValue}setState(e){if(this.stateValue!==e&&this.stateValue!==`closed`){this.stateValue=e;for(let t of this.stateHandlers)t(e)}}get readyState(){return this.stateValue}get label(){try{return this.native.getLabel?.()??``}catch{return``}}get bufferedAmount(){try{return this.native.bufferedAmount()}catch{return 0}}send(e){if(this.stateValue!==`open`)throw Error(`data channel is not open`);this.native.sendMessage(e)}onMessage(e){return this.messageHandlers.add(e),()=>this.messageHandlers.delete(e)}onStateChange(e){return this.stateHandlers.add(e),()=>this.stateHandlers.delete(e)}close(){if(this.stateValue!==`closed`){try{this.native.close()}catch{}this.closedWith(`closed by this side`)}}},b=class{native;stateValue=`new`;stateHandlers=new Set;candidateHandlers=new Set;channelHandlers=new Set;descriptionWaiters=[];hasRemoteDescription=!1;history=[];closedBy;handlerError;localCandidateCount=0;remoteCandidateCount=0;earlyCandidates=[];closed=!1;constructor(e={}){let t=(e.loadDataChannel??g)();this.native=new t.PeerConnection(`robota`,{iceServers:(e.iceServers??[]).map(se),...e.forceTurn===!0?{iceTransportPolicy:`relay`}:{},disableAutoNegotiation:!0}),this.native.onStateChange(e=>this.guard(()=>{if(e!==this.stateValue&&this.stateValue!==`closed`){this.stateValue=e,this.remember(this.stateValue);for(let e of this.stateHandlers)e(this.stateValue)}})),this.native.onLocalCandidate((e,t)=>this.guard(()=>{let n=e.startsWith(`a=`)?e.slice(2):e;if(n.length!==0){this.localCandidateCount+=1;for(let e of this.candidateHandlers)e({candidate:n,mid:t})}})),this.native.onLocalDescription(e=>this.guard(()=>{for(let t of this.descriptionWaiters.splice(0))t(e)})),this.native.onDataChannel(e=>this.guard(()=>{let t=new y(e,e=>this.fault(e));for(let e of this.channelHandlers)e(t)}))}guard(e){try{e()}catch(e){this.fault(e)}}fault(e){this.closed||(this.closedBy=`handler-error`,this.handlerError=v(e),this.close())}remember(e){this.history.length<16&&this.history.push(e)}diagnostics(){let e=_(this.native.iceState?.bind(this.native)),t=_(this.native.gatheringState?.bind(this.native)),n=_(this.native.signalingState?.bind(this.native));return{state:this.stateValue,...e===void 0?{}:{ice:e},...t===void 0?{}:{gathering:t},...n===void 0?{}:{signaling:n},history:[...this.history],...this.closedBy===void 0?{}:{closedBy:this.closedBy},...this.handlerError===void 0?{}:{handlerError:this.handlerError},localCandidates:this.localCandidateCount,remoteCandidates:this.remoteCandidateCount}}get state(){return this.stateValue}createDataChannel(e){return new y(this.native.createDataChannel(e),e=>this.fault(e))}localDescription(e){return new Promise((t,n)=>{this.descriptionWaiters.push(t);try{this.native.setLocalDescription(e)}catch(e){let r=this.descriptionWaiters.indexOf(t);r>=0&&this.descriptionWaiters.splice(r,1),n(e instanceof Error?e:Error(String(e)))}})}createOffer(){return this.localDescription(`offer`)}async acceptOffer(e){return this.native.setRemoteDescription(e,`offer`),this.remoteDescriptionApplied(),this.localDescription(`answer`)}acceptAnswer(e){this.native.setRemoteDescription(e,`answer`),this.remoteDescriptionApplied()}remoteDescriptionApplied(){this.hasRemoteDescription=!0;for(let e of this.earlyCandidates.splice(0))this.addRemoteCandidate(e)}addRemoteCandidate(e){if(!this.closed){if(!this.hasRemoteDescription){this.earlyCandidates.length<64&&this.earlyCandidates.push(e);return}try{this.native.addRemoteCandidate(e.candidate,e.mid),this.remoteCandidateCount+=1}catch{}}}remoteFingerprint(){if(this.stateValue===`connected`)try{let e=this.native.remoteFingerprint();return e.value.length>0?e:void 0}catch{return}}onStateChange(e){return this.stateHandlers.add(e),()=>this.stateHandlers.delete(e)}onLocalCandidate(e){return this.candidateHandlers.add(e),()=>this.candidateHandlers.delete(e)}onDataChannel(e){return this.channelHandlers.add(e),()=>this.channelHandlers.delete(e)}close(){if(!this.closed){this.closed=!0,this.closedBy??=`local`;try{this.native.close()}catch{}if(this.stateValue!==`closed`){this.stateValue=`closed`,this.remember(`closed`);for(let e of this.stateHandlers)e(`closed`)}}}};function le(e,t){let n=t===`operator-approval`;return e.localPeer!==void 0&&t!==`local-proof`&&t!==`handoff-grant`&&!n?`local-proof`:e.handoffGrant!==void 0&&t!==`handoff-grant`&&!n?`handoff-grant`:e.connectionApproval!==void 0&&!n?`operator-approval`:null}function ue(e){return typeof e==`object`&&!!e&&e.t===`handoff-grant`&&e.grant!==void 0}function x(e){return{admitted:!1,trust:`unproven`,reason:e}}async function de(e,t){if(t===void 0)return x(`no hand-off grant verifier is configured for this channel`);if(!ue(e))return x(`expected a handoff-grant frame carrying the signed grant`);try{let n=await t.verify(e.grant);return n.admitted&&n.trust===`same-user-same-host`?x(`the grant verifier returned same-user-same-host. A cross-device grant proves the user, not the machine — see SEC-010 for what same-host requires.`):!n.admitted||t.consent===void 0||await t.consent(n)?n:x(`the person at this machine declined the transfer`)}catch(e){return x(`the grant verifier could not decide: ${e instanceof Error?e.message:`unknown error`}`)}}function fe(e){return{t:`handoff-grant`,grant:e}}function pe(e){return typeof e==`object`&&!!e&&e.t===`local-proof`&&typeof e.nonce==`string`}function S(e){return{admitted:!1,trust:`unproven`,reason:e}}function me(e,t){if(t===void 0)return S(`no local-peer proof is configured for this channel`);if(!pe(e))return S(`expected a local-proof frame carrying the rendezvous nonce`);try{return t.redeem(e.nonce)}catch(e){return S(`the rendezvous ledger could not decide: ${e instanceof Error?e.message:`unknown error`}`)}}function he(e){return{t:`local-proof`,nonce:e}}function ge(e,t){try{e.send(t)}catch{}}function _e(e){try{e.close()}catch{}}function ve(e,t,n){try{e?.(t,n)}catch{}}const C=Object.freeze({send:ge,close:_e,reportDeliveryError:ve});function ye(e,t,n,r,i,a=u.startPairingHandshake){let o=a({secret:t,role:n,localFingerprint:e.localFingerprint,remoteFingerprint:e.remoteFingerprint,send:t=>C.send(e.channel,JSON.stringify(t)),...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}});return o.result.then(r,i),o}function be(e,t,n,r){let i=(0,u.startHostReconnect)({hostIdentityId:t.hostIdentityId,localFingerprint:e.localFingerprint,remoteFingerprint:e.remoteFingerprint,hostPrivateKey:t.hostPrivateKey,resolveDevicePublicKey:t.resolveDevicePublicKey,send:t=>C.send(e.channel,JSON.stringify(t)),...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}});return i.result.then(n,r),i}function w(e){return(0,u.decodePairingFrame)(e).ok}function T(e){return(0,u.decodeReconnectFrame)(e).ok}function xe(e){return(0,u.decodeEnrollFrame)(e).ok}function Se(e,t,n){let r=e.resumeBridge;if(r)return r.attach(t=>e.channel.send(t),{awaitResume:t,onDeliveryError:n,pendingBytes:()=>e.channel.bufferedAmount}),{onSessionMessage:e=>r.onClientMessage(e),cleanup:()=>r.detach()};let{onMessage:i,cleanup:a}=(e.createHandler??c.createSessionMessageHandler)({session:e.session,deliver:re(e.channel,n),...e.personalUsageReporter?{personalUsageReporter:e.personalUsageReporter}:{},...e.usageReporter?{usageReporter:e.usageReporter}:{},...e.storedSessionUsageReporter?{storedSessionUsageReporter:e.storedSessionUsageReporter}:{},...e.surface?{surface:e.surface}:{}});return{onSessionMessage:i,cleanup:a}}var Ce=class{options;state;onSessionMessage;handlerCleanup;pairingController;reconnectController;pendingResult;pendingViaReconnect=!1;pendingDeviceId;pendingEnrollment;heldFrames=[];heldChars=0;approvalAbort;constructor(e){this.options=e,e.reconnect?this.state=`awaiting-mode`:(this.state=`pairing`,this.startFirstPair())}onInbound(e){if(this.state===`closed`)return;if(this.state===`accepted`){this.onSessionMessage?.(e);return}if(this.state===`operator-approval`){this.hold(e);return}let t;try{t=JSON.parse(e)}catch{return}if(this.state===`awaiting-mode`){T(t)&&t.t===`rc-hello`?(this.state=`reconnecting`,this.startReconnect(),this.reconnectController?.onFrame(t)):w(t)&&t.t===`pair-nonce`&&(this.state=`pairing`,this.startFirstPair(),this.pairingController?.onFrame(t));return}if(this.state===`pairing`){w(t)?this.pairingController?.onFrame(t):this.hold(e);return}if(this.state===`reconnecting`){T(t)?this.reconnectController?.onFrame(t):this.hold(e);return}if(this.state===`local-proof`){let e=me(t,this.options.localPeer);this.options.localPeer?.onAdmission?.(e),e.admitted?this.accept(this.pendingResult,this.pendingViaReconnect):this.rejectAndClose();return}if(this.state===`handoff-grant`){this.judgeGrantFrame(t);return}if(this.state===`enrolling`){xe(t)?this.completeEnrollment(t.spki):this.hold(e);return}}cleanup(){this.state=`closed`,this.withdrawApproval(),this.handlerCleanup?.(),this.handlerCleanup=void 0,this.onSessionMessage=void 0}onChannelClosed(){this.state!==`accepted`&&this.state!==`closed`&&this.rejectAndClose()}withdrawApproval(){this.heldFrames=[],this.heldChars=0,this.approvalAbort?.abort(),this.approvalAbort=void 0}startFirstPair(){this.pairingController=ye(this.options,this.options.secret,this.options.role,e=>this.onFirstPairAccepted(e),()=>this.rejectAndClose(),this.options.startHandshake??u.startPairingHandshake)}startReconnect(){let e=this.options.reconnect;if(!e){this.rejectAndClose();return}this.reconnectController=be(this.options,e,e=>{this.pendingDeviceId=e.deviceId,this.accept(void 0,!0)},()=>this.rejectAndClose())}onFirstPairAccepted(e){if(this.state!==`pairing`)return;this.pendingResult=e;let t=this.options.reconnect;if(!t){this.accept(e);return}this.state=`enrolling`,C.send(this.options.channel,JSON.stringify({t:`enroll-key`,spki:t.hostPublicSpki})),this.replayHeld()}completeEnrollment(e){if(this.state===`enrolling`){if(!this.options.reconnect){this.rejectAndClose();return}(async()=>{try{await(0,u.importPublicKey)(e);let t=await(0,u.deriveIdentityId)(e);if(this.state!==`enrolling`)return;this.pendingDeviceId=t,this.pendingEnrollment={deviceId:t,spki:e},this.accept(this.pendingResult)}catch{this.rejectAndClose()}})()}}accept(e,t=!1){if(this.state===`closed`||this.state===`accepted`)return;let n=le(this.options,this.state);if(n!==null){this.pendingResult=e??this.pendingResult,this.pendingViaReconnect=t,this.state=n,n===`operator-approval`&&this.askOperator(),this.replayHeld();return}let r=this.pendingEnrollment;if(this.pendingEnrollment=void 0,r!==void 0)try{this.options.reconnect?.onEnroll(r.deviceId,r.spki)}catch{this.rejectAndClose();return}let i=Se(this.options,t,(e,t)=>this.handleSessionDeliveryError(e,t));this.onSessionMessage=i.onSessionMessage,this.handlerCleanup=i.cleanup,this.state=`accepted`,this.options.onAccept?.(e);let a=this.heldFrames;this.heldFrames=[],this.heldChars=0;for(let e of a){if(this.state!==`accepted`)return;this.onSessionMessage?.(e)}}replayHeld(){let e=this.heldFrames;this.heldFrames=[],this.heldChars=0;for(let t of e){if(this.state===`closed`)return;this.onInbound(t)}}hold(e){this.heldChars+=e.length,this.heldFrames.push(e),(this.heldFrames.length>32||this.heldChars>262144)&&this.rejectAndClose()}async askOperator(){let e=new AbortController;this.approvalAbort=e;let t={...this.pendingDeviceId===void 0?{}:{deviceId:this.pendingDeviceId},viaReconnect:this.pendingViaReconnect,signal:e.signal},n=!1;try{n=await this.options.connectionApproval?.approve(t)===!0}catch{n=!1}this.state!==`operator-approval`||e.signal.aborted||(this.approvalAbort=void 0,n?this.accept(this.pendingResult,this.pendingViaReconnect):this.rejectAndClose())}async judgeGrantFrame(e){let t=await de(e,this.options.handoffGrant);this.state===`handoff-grant`&&(this.options.handoffGrant?.onAdmission?.(t),t.admitted?this.accept(this.pendingResult,this.pendingViaReconnect):this.rejectAndClose())}rejectAndClose(){this.state!==`closed`&&(this.state=`closed`,this.withdrawApproval(),C.close(this.options.channel),this.options.onReject?.())}handleSessionDeliveryError(e,t){this.state!==`closed`&&(this.state=`closed`,this.handlerCleanup?.(),this.handlerCleanup=void 0,this.onSessionMessage=void 0,C.reportDeliveryError(this.options.onDeliveryError,e,t),C.close(this.options.channel))}};function E(e){return Object.assign(Error(`WebRtcTransport ${e}.`),{name:`TransportLifecycleError`,code:e,transportName:`webrtc`})}var we=class{options;generation=0;paired=!1;dropped=!1;constructor(e){this.options=e}reset(e){this.generation=e,this.paired=!1,this.dropped=!1}accept(e){e===this.generation&&(this.paired=!0)}handleDrop(e){e!==this.generation||!this.paired||this.dropped||(this.dropped=!0,this.options.cleanup(),this.options.onDropped())}handleFailure(e,t,n,r){if(t!==this.generation||this.dropped)return;let i=this.paired;i&&(this.dropped=!0);try{this.options.onDeliveryError(n,r)}catch{}this.options.cleanup();try{e.close()}catch{}i&&this.options.onDropped()}},Te=class{options;name=`webrtc`;lifecycle=Object.freeze({kind:`service`});defaultEnabled=!1;optionsSchema={};session;peer;unsubscribeSignal;cleanupHandler;generation=0;localFingerprint;pairingGate;offerSent=!1;localCandidates=[];answered=!1;pendingFrames=[];stopAwaitingCertificate;deliveryLifecycle;constructor(e){if(this.options=e,this.deliveryLifecycle=new we({cleanup:()=>{this.cleanupHandler?.(),this.pairingGate?.cleanup()},onDropped:()=>this.options.onDropped?.(),onDeliveryError:(e,t)=>this.options.onDeliveryError?.(e,t)}),this.options.secret&&this.options.open===!0)throw Error("WebRtcTransport: `secret` and `open: true` are contradictory. A pairing secret gates the data channel; `open` runs without a gate. Pass one.");if(this.options.secret===void 0||this.options.secret===``){if(this.options.open!==!0)throw Error('WebRtcTransport: no pairing `secret` and no explicit `open`. Pass a `secret` to gate the data channel, or `{ open: true, openReason: "…" }` to run without pairing on purpose.');(0,l.resolveAdmission)({open:!0,...this.options.openReason===void 0?{}:{openReason:this.options.openReason}})}}validateOptions(){return!0}attach(e){this.session=e}createPeer(){return new b({...this.options.iceServers?{iceServers:this.options.iceServers}:{},...this.options.forceTurn?{forceTurn:!0}:{},...this.options.loadDataChannel?{loadDataChannel:this.options.loadDataChannel}:{}})}wireSignaling(e,t,n,r){let i=this.options.signaling;e.onLocalCandidate(t=>{r===this.generation&&e===this.peer&&(this.offerSent?this.sendCandidate(t):this.localCandidates.length<64&&this.localCandidates.push(t))});let a=Promise.resolve();this.unsubscribeSignal=i.onSignal(i=>{r===this.generation&&(a=a.then(async()=>{if(r===this.generation&&e===this.peer){if(i.kind===`answer`){if(this.answered)return;this.answered=!0;let a=this.remoteFingerprintAlgorithm(t,i.data);if(this.options.secret&&a===void 0)return;let o=i.data?.sdp;if(typeof o!=`string`)return;e.acceptAnswer(o),a!==void 0&&this.awaitVerifiedCertificate(e,t,n,a,r)}else if(i.kind===`ice`){let t=Ee(i.data);t!==void 0&&e.addRemoteCandidate(t)}}}).catch(()=>{r===this.generation&&this.failPairing(t)}))})}sendCandidate(e){this.options.signaling.send({kind:`ice`,data:{candidate:e.candidate,sdpMid:e.mid,sdpMLineIndex:0}})}requireCurrentPeer(e,t){if(t!==this.generation||this.peer!==e)throw e.close(),Error(`WebRtcTransport startup was stopped.`)}async start(){let e=this.session;if(!e)throw E(`not-attached`);if(this.peer)throw E(`already-started`);let t=++this.generation;this.deliveryLifecycle.reset(t),this.answered=!1,this.offerSent=!1,this.localCandidates=[],this.pendingFrames=[];let n=this.createPeer();this.peer=n;let r=this.options.signaling,i=n.createDataChannel(`robota-session`);this.wireChannel(i,e,t),this.wireSignaling(n,i,e,t);let a=await n.createOffer();this.requireCurrentPeer(n,t),this.options.secret&&(this.localFingerprint=(0,u.extractDtlsFingerprint)(a)),r.send({kind:`offer`,data:{type:`offer`,sdp:a}}),this.offerSent=!0;for(let e of this.localCandidates.splice(0))this.sendCandidate(e)}remoteFingerprintAlgorithm(e,t){if(!this.options.secret)return;let n=t.sdp;try{if(typeof n!=`string`)throw Error(`answer carries no SDP`);return(0,u.extractDtlsFingerprintAttribute)(n).algorithm}catch{this.failPairing(e);return}}failPairing(e){e.close(),this.options.onPairingFailed?.()}awaitVerifiedCertificate(e,t,n,r,i){this.stopAwaitingCertificate=h(e,r,e=>{if(i!==this.generation)return;if(t.readyState===`open`){this.startPairing(t,n,e,i);return}let r=t.onStateChange(a=>{a===`open`&&i===this.generation&&(r(),this.startPairing(t,n,e,i))})},()=>{i===this.generation&&this.failPairing(t)})}startPairing(e,t,n,r){let i=this.options.secret;if(!i||!this.localFingerprint)return;this.pairingGate=new Ce({channel:{send:t=>e.send(t),close:()=>e.close()},session:t,secret:i,role:`initiator`,localFingerprint:this.localFingerprint,remoteFingerprint:n,onAccept:e=>{r===this.generation&&(this.deliveryLifecycle.accept(r),this.options.onPaired?.(e))},...this.options.onPairingFailed?{onReject:this.options.onPairingFailed}:{},...this.options.reconnect?{reconnect:this.options.reconnect}:{},...this.options.localPeer?{localPeer:this.options.localPeer}:{},...this.options.connectionApproval?{connectionApproval:this.options.connectionApproval}:{},...this.options.resumeBridge?{resumeBridge:this.options.resumeBridge}:{},...this.options.personalUsageReporter?{personalUsageReporter:this.options.personalUsageReporter}:{},...this.options.usageReporter?{usageReporter:this.options.usageReporter}:{},...this.options.storedSessionUsageReporter?{storedSessionUsageReporter:this.options.storedSessionUsageReporter}:{},surface:`remote`,onDeliveryError:(t,n)=>this.deliveryLifecycle.handleFailure(e,r,t,n)});let a=this.pairingGate;for(let e of this.pendingFrames.splice(0))a.onInbound(e)}wireChannel(e,t,n){if(this.options.secret){e.onMessage(e=>{n===this.generation&&(this.pairingGate?this.pairingGate.onInbound(e):this.pendingFrames.length<16&&this.pendingFrames.push(e))}),e.onStateChange(e=>{n===this.generation&&e===`closed`&&(this.pairingGate?.onChannelClosed(),this.deliveryLifecycle.handleDrop(n))}),this.cleanupHandler=()=>this.pairingGate?.cleanup();return}let{onMessage:r,cleanup:i}=(0,c.createSessionMessageHandler)({session:t,deliver:re(e,(t,r)=>this.deliveryLifecycle.handleFailure(e,n,t,r)),...this.options.personalUsageReporter?{personalUsageReporter:this.options.personalUsageReporter}:{},...this.options.usageReporter?{usageReporter:this.options.usageReporter}:{},...this.options.storedSessionUsageReporter?{storedSessionUsageReporter:this.options.storedSessionUsageReporter}:{},surface:`remote`});this.cleanupHandler=i,e.onMessage(e=>{n===this.generation&&r(e)})}async stop(){this.generation+=1,this.cleanupHandler?.(),this.unsubscribeSignal?.(),this.cleanupHandler=void 0,this.unsubscribeSignal=void 0,this.pairingGate=void 0,this.localFingerprint=void 0,this.answered=!1,this.offerSent=!1,this.localCandidates=[],this.pendingFrames=[],this.stopAwaitingCertificate?.(),this.stopAwaitingCertificate=void 0,this.deliveryLifecycle.reset(this.generation),this.peer&&=(this.peer.close(),void 0),this.session=void 0}};function Ee(e){if(typeof e!=`object`||!e)return;let t=e;if(typeof t.candidate!=`string`||t.candidate.length===0)return;let n=typeof t.sdpMid==`string`?t.sdpMid:typeof t.sdpMLineIndex==`number`?String(t.sdpMLineIndex):`0`;return{candidate:t.candidate,mid:n}}function De(){let e=[],t=[],n=!0;function r(e,t){return{send(e){n&&queueMicrotask(()=>{for(let n of t)n(e)})},onSignal(t){return e.push(t),()=>{let n=e.indexOf(t);n>=0&&e.splice(n,1)}},close(){n=!1,e.length=0}}}return[r(e,t),r(t,e)]}const Oe=new Set([`offer`,`answer`,`ice`]);function ke(e){return new f.default(e)}var Ae=class{options;socket;handlers=[];outbox=[];joined=!1;closed=!1;constructor(e){this.options=e;let t=e.createSocket??ke;this.socket=t(e.url),this.socket.on(`open`,()=>this.handleOpen()),this.socket.on(`message`,e=>this.handleMessage(e)),this.socket.on(`error`,e=>this.fail(e instanceof Error?e:Error(String(e)))),this.socket.on(`close`,()=>this.handleClose())}handleOpen(){this.socket.send(JSON.stringify({type:`join`,rendezvous:this.options.rendezvous}));for(let e of this.outbox)this.writeSignal(e);this.outbox.length=0}handleMessage(e){let t;try{t=JSON.parse(typeof e==`string`?e:String(e))}catch{return}if(typeof t!=`object`||!t)return;let n=t;if(n.type===`joined`){this.joined=!0,this.options.onReady?.();return}if(n.type===`error`){this.fail(Error(`signaling relay rejected the connection: ${String(n.reason)}`));return}if(n.type===`signal`&&typeof n.kind==`string`&&Oe.has(n.kind)){let e={kind:n.kind,data:n.data};for(let t of this.handlers)t(e)}}handleClose(){this.closed||this.joined||this.fail(Error(`signaling socket closed before the rendezvous was joined`))}fail(e){this.options.onError?.(e)}writeSignal(e){this.socket.send(JSON.stringify({type:`signal`,kind:e.kind,data:e.data}))}send(e){this.closed||(this.socket.readyState===1?this.writeSignal(e):this.outbox.push(e))}onSignal(e){return this.handlers.push(e),()=>{let t=this.handlers.indexOf(e);t>=0&&this.handlers.splice(t,1)}}close(){this.closed=!0,this.handlers.length=0,this.outbox.length=0,this.socket.close()}};const je=/^[A-Za-z0-9_-]{22}$/,D=6e4;function O(e){return typeof e==`string`&&je.test(e)}function k(e,t){return typeof e==`string`&&e.length>0&&e.length<=t}function Me(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e;if(!(t.v!==1||!O(t.from))){if(t.kind===`hello`)return{v:1,kind:`hello`,from:t.from};if(!(!O(t.to)||!O(t.cid))){if(t.kind===`offer`||t.kind===`answer`)return k(t.sdp,49152)?{v:1,kind:t.kind,from:t.from,to:t.to,cid:t.cid,sdp:t.sdp}:void 0;if(t.kind===`ice`){let e=t.candidate;if(typeof e!=`object`||!e||Array.isArray(e))return;let n=e;return!k(n.candidate,2048)||n.sdpMid!==void 0&&!k(n.sdpMid,64)||n.sdpMLineIndex!==void 0&&!(typeof n.sdpMLineIndex==`number`&&Number.isInteger(n.sdpMLineIndex)&&n.sdpMLineIndex>=0&&n.sdpMLineIndex<64)?void 0:{v:1,kind:`ice`,from:t.from,to:t.to,cid:t.cid,candidate:{candidate:n.candidate,...n.sdpMid===void 0?{}:{sdpMid:n.sdpMid},...n.sdpMLineIndex===void 0?{}:{sdpMLineIndex:n.sdpMLineIndex}}}}}}}function Ne(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e;if(!(t.t!==`mesh-msg`||typeof t.body!=`string`||t.body.length>6e4))return{t:`mesh-msg`,body:t.body}}var A=class extends Error{end;stage;role;peer;channelClose;cause;constructor(e){let t=[`mesh link ended (${e.end}) during ${e.stage} as ${e.role}`];e.detail!==void 0&&t.push(e.detail),e.cause!==void 0&&t.push(e.cause instanceof Error?e.cause.message:String(e.cause));let n=e.peer;n!==void 0&&t.push(`connection ${n.state} [${n.history.join(` > `)}], ice ${n.ice??`?`}, gathering ${n.gathering??`?`}, candidates ${n.localCandidates} sent / ${n.remoteCandidates} applied, closed by ${n.closedBy??`the peer or the network`}${n.handlerError===void 0?``:` (${n.handlerError})`}`),e.channelClose!==void 0&&t.push(`channel ${e.channelClose}`),super(t.join(`; `)),this.name=`MeshLinkEndedError`,this.end=e.end,this.stage=e.stage,this.role=e.role,n!==void 0&&(this.peer=n),e.channelClose!==void 0&&(this.channelClose=e.channelClose),e.cause!==void 0&&(this.cause=e.cause)}};const j=`robota-file`,Pe=JSON.stringify({t:`mesh-admitted`});var Fe=class{options;state=`connecting`;peer;channel;localFingerprint;remoteFingerprint;tookRemoteDescription=!1;sentDescription=!1;localCandidates=[];remoteCandidates=0;handshake;fedProof=!1;held=[];handlers=new Set;endHandlers=new Set;fileChannels=new Set;fileHandlers=new Set;signalChain=Promise.resolve();stopAwaitingCertificate;timer;admissionResult;constructor(e){this.options=e,this.timer=setTimeout(()=>this.end(`timeout`),e.connectTimeoutMs)}get role(){return this.options.role}get admitted(){return this.state===`admitted`}get ended(){return this.isEnded()}isEnded(){return this.state===`ended`}get admission(){return this.state===`admitted`?this.admissionResult:void 0}get channelBinding(){if(this.state===`admitted`&&this.localFingerprint!==void 0&&this.remoteFingerprint!==void 0)return{localFingerprint:this.localFingerprint,remoteFingerprint:this.remoteFingerprint}}async offer(){if(!(this.options.role!==`offerer`||this.peer!==void 0||this.isEnded()))try{let e=this.createPeer();this.adoptChannel(e.createDataChannel(`robota-mesh`));let t=await e.createOffer();if(this.isEnded())return;this.localFingerprint=(0,u.extractDtlsFingerprint)(t),this.sendDescription({kind:`offer`,sdp:t})}catch(e){this.end(`signaling`,e)}}onSignal(e){this.isEnded()||(this.signalChain=this.signalChain.then(()=>this.applySignal(e)).catch(e=>this.end(`signaling`,e)))}async applySignal(e){if(this.isEnded())return;if(e.kind===`ice`){if(!this.tookRemoteDescription||!this.peer||++this.remoteCandidates>64)return;this.peer.addRemoteCandidate({candidate:e.candidate.candidate,mid:e.candidate.sdpMid??String(e.candidate.sdpMLineIndex??0)});return}let t=this.options.role===`offerer`?`answer`:`offer`;if(e.kind!==t||this.tookRemoteDescription)return;this.tookRemoteDescription=!0;let n;try{n=(0,u.extractDtlsFingerprintAttribute)(e.sdp).algorithm}catch(e){this.end(`channel-binding`,e);return}if(this.options.role===`offerer`){let t=this.peer;if(!t)return;t.acceptAnswer(e.sdp),this.awaitCertificate(t,n);return}let r=this.createPeer(),i=await r.acceptOffer(e.sdp);this.isEnded()||(this.localFingerprint=(0,u.extractDtlsFingerprint)(i),this.sendDescription({kind:`answer`,sdp:i}),this.awaitCertificate(r,n))}createPeer(){let e=this.options.createPeer();return this.peer=e,e.onLocalCandidate(e=>{if(this.isEnded())return;let t={kind:`ice`,candidate:{candidate:e.candidate,sdpMid:e.mid}};this.sentDescription?this.options.sendSignal(t):this.localCandidates.length<64&&this.localCandidates.push(t)}),e.onStateChange(e=>{(e===`failed`||e===`closed`)&&this.end(`closed`,void 0,`connection ${e}`)}),e.onDataChannel(e=>this.incomingChannel(e)),e}incomingChannel(e){if(this.options.role===`answerer`&&this.channel===void 0&&!this.isEnded()){this.adoptChannel(e);return}if(this.state!==`admitted`||e.label!==j||this.fileHandlers.size===0||this.fileChannels.size>=4){e.close();return}this.trackFileChannel(e);for(let t of this.fileHandlers)t(e)}trackFileChannel(e){this.fileChannels.add(e),e.onStateChange(t=>{t===`closed`&&this.fileChannels.delete(e)})}openFileChannel(){let e=this.peer;if(this.state!==`admitted`||e===void 0)return Promise.reject(Error(`mesh link is not admitted`));if(this.fileChannels.size>=4)return Promise.reject(Error(`too many file transfers are open on this link`));let t=e.createDataChannel(j);return this.trackFileChannel(t),new Promise((e,n)=>{if(t.readyState===`open`){e(t);return}let r=setTimeout(()=>{i(),t.close(),n(Error(`the file channel did not open`))},1e4),i=t.onStateChange(a=>{a!==`connecting`&&(clearTimeout(r),i(),a===`open`?e(t):n(Error(`the file channel closed before it opened`)))})})}onFileChannel(e){return this.fileHandlers.add(e),()=>this.fileHandlers.delete(e)}sendDescription(e){this.options.sendSignal(e),this.sentDescription=!0;for(let e of this.localCandidates.splice(0))this.options.sendSignal(e)}awaitCertificate(e,t){this.stopAwaitingCertificate=h(e,t,e=>{this.isEnded()||(this.remoteFingerprint=e,this.maybeStartHandshake())},e=>this.end(`channel-binding`,Error(e)))}adoptChannel(e){this.channel=e,e.onMessage(e=>this.inbound(e)),e.onStateChange(e=>{e===`open`?this.maybeStartHandshake():e===`closed`&&this.end(`closed`,void 0,`data channel closed`)}),e.readyState===`open`&&this.maybeStartHandshake()}maybeStartHandshake(){let e=this.channel;if(this.state!==`connecting`||this.handshake!==void 0||e===void 0||this.localFingerprint===void 0||this.remoteFingerprint===void 0||e.readyState!==`open`)return;let t;try{t=this.options.startHandshake({localFingerprint:this.localFingerprint,remoteFingerprint:this.remoteFingerprint,send:e=>this.write(JSON.stringify(e))})}catch(e){this.end(`handshake`,e);return}this.handshake=t,t.result.then(e=>this.confirm(e),e=>this.end(`handshake`,e));let n=this.held.splice(0);for(let e of n)this.feed(e)}inbound(e){if(!this.isEnded()){if(this.state===`admitted`){this.deliver(e);return}if(this.state===`confirming`){this.confirmed(e);return}if(this.handshake===void 0){this.hold(e);return}this.feed(e)}}hold(e){if(this.held.length>=16){this.end(`protocol`);return}this.held.push(e)}feed(e){if(this.fedProof){this.hold(e);return}let t;try{t=JSON.parse(e)}catch{t=e}typeof t==`object`&&t&&t.t===`dh-prove`&&(this.fedProof=!0),this.handshake?.onFrame(t)}confirm(e){if(this.state!==`connecting`)return;if(this.options.accepts!==void 0&&!this.options.accepts(e)){this.end(`handshake`,Error(`the admitted device is not the one this link is for`));return}this.state=`confirming`,this.admissionResult=e,this.write(Pe);let t=this.held.splice(0);for(let e of t)this.inbound(e)}confirmed(e){let t;try{t=JSON.parse(e)}catch{t=void 0}let n=this.admissionResult;if(n===void 0||typeof t!=`object`||!t||t.t!==`mesh-admitted`){this.end(`protocol`);return}clearTimeout(this.timer),this.state=`admitted`,this.options.onAdmitted(n)}deliver(e){let t;try{t=JSON.parse(e)}catch{t=void 0}let n=Ne(t);if(n===void 0){this.end(`protocol`);return}for(let e of this.handlers)e(n.body)}write(e){try{this.channel?.send(e)}catch(e){this.end(`closed`,e)}}send(e){if(this.state!==`admitted`)throw Error(`mesh link is not admitted`);if(e.length>6e4)throw Error(`mesh message exceeds ${D} characters`);this.write(JSON.stringify({t:`mesh-msg`,body:e}))}onMessage(e){return this.handlers.add(e),()=>this.handlers.delete(e)}onEnd(e){return this.endHandlers.add(e),()=>this.endHandlers.delete(e)}close(){this.end(`closed`,void 0,`closed by this side`)}get stage(){return this.state===`admitted`||this.state===`confirming`?this.state:this.handshake===void 0?this.remoteFingerprint===void 0?this.tookRemoteDescription?`connecting`:`signaling`:`awaiting-channel`:`handshake`}end(e,t,n){if(this.isEnded())return;let r=new A({end:e,stage:this.stage,role:this.options.role,...n===void 0?{}:{detail:n},...this.peer===void 0?{}:{peer:this.peer.diagnostics()},...this.channel?.closeCause===void 0?{}:{channelClose:this.channel.closeCause},...t===void 0?{}:{cause:t}});this.state=`ended`,clearTimeout(this.timer),this.stopAwaitingCertificate?.(),this.held.length=0,this.handshake?.onFrame(void 0),this.channel?.close();for(let e of this.fileChannels)e.close();this.fileChannels.clear(),this.fileHandlers.clear();let i=this.peer;i&&setTimeout(()=>i.close(),250).unref?.(),this.options.onEnded(e,r);for(let t of this.endHandlers)t(e);this.endHandlers.clear(),this.handlers.clear()}};const M=2e4,N=1e3;function P(e){return{send:t=>e.send(t),onFrame:t=>e.onMessage(t),onClose:t=>e.readyState===`closed`?(queueMicrotask(t),()=>void 0):e.onStateChange(e=>{e===`closed`&&t()}),close:()=>e.close()}}var F=class{last;waiting;timer;run(e){let t=Date.now();if(this.last===void 0||t-this.last>=N){this.last=t,e();return}this.waiting=e,this.timer===void 0&&(this.timer=setTimeout(()=>{this.timer=void 0;let e=this.waiting;this.waiting=void 0,e!==void 0&&this.run(e)},N-(t-this.last)),this.timer.unref?.())}cancel(){this.timer!==void 0&&clearTimeout(this.timer),this.timer=void 0,this.waiting=void 0}};function I(){let e=new Uint8Array(16);return globalThis.crypto.getRandomValues(e),Buffer.from(e).toString(`base64url`)}function L(e,t){return t!==void 0&&t.seq>e.seq&&t.signingKeyId===e.signingKeyId}function R(e,t){return e===void 0?t:t===void 0?e:Math.max(e,t)}function z(e,t){let n=new Map;for(let r of[e.bySigningKey??{},t.bySigningKey??{}])for(let[e,t]of Object.entries(r)){let r=n.get(e)??{},i=R(r.rosterSeq,t.rosterSeq),a=R(r.revocationSeq,t.revocationSeq);n.set(e,{...i===void 0?{}:{rosterSeq:i},...a===void 0?{}:{revocationSeq:a}})}let r=Object.fromEntries(n),i=R(e.signingKeyRevocationSeq,t.signingKeyRevocationSeq);return{...i===void 0?{}:{signingKeyRevocationSeq:i},bySigningKey:r}}function Ie(e,t){let n=t.signingKeyCertificate.signingKeyId===e.signingKeyCertificate.signingKeyId;return{...t,...n&&L(t.roster,e.roster)?{roster:e.roster}:{},...n&&L(t.revocation,e.revocation)?{revocation:e.revocation}:{},...e.signingKeyRevocation.seq>t.signingKeyRevocation.seq?{signingKeyRevocation:e.signingKeyRevocation}:{},marks:z(e.marks,t.marks)}}var Le=class{options;instance=I();identity;sessionDescriptor;peers=new Map;byInbound=new Map;linkHandlers=new Set;refusalHandlers=new Set;waiters=new Set;unsubscribes=[];started=!1;stopped=!1;sync=Promise.resolve();constructor(e){this.options=e,this.identity=e.identity,this.sessionDescriptor=e.sessionDescriptor}get deviceId(){return this.identity.deviceCertificate.deviceId}async start(){if(this.started)throw Error(`DeviceMeshNode already started`);this.started=!0,this.unsubscribes.push(this.options.relay.onMessage((e,t)=>this.receive(e,t))),await this.syncPeers()}refresh(e){return e.sessionDescriptor!==void 0&&(this.sessionDescriptor=e.sessionDescriptor),e.identity===void 0?Promise.resolve():(this.identity=Ie(this.identity,e.identity),this.syncPeers())}syncPeers(){this.sync=this.sync.then(async()=>{if(this.stopped)return;let e=this.identity,t=e.deviceCertificate,n=new Set(e.revocation.revokedDeviceIds),r=new Map;for(let i of e.roster.devices)i.deviceId!==t.deviceId&&!n.has(i.deviceId)&&r.set(i.deviceId,i);for(let[e,t]of this.peers){let n=r.get(e);(n===void 0||n.kaKey!==t.device.kaKey||n.kaEpoch!==t.device.kaEpoch)&&this.drop(t)}let i=[];for(let[n,a]of r){if(this.peers.has(n))continue;let r,o;try{r=await(0,u.derivePairRendezvous)({ownKaPrivateKey:e.kaPrivateKey,own:t,peerDeviceId:n,lists:e}),o=await r.relayInbox()}catch{continue}let s={device:a,role:t.deviceId<a.deviceId?`offerer`:`answerer`,rendezvous:r,inbound:o.inbound,outbound:o.outbound,attempts:new F,replies:new F};this.peers.set(n,s),this.byInbound.set(o.inbound,s),i.push(s)}if(!this.stopped){this.options.relay.declarePresence([...this.byInbound.keys()]),this.options.relay.declarePeers?.([...this.peers.values()].map(e=>({deviceId:e.device.deviceId,inbound:e.inbound,outbound:e.outbound,rendezvous:e.rendezvous})));for(let e of i)this.hello(e)}});let e=this.sync;return this.sync=e.catch(()=>void 0),e}drop(e){this.peers.delete(e.device.deviceId),this.byInbound.delete(e.inbound),e.attempts.cancel(),e.replies.cancel();let{pending:t,admitted:n}=e;if(e.pending=void 0,e.admitted=void 0,t?.link.close(),n?.link.close(),t!==void 0&&!this.stopped){let t={deviceId:e.device.deviceId,end:`closed`,error:Error(`peer device was revoked or is no longer in the roster`)};for(let e of this.refusalHandlers)e(t)}}roleFor(e){return this.peers.get(e)?.role}link(e){return this.peers.get(e)?.admitted?.exposed}connect(e,t=M){if(this.stopped)return Promise.reject(Error(`device mesh node stopped`));let n=this.peers.get(e);if(n===void 0)return Promise.reject(Error(`not a rostered, unrevoked peer device`));let r=n.admitted?.exposed;return r===void 0?new Promise((r,i)=>{let a=()=>{clearTimeout(s),c(),l(),this.waiters.delete(o)},o=e=>{a(),i(e)},s=setTimeout(()=>o(Error(`peer device did not connect in time`)),t);s.unref?.();let c=this.onLink(t=>{t.admission.deviceId===e&&(a(),r(t))}),l=this.onRefusal(t=>{t.deviceId===e&&o(t.error instanceof Error?t.error:Error(`connection ${t.end}`))});this.waiters.add(o),this.hello(n)}):Promise.resolve(r)}onLink(e){return this.linkHandlers.add(e),()=>this.linkHandlers.delete(e)}onRefusal(e){return this.refusalHandlers.add(e),()=>this.refusalHandlers.delete(e)}stop(){if(!this.stopped){this.stopped=!0;for(let e of this.unsubscribes.splice(0))e();for(let e of[...this.peers.values()])this.drop(e);for(let e of[...this.waiters])e(Error(`device mesh node stopped`));this.linkHandlers.clear(),this.refusalHandlers.clear()}}hello(e){this.stopped||this.options.relay.send(e.outbound,{v:1,kind:`hello`,from:this.instance})}receive(e,t){if(this.stopped)return;let n=this.byInbound.get(e);if(n===void 0)return;let r=Me(t);if(r===void 0)return;if(r.kind===`hello`){this.onHello(n,r.from);return}if(r.to!==this.instance)return;if(r.kind===`offer`){this.onOffer(n,r);return}for(let e of[n.pending,n.admitted])if(e?.cid===r.cid&&e.remoteInstance===r.from){e.link.onSignal(B(r));return}let i=n.waitingOffer;r.kind===`ice`&&i?.cid===r.cid&&i.from===r.from&&i.ice.length<64&&i.ice.push(B(r))}serves(e,t){return e.admitted?.remoteInstance===t||e.pending?.remoteInstance===t}onHello(e,t){if(!this.serves(e,t)){if(e.role===`answerer`){e.replies.run(()=>this.hello(e));return}e.attempts.run(()=>{this.stopped||this.peers.get(e.device.deviceId)!==e||this.serves(e,t)||this.startAttempt(e,t,I()).link.offer()})}}onOffer(e,t){if(e.role!==`answerer`||e.admitted?.cid===t.cid||e.pending?.cid===t.cid)return;let n={cid:t.cid,from:t.from,ice:[]};e.waitingOffer=n,e.attempts.run(()=>{if(e.waitingOffer===n&&(e.waitingOffer=void 0),this.stopped||this.peers.get(e.device.deviceId)!==e||e.admitted?.cid===t.cid||e.pending?.cid===t.cid)return;let{link:r}=this.startAttempt(e,t.from,t.cid);r.onSignal(B(t));for(let e of n.ice)r.onSignal(e)})}startAttempt(e,t,n){let r=e.pending;e.pending=void 0,r?.link.close();let i=new Fe({role:e.role,createPeer:()=>this.createPeer(),sendSignal:r=>{this.stopped||this.options.relay.send(e.outbound,{v:1,from:this.instance,to:t,cid:n,...r})},startHandshake:t=>(0,u.startDeviceHandshake)({role:e.role===`offerer`?`initiator`:`responder`,identity:this.identity,sessionDescriptor:this.sessionDescriptor,...e.role===`offerer`?{expectedPeerDeviceId:e.device.deviceId}:{},localFingerprint:t.localFingerprint,remoteFingerprint:t.remoteFingerprint,locality:`another-host`,localPolicy:this.options.localPolicy,send:t.send,...this.options.fetchLatestLists===void 0?{}:{fetchLatestLists:this.options.fetchLatestLists},onListsAdopted:e=>this.adoptLists(e),...this.options.now===void 0?{}:{now:this.options.now},...this.options.handshakeTimeoutMs===void 0?{}:{timeoutMs:this.options.handshakeTimeoutMs}}),connectTimeoutMs:this.options.connectTimeoutMs??M,accepts:t=>t.admission.deviceId===e.device.deviceId,onAdmitted:t=>this.admitted(e,i,t),onEnded:(t,n)=>this.ended(e,i,t,n)}),a={cid:n,remoteInstance:t,link:i};return e.pending=a,a}adoptLists(e){let t=this.identity,n={...t,...L(t.roster,e.roster)?{roster:e.roster}:{},...L(t.revocation,e.revocation)?{revocation:e.revocation}:{},...e.signingKeyRevocation!==void 0&&e.signingKeyRevocation.seq>t.signingKeyRevocation.seq?{signingKeyRevocation:e.signingKeyRevocation}:{},marks:z(t.marks,e.marks)};this.refresh({identity:n}).catch(()=>void 0),this.options.onListsAdopted?.(e)}admitted(e,t,n){let r=e.pending;if(r===void 0||r.link!==t)return;if(n.admission.deviceId!==e.device.deviceId){t.close();return}let i=n.admission,a=t.channelBinding;if(a===void 0){t.close();return}let o=new ee.ConnectionAuthority({deviceId:i.deviceId,sessionId:i.sessionId,locality:i.locality,capabilities:i.capabilities},this.options.operatorApprover),s=o.authorize(`message`),c={admission:i,result:n,channelBinding:a,authority:o,send:e=>t.send(e),onMessage:e=>t.onMessage(n=>{s.then(r=>{r.allowed?e(n):t.close()})}),openFileChannel:async()=>P(await t.openFileChannel()),onFileChannel:e=>t.onFileChannel(t=>e(P(t))),onClose:e=>t.onEnd(()=>e()),close:()=>t.close()};r.exposed=c;let l=e.admitted;e.admitted=r,e.pending=void 0,l?.link.close(),this.options.relay.confirmPeer?.(i.deviceId);for(let e of this.linkHandlers)e(c)}ended(e,t,n,r){if(e.admitted?.link===t){e.admitted=void 0;return}if(e.pending?.link!==t)return;e.pending=void 0;let i={deviceId:e.device.deviceId,end:n,...r===void 0?{}:{error:r}};for(let e of this.refusalHandlers)e(i)}createPeer(){return new b({...this.options.iceServers===void 0?{}:{iceServers:this.options.iceServers},...this.options.loadDataChannel===void 0?{}:{loadDataChannel:this.options.loadDataChannel}})}};function B(e){return e.kind===`ice`?{kind:`ice`,candidate:{...e.candidate}}:{kind:e.kind,sdp:e.sdp}}function Re(){let e=new Map;return{connect(){let t={messages:new Set,absents:new Set,topics:new Set,open:!0},n=()=>{for(let n of t.topics)e.get(n)===t&&e.delete(n);t.topics=new Set};return{declarePresence(r){if(t.open){n(),t.topics=new Set(r);for(let n of r)e.set(n,t)}},send(n,r){if(!t.open)return;let i=JSON.parse(JSON.stringify(r));queueMicrotask(()=>{let r=e.get(n);if(r===void 0||r===t||!r.open){for(let e of t.absents)e(n);return}for(let e of r.messages)e(n,i)})},onMessage(e){return t.messages.add(e),()=>t.messages.delete(e)},onAbsent(e){return t.absents.add(e),()=>t.absents.delete(e)},close(){t.open=!1,n(),t.messages.clear(),t.absents.clear()}}}}}var ze=class{options;socket;messages=new Set;absents=new Set;outbox=[];topics;closed=!1;constructor(e){this.options=e;let t=e.createSocket??(e=>new f.default(e));this.socket=t(e.url),this.socket.on(`open`,()=>this.flush()),this.socket.on(`message`,e=>this.receive(e)),this.socket.on(`error`,e=>this.options.onError?.(e instanceof Error?e:Error(String(e)))),this.socket.on(`close`,()=>{this.closed||this.options.onError?.(Error(`signaling relay connection closed`))})}flush(){this.topics!==void 0&&this.write({type:`presence`,topics:this.topics});for(let e of this.outbox.splice(0))this.socket.send(e)}write(e){this.socket.send(JSON.stringify(e))}receive(e){let t;try{t=JSON.parse(typeof e==`string`?e:String(e))}catch{return}if(typeof t!=`object`||!t)return;let n=t;if(n.type===`message`&&typeof n.topic==`string`)for(let e of this.messages)e(n.topic,n.data);else if(n.type===`absent`&&typeof n.topic==`string`)for(let e of this.absents)e(n.topic);else n.type===`present`?this.options.onPresent?.():n.type===`error`&&this.options.onError?.(Error(`signaling relay refused a frame: ${String(n.reason)}`))}declarePresence(e){this.closed||(this.topics=[...e],this.socket.readyState===1&&this.write({type:`presence`,topics:this.topics}))}send(e,t){if(this.closed)return;let n=JSON.stringify({type:`message`,topic:e,data:t});this.socket.readyState===1?this.socket.send(n):this.outbox.length<256?this.outbox.push(n):this.options.onError?.(Error(`signaling relay is not reachable; message dropped`))}onMessage(e){return this.messages.add(e),()=>this.messages.delete(e)}onAbsent(e){return this.absents.add(e),()=>this.absents.delete(e)}close(){this.closed=!0,this.messages.clear(),this.absents.clear(),this.outbox.length=0,this.socket.close()}};function Be(e){return{candidates:t=>Promise.resolve(e.recall(t.deviceId))}}function Ve(e,t){return e.host===t.host&&e.port===t.port}function He(e,t){return[{host:t.host,port:t.port},...e.filter(e=>!Ve(e,t))].slice(0,3)}function Ue(){let e=new Map,t;return{recall:t=>e.get(t)??[],remember:(t,n)=>{e.set(t,He(e.get(t)??[],n))},retain:t=>{let n=new Set(t);for(let t of[...e.keys()])n.has(t)||e.delete(t)},lastListenPort:()=>t,rememberListenPort:e=>{t=e}}}const We=/^[A-Za-z0-9_-]{43}$/,Ge=/^[A-Za-z0-9_-]{22}$/,Ke=12e4,V=1008;function H(e){return(0,p.createHash)(`sha256`).update(`lan-address:${e}`).digest(`base64url`)}function qe(e,t){return(0,p.createHmac)(`sha256`,e).update(`lan-present:${t}`).digest(`base64url`)}function Je(e,t,n){if(typeof n!=`string`)return!1;let r=Buffer.from(qe(e,t)),i=Buffer.from(n);return i.length===r.length&&(0,p.timingSafeEqual)(i,r)}function Ye(e,t){return new Promise((n,r)=>{let i=new f.WebSocketServer({port:e,...t===void 0?{}:{host:t},maxPayload:65536,perMessageDeflate:!1,verifyClient:e=>e.origin===void 0||e.origin===``}),a=e=>{i.close(),r(e)};i.once(`error`,a),i.once(`listening`,()=>{i.off(`error`,a),n(i)})})}function Xe(e){let t;try{t=JSON.parse(String(e))}catch{return}if(typeof t!=`object`||!t||Array.isArray(t))return;let n=t;if(!(typeof n.type!=`string`||typeof n.to!=`string`||!We.test(n.to)))return n.type===`probe`?typeof n.nonce==`string`&&Ge.test(n.nonce)?{type:n.type,to:n.to,nonce:n.nonce}:void 0:{type:n.type,to:n.to,data:n.data}}async function Ze(e={}){let t;try{t=await Ye(e.port??0,e.host)}catch(n){if(e.port===void 0||e.port===0)throw n;t=await Ye(0,e.host)}let n=t.address(),r=typeof n==`object`&&n?n.port:0,i=new Map,a=new Set;return t.on(`connection`,e=>{if(t.clients.size>32){e.close(V);return}let n=0,r=Date.now(),o=setTimeout(()=>e.close(),Ke);o.unref?.(),e.on(`close`,()=>clearTimeout(o)),e.on(`error`,()=>e.close()),e.on(`message`,t=>{let s=Date.now();if(s-r>1e4&&(r=s,n=0),n+=1,n>128){e.close(V);return}clearTimeout(o),o=setTimeout(()=>e.close(),Ke),o.unref?.();let c=Xe(t);if(c===void 0)return;let l=i.get(c.to);if(l===void 0){e.send(JSON.stringify({type:`absent`,to:c.to}));return}if(c.type===`probe`&&c.nonce!==void 0){e.send(JSON.stringify({type:`present`,to:c.to,nonce:c.nonce,proof:qe(l,c.nonce)}));return}if(c.type===`message`)for(let e of a)e(l,c.data)})}),{port:r,setPresence(e){i=new Map(e.map(e=>[H(e),e]))},onMessage(e){return a.add(e),()=>a.delete(e)},close(){a.clear(),i=new Map;for(let e of t.clients)e.terminate();return new Promise(e=>t.close(()=>e()))}}}const U=`_rdv._tcp.local`;function Qe(){return(0,d.createRequire)(require("url").pathToFileURL(__filename).href)(`multicast-dns`)()}function $e(){let e=[];for(let t of Object.values((0,te.networkInterfaces)()))for(let n of t??[])n.internal||(n.family===`IPv4`||n.family===`IPv6`&&!n.address.startsWith(`fe80`))&&e.push(n.address);return e}function et(e){let t=new Uint8Array(e);return globalThis.crypto.getRandomValues(t),Buffer.from(t).toString(`hex`)}function tt(e){return Buffer.from(e.subarray(0,16)).toString(`hex`)}function nt(e){return Math.max(8,Math.ceil(e/8)*8)}function W(e,t){return e.toLowerCase()===t.toLowerCase()}var rt=class{options;transport;routes=[];port;records;instances=new Set;timer;generation=0;closed=!1;epochCover;announced;lastAnswer;answerTimer;constructor(e={}){this.options=e}now(){return(this.options.now??Date.now)()}open(){if(!this.closed){if(this.transport!==void 0)return this.transport;try{let e=(this.options.createTransport??Qe)();return e.on(`query`,e=>this.onQuery(e)),e.on(`error`,e=>this.options.onError?.(e)),this.transport=e,e}catch(e){this.closed=!0,this.options.onError?.(e instanceof Error?e:Error(String(e)));return}}}advertise(e,t){return this.routes=e,this.port=t,this.rebuild()}async rebuild(){let e=++this.generation,t=this.port;if(this.closed||t===void 0)return;let n=(0,u.rendezvousEpoch)(this.now()),r=await Promise.all(this.routes.map(e=>e.rendezvous.tag(`mdns`,`outbound`,n)));if(e!==this.generation||this.closed)return;this.epochCover?.epoch!==n&&(this.epochCover={epoch:n,padding:[],target:`${et(8)}.local`});let{padding:i,target:a}=this.epochCover,o=nt(r.length)-r.length;for(;i.length<o;)i.push(et(16));let s=[...r.map(tt),...i.slice(0,o)].sort(),c=JSON.stringify([n,t,s]),l=s.map(e=>({name:U,type:`PTR`,ttl:120,data:`${e}.${U}`})),d=[];for(let e of s){let n=`${e}.${U}`;d.push({name:n,type:`SRV`,ttl:120,data:{priority:0,weight:0,port:t,target:a}},{name:n,type:`TXT`,ttl:120,data:[]})}for(let e of(this.options.addresses??$e)())d.push({name:a,type:e.includes(`:`)?`AAAA`:`A`,ttl:120,data:e});this.records={answers:l,additionals:d},this.instances=new Set(s.map(e=>`${e}.${U}`.toLowerCase())),this.schedule(n),c!==this.announced&&(this.announced=c,this.open()?.respond(this.records))}schedule(e){this.timer!==void 0&&clearTimeout(this.timer);let t=Math.max(0,(e+1)*u.RENDEZVOUS_EPOCH_MS-this.now());this.timer=setTimeout(()=>{this.rebuild().catch(()=>void 0)},t),this.timer.unref?.()}onQuery(e){this.records===void 0||this.closed||(e.questions??[]).some(e=>W(e.name,`_rdv._tcp.local`)&&(e.type===`PTR`||e.type===`ANY`)||this.instances.has(e.name.toLowerCase()))&&this.answer()}answer(){if(this.answerTimer!==void 0)return;let e=this.options.minAnswerIntervalMs??500,t=this.lastAnswer===void 0?0:this.lastAnswer+e-this.now(),n=()=>{this.answerTimer=void 0,!(this.closed||this.records===void 0)&&(this.lastAnswer=this.now(),this.transport?.respond(this.records))};if(t<=0){n();return}this.answerTimer=setTimeout(n,t),this.answerTimer.unref?.()}async candidates(e,t){let n=this.open();if(n===void 0||t.aborted)return[];let r=(0,u.rendezvousEpoch)(this.now()),i=new Set((await e.rendezvous.lookupTags(`mdns`,r)).map(e=>`${tt(e)}.${U}`));return t.aborted||this.closed?[]:new Promise(e=>{let r=[],a=()=>{clearTimeout(s),t.removeEventListener(`abort`,a),n.removeListener(`response`,o),e(r)},o=(e,t)=>{let n=[...e.answers??[],...e.additionals??[]];for(let e of n){if(e.type!==`PTR`||!W(e.name,`_rdv._tcp.local`)||typeof e.data!=`string`||!i.has(e.data.toLowerCase()))continue;let a=n.find(t=>t.type===`SRV`&&W(t.name,e.data))?.data?.port;typeof a!=`number`||!Number.isInteger(a)||a<=0||a>65535||r.some(e=>e.host===t.address&&e.port===a)||r.push({host:t.address,port:a})}r.length>0&&a()},s=setTimeout(a,this.options.lookupTimeoutMs??1e3);s.unref?.(),t.addEventListener(`abort`,a,{once:!0}),n.on(`response`,o),n.query({questions:[{name:U,type:`PTR`}]})})}close(){this.closed=!0,this.generation+=1,this.timer!==void 0&&clearTimeout(this.timer),this.answerTimer!==void 0&&clearTimeout(this.answerTimer),this.transport?.destroy(),this.transport=void 0}};function it(){let e=new Set,t=[],n=(t,n,r)=>{let i=JSON.parse(JSON.stringify(n));queueMicrotask(()=>{for(let n of e)for(let e of r(n))e(i,{address:t.address})})};return{responses:t,transport(r){let i={address:r,queries:new Set,responses:new Set};return e.add(i),{on(e,t){e===`query`&&i.queries.add(t),e===`response`&&i.responses.add(t)},removeListener(e,t){i.responses.delete(t)},query(e){n(i,e,e=>e.queries)},respond(e){t.push(JSON.parse(JSON.stringify(e))),n(i,e,e=>e.responses)},destroy(){e.delete(i)}}}}}function at(e){return`${e.host}:${e.port}`}function ot(){let e=new Uint8Array(16);return globalThis.crypto.getRandomValues(e),Buffer.from(e).toString(`base64url`)}function st(e){return Buffer.from(e).toString(`base64url`)}function ct(e){return`ws://${e.host.includes(`:`)?`[${e.host}]`:e.host}:${e.port}`}function lt(e){try{let t=JSON.parse(String(e));return typeof t==`object`&&t?t:void 0}catch{return}}var ut=class{options;routes=new Map;byDevice=new Map;lanIn=new Map;messages=new Set;absents=new Set;unsubscribes=[];abort=new AbortController;sync=Promise.resolve();pendingPeers=0;waiting=[];timer;closed=!1;constructor(e){this.options=e;for(let e of this.carriers())this.unsubscribes.push(e.onMessage((e,t)=>this.deliver(e,t)),e.onAbsent(e=>this.absent(e)));this.unsubscribes.push(e.listener.onMessage((e,t)=>{let n=this.lanIn.get(e);n!==void 0&&this.deliver(n,t)}))}now(){return(this.options.now??Date.now)()}carriers(){return[...this.options.signaling??[],...this.options.relay===void 0?[]:[this.options.relay]]}deliver(e,t){if(!this.closed)for(let n of this.messages)n(e,t)}absent(e){if(!this.closed)for(let t of this.absents)t(e)}declarePresence(e){for(let t of this.carriers())t.declarePresence(e)}declarePeers(e){for(let t of this.carriers())t.declarePeers?.(e);this.pendingPeers+=1,this.sync=this.sync.then(()=>this.applyPeers(e)).catch(e=>{this.options.onError?.(e instanceof Error?e:Error(String(e)))}).finally(()=>{if(--this.pendingPeers,!(this.pendingPeers>0))for(let{topic:e,data:t}of this.waiting.splice(0))this.send(e,t)})}async applyPeers(e){if(this.closed)return;let t=(0,u.rendezvousEpoch)(this.now()),n=new Map,r=new Map;for(let i of e){let e=st(await i.rendezvous.tag(`lan-inbox`,`outbound`,t));for(let e of await i.rendezvous.lookupTags(`lan-inbox`,t))n.set(st(e),i.inbound);let a=this.routes.get(i.outbound),o=a!==void 0&&a.route.deviceId===i.deviceId?a:{route:i,lanOut:e,path:{kind:`unresolved`},avoid:new Map,avoidCarriers:new Map};o.route=i,o.lanOut=e,r.set(i.outbound,o)}if(!this.closed){for(let[e,t]of this.routes)r.get(e)!==t&&this.reset(t);this.routes.clear(),this.byDevice.clear();for(let[e,t]of r)this.routes.set(e,t),this.byDevice.set(t.route.deviceId,t);this.lanIn=n,this.options.listener.setPresence([...n.keys()]),this.cacheWrite(()=>this.options.cache?.retain(e.map(e=>e.deviceId))),await Promise.all((this.options.advertisers??[]).map(t=>t.advertise(e,this.options.listener.port))),!this.closed&&this.scheduleEpoch(t,e)}}scheduleEpoch(e,t){this.timer!==void 0&&clearTimeout(this.timer);let n=Math.max(0,(e+1)*u.RENDEZVOUS_EPOCH_MS-this.now());this.timer=setTimeout(()=>this.declarePeers(t),n),this.timer.unref?.()}confirmPeer(e){let t=this.byDevice.get(e),n=t?.path;if(!(t===void 0||n?.kind!==`direct`&&n?.kind!==`relay`)){if(clearTimeout(n.deadline),n.deadline=void 0,n.kind===`relay`){t.avoidCarriers.clear();return}t.avoid.clear(),this.cacheWrite(()=>this.options.cache?.remember(e,n.candidate))}}cacheWrite(e){try{e()}catch(e){this.options.onError?.(e instanceof Error?e:Error(String(e)))}}send(e,t){if(this.closed)return;if(this.pendingPeers>0){this.waiting.length<256?this.waiting.push({topic:e,data:t}):this.viaRelay(e,t);return}let n=this.routes.get(e);if(n===void 0){this.viaRelay(e,t);return}let r=n.path;if(r.kind===`direct`&&r.socket.readyState===1){this.armDeadline(n,r),this.sendDirect(r.socket,n,t);return}if(r.kind===`relay`&&this.now()<r.until){this.viaCarrier(n,r,t);return}if(r.kind===`resolving`){r.queue.length<256?r.queue.push(t):this.viaRelay(e,t);return}r.kind===`direct`&&this.reset(n);let i=[t];n.path={kind:`resolving`,queue:i},this.resolve(n,i)}sendDirect(e,t,n){e.send(JSON.stringify({type:`message`,to:H(t.lanOut),data:n}))}viaRelay(e,t){let n=this.carriers().at(-1);if(n===void 0){queueMicrotask(()=>this.absent(e));return}n.send(e,t)}viaCarrier(e,t,n){t.carrier!==this.carriers().at(-1)&&this.armDeadline(e,t),t.carrier.send(e.route.outbound,n)}async resolve(e,t){let n=new Set,r=this.now();for(let[t,n]of e.avoid)n<=r&&e.avoid.delete(t);for(let r of this.options.sources){if(this.closed||e.path.kind!==`resolving`)return;for(let i of await this.look(r,e.route)){let r=at(i);if(n.size>=8)break;if(n.has(r)||e.avoid.has(r))continue;n.add(r);let a=await this.probe(i,e.lanOut);if(a!==void 0){if(this.closed||e.path.kind!==`resolving`||this.routes.get(e.route.outbound)!==e){a.close();return}this.useDirect(e,a,i);for(let n of t)this.sendDirect(a,e,n);return}}}if(this.closed||e.path.kind!==`resolving`)return;this.fallBack(e);let i=e.path;for(let n of t)i.kind===`relay`?this.viaCarrier(e,i,n):this.viaRelay(e.route.outbound,n)}fallBack(e){let t=this.now();for(let[n,r]of e.avoidCarriers)r<=t&&e.avoidCarriers.delete(n);let n=this.carriers(),r=n.find(t=>!e.avoidCarriers.has(t))??n.at(-1);e.path=r===void 0?{kind:`unresolved`}:{kind:`relay`,until:t+this.recheckMs(),carrier:r}}recheckMs(){return this.options.relayRecheckMs??3e4}async look(e,t){let n=AbortSignal.timeout(e.timeoutMs??this.options.sourceTimeoutMs??1500),r=AbortSignal.any([n,this.abort.signal]);try{return await Promise.race([e.candidates(t,r),new Promise(e=>r.addEventListener(`abort`,()=>e([]),{once:!0}))])}catch{return[]}}probe(e,t){let n=H(t),r=ot();return new Promise(i=>{let a;try{a=(this.options.connect??(e=>new f.default(ct(e),{maxPayload:4096})))(e)}catch{i(void 0);return}let o=!1,s=e=>{o||(o=!0,clearTimeout(c),e||a.close(),i(e?a:void 0))},c=setTimeout(()=>s(!1),this.options.probeTimeoutMs??1500);c.unref?.(),a.on(`open`,()=>a.send(JSON.stringify({type:`probe`,to:n,nonce:r}))),a.on(`message`,e=>{let i=lt(e);i?.to===n&&(i.type===`present`?s(Je(t,r,i.proof)):i.type===`absent`&&s(!1))}),a.on(`error`,()=>s(!1)),a.on(`close`,()=>s(!1))})}armDeadline(e,t){t.deadline===void 0&&(t.deadline=setTimeout(()=>{if(e.path!==t)return;let n=this.now()+this.recheckMs();t.kind===`direct`?e.avoid.set(at(t.candidate),n):e.avoidCarriers.set(t.carrier,n),this.reset(e),this.fallBack(e)},this.options.admissionTimeoutMs??2e4),t.deadline.unref?.())}useDirect(e,t,n){let r={kind:`direct`,socket:t,candidate:n};e.path=r,this.armDeadline(e,r);let i=()=>{e.path===r&&this.reset(e)};t.on(`close`,i),t.on(`error`,i),t.on(`message`,t=>{let n=lt(t);n?.type===`absent`&&n.to===H(e.lanOut)&&i()})}reset(e){let t=e.path;e.path={kind:`unresolved`},t.kind===`relay`&&clearTimeout(t.deadline),t.kind===`direct`&&(clearTimeout(t.deadline),t.socket.close())}onMessage(e){return this.messages.add(e),()=>this.messages.delete(e)}onAbsent(e){return this.absents.add(e),()=>this.absents.delete(e)}close(){if(!this.closed){this.closed=!0,this.abort.abort(),this.timer!==void 0&&clearTimeout(this.timer);for(let e of this.unsubscribes.splice(0))e();for(let e of this.routes.values())this.reset(e);this.routes.clear(),this.byDevice.clear(),this.messages.clear(),this.absents.clear(),this.waiting=[];for(let e of this.options.advertisers??[])e.close();this.options.listener.close().catch(()=>void 0)}}};async function dt(e={}){let t=e.cache??Ue(),n=t.lastListenPort(),r=await Ze({...n===void 0?{}:{port:n},...e.host===void 0?{}:{host:e.host}});try{t.rememberListenPort(r.port)}catch(t){e.onError?.(t instanceof Error?t:Error(String(t)))}let i=e.mdns===!1?void 0:new rt({...e.onError===void 0?{}:{onError:e.onError},...e.mdns}),a=e.internet?.dht,o=e.internet?.signaling??[];return new ut({...e.relay===void 0?{}:{relay:e.relay},...o.length>0?{signaling:o}:{},listener:r,sources:[Be(t),...i===void 0?[]:[i],...a===void 0?[]:[a]],cache:t,advertisers:[...i===void 0?[]:[i],...a===void 0?[]:[a]],...e.onError===void 0?{}:{onError:e.onError}})}const ft=Uint8Array.from([48,46,2,1,0,48,5,6,3,43,101,112,4,34,4,32]),G=new TextEncoder,pt=new TextDecoder;function K(e){return e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)}function mt(e){let t=atob(e.replace(/-/g,`+`).replace(/_/g,`/`)),n=new Uint8Array(t.length);for(let e=0;e<t.length;e+=1)n[e]=t.charCodeAt(e);return n}function q(e){let t=new Uint8Array(e.reduce((e,t)=>e+t.length,0)),n=0;for(let r of e)t.set(r,n),n+=r.length;return t}function ht(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r+=1)n|=e[r]^t[r];return n===0}async function gt(e){if(e.length!==32)throw Error(`an Ed25519 seed is 32 bytes`);let t=await globalThis.crypto.subtle.importKey(`pkcs8`,K(q([ft,e])),{name:`Ed25519`},!0,[`sign`]),n=await globalThis.crypto.subtle.exportKey(`jwk`,t);if(typeof n.x!=`string`)throw Error(`Ed25519 public key unavailable`);return{publicKey:mt(n.x),sign:async e=>new Uint8Array(await globalThis.crypto.subtle.sign({name:`Ed25519`},t,K(e)))}}async function _t(e,t,n){if(e.length!==32||n.length!==64)return!1;try{let r=await globalThis.crypto.subtle.importKey(`raw`,K(e),{name:`Ed25519`},!1,[`verify`]);return await globalThis.crypto.subtle.verify({name:`Ed25519`},r,K(n),K(t))}catch{return!1}}function J(e,t,n){let r=[];return e!==void 0&&e.length>0&&r.push(G.encode(`4:salt${e.length}:`),e),r.push(G.encode(`3:seqi${t}e1:v${n.length}:`),n),q(r)}async function vt(e,t,n,r){if(r.length>999)throw Error(`record value exceeds 999 bytes`);if(t!==void 0&&t.length>64)throw Error(`salt exceeds 64 bytes`);if(!Number.isSafeInteger(n)||n<0)throw Error(`seq must be a non-negative integer`);let i=await e.sign(J(t,n,r));return{k:e.publicKey,...t===void 0?{}:{salt:t},seq:n,v:r,sig:i}}async function Y(e,t,n){return!ht(e.k,t)||!ht(e.salt??new Uint8Array,n??new Uint8Array)||e.v.length>999||!Number.isSafeInteger(e.seq)||e.seq<0?!1:_t(t,J(n,e.seq,e.v),e.sig)}const yt={hints:`bep44-salt`,revocation:`bep44-revocation-salt`};async function bt(e){return new Uint8Array(await globalThis.crypto.subtle.digest(`SHA-256`,K(q(e))))}async function xt(e,t,n,r,i=0){let[a,o]=await Promise.all([e.signingSeed(n,r,t),e.tag(yt[t],n,r)]);if(i===0)return{key:await gt(a),salt:o};let s=G.encode(`chunk`),c=Uint8Array.of(i),[l,u]=await Promise.all([bt([s,a,c]),bt([s,o,c])]);return{key:await gt(l),salt:u}}function St(e){let t=G.encode(JSON.stringify(e)),n=Math.max(1,Math.ceil(t.length/862));if(n>8)throw Error(`device lists too large to publish as records`);return Array.from({length:n},(e,r)=>{let i=new Uint8Array(864).fill(32);return i[0]=n,i[1]=r,i.set(t.subarray(r*862,(r+1)*862),2),i})}function Ct(e){let t=e[0];if(e.length===864&&e[1]===0)return t!==void 0&&t>=1&&t<=8?t:void 0}function wt(e){let t=e.length,n=[];for(let[r,i]of e.entries()){if(i.length!==864||i[0]!==t||i[1]!==r)return;n.push(i.subarray(2))}return X(q(n))}function Tt(e,t){let n=G.encode(JSON.stringify(e));if(n.length>t)throw Error(`record does not fit its fixed size`);let r=new Uint8Array(t).fill(32);return r.set(n),r}function X(e){try{return JSON.parse(pt.decode(e))}catch{return}}function Et(e){let t=[];for(let n of e){if(t.length>=8)break;n.host.length===0||n.host.length>64||t.push([n.host,n.port])}return Tt({v:1,c:t},640)}const Dt=/^[0-9A-Za-z.:%_-]{1,64}$/;function Ot(e){let t=X(e);if(typeof t!=`object`||!t)return[];let n=t;if(n.v!==1||!Array.isArray(n.c))return[];let r=[];for(let e of n.c.slice(0,8)){if(!Array.isArray(e)||e.length!==2)continue;let[t,n]=e;typeof t!=`string`||!Dt.test(t)||typeof n!=`number`||!Number.isInteger(n)||n<1||n>65535||r.push({host:t,port:n})}return r}function kt(e,t=Math.random){let n=t();return Math.floor((Number.isFinite(n)?Math.min(Math.max(n,0),1):0)*Math.max(0,e))}function At(e){if(typeof e!=`object`||!e)return-1;let t=e.seq;return typeof t==`number`&&Number.isSafeInteger(t)?t:-1}function Z(e){return e instanceof Error?e:Error(String(e))}var jt=class{options;timeoutMs;scheduled=new Map;routes=[];listsFound;prefetchedEpoch;closed=!1;constructor(e){this.options=e,this.timeoutMs=e.lookupTimeoutMs??6e3}now(){return(this.options.now??Date.now)()}async candidates(e,t){let n=(0,u.rendezvousEpoch)(this.now()),r=await this.lookup(e,`hints`,n,t),i=[],a=new Set;for(let e of r.sort((e,t)=>t.epoch-e.epoch))for(let t of Ot(e.data)){let e=`${t.host}:${t.port}`;a.has(e)||(a.add(e),i.push(t))}return i}async lookup(e,t,n,r){let i=[n-1,n,n+1].flatMap(n=>this.options.stores.map(async i=>{let a=await this.read(i,e,t,n,0,r);return a===void 0?void 0:{epoch:n,data:a}}));return(await Promise.all(i)).filter(e=>e!==void 0)}async read(e,t,n,r,i,a){try{let o=await xt(t.rendezvous,n,`inbound`,r,i),s=await e.get(o.key.publicKey,o.salt,a);if(s===void 0)return;let c=await t.rendezvous.openRecord(s,r,n);return c?.epoch===r?c.hints:void 0}catch{return}}async lookupLists(e,t,n){let r=[t-1,t,t+1].flatMap(t=>this.options.stores.map(async r=>{let i=await this.read(r,e,`revocation`,t,0,n),a=i===void 0?void 0:Ct(i);if(i===void 0||a===void 0)return;let o=await Promise.all(Array.from({length:a-1},(i,a)=>this.read(r,e,`revocation`,t,a+1,n)));if(!o.some(e=>e===void 0))return wt([i,...o])}));return(await Promise.all(r)).filter(e=>e!==void 0)}advertise(e,t){if(this.closed)return Promise.resolve();this.routes=e;let n=(0,u.rendezvousEpoch)(this.now()),r=new Set(e.map(e=>e.outbound));for(let[e,t]of this.scheduled)r.has(e)||(clearTimeout(t.timer),this.scheduled.delete(e));this.prefetchedEpoch!==n&&(this.prefetchedEpoch=n,this.findLists(AbortSignal.timeout(3e4)));let i=this.options.maxPublishJitterMs??9e4;for(let r of e){let e=this.scheduled.get(r.outbound);if(e!==void 0&&e.epoch===n&&e.port===t)continue;e!==void 0&&clearTimeout(e.timer);let a=setTimeout(()=>{this.scheduled.delete(r.outbound),this.publish(r,t)},kt(i,this.options.random));a.unref?.(),this.scheduled.set(r.outbound,{timer:a,epoch:n,port:t})}return Promise.resolve()}async publish(e,t){if(this.closed)return;let n=this.now(),r=(0,u.rendezvousEpoch)(n),i=[];try{i.push({purpose:`hints`,chunk:0,plaintext:Et(this.options.addresses().map(e=>({host:e,port:t})))});let e=this.options.lists?.();if(e!==void 0){let t=St({r:e.revocation,...e.signingKeyRevocation===void 0?{}:{s:e.signingKeyRevocation}});for(let[e,n]of t.entries())i.push({purpose:`revocation`,chunk:e,plaintext:n})}}catch(e){this.options.onError?.(Z(e))}for(let{purpose:t,chunk:a,plaintext:o}of i)try{let i=await e.rendezvous.sealRecord(o,r,t),s=await xt(e.rendezvous,t,`outbound`,r,a),c=await Promise.allSettled(this.options.stores.map(e=>e.put(s,i,n)));for(let e of c)e.status===`rejected`&&this.options.onError?.(Z(e.reason))}catch(e){this.options.onError?.(Z(e))}}async latestLists(e){let t=new Promise(t=>{e.aborted&&t(void 0),e.addEventListener(`abort`,()=>t(void 0),{once:!0})});return await Promise.race([this.findLists(e),t])??this.listsFound}async findLists(e){let t=(0,u.rendezvousEpoch)(this.now()),n=await Promise.all(this.routes.slice(0,16).map(n=>this.lookupLists(n,t,e))),r=[],i=[];for(let e of n.flat()){if(typeof e!=`object`||!e)continue;let t=e;t.r!==void 0&&r.push(t.r),t.s!==void 0&&i.push(t.s)}let a=r.length===0&&i.length===0?void 0:{...r.length>0?{revocation:Mt(r)}:{},...i.length>0?{signingKeyRevocation:Mt(i)}:{}};return e.aborted||(this.listsFound=a),a}close(){if(!this.closed){this.closed=!0;for(let{timer:e}of this.scheduled.values())clearTimeout(e);this.scheduled.clear();for(let e of this.options.stores)e.close()}}};function Mt(e){let t=new Set;return e.filter(e=>{let n=JSON.stringify(e);return t.has(n)?!1:(t.add(n),!0)}).sort((e,t)=>At(t)-At(e)).slice(0,16)}function Nt(e){let t=``;for(let n of e)t+=n.toString(16).padStart(2,`0`);return t}function Pt(){let e=new Map,t=e=>e,n=(e,t)=>`${Nt(e)}/${Nt(t??new Uint8Array)}`;return{store:()=>({async put(t,r,i){let a=await vt(t.key,t.salt,Math.floor(i/1e3),r);if(!await Y(a,a.k,a.salt))throw Error(`item refused`);let o=n(a.k,a.salt),s=e.get(o);if(s!==void 0&&s.seq>a.seq)throw Error(`item refused: older seq`);e.set(o,a)},async get(r,i){await Promise.resolve();let a=e.get(n(r,i)),o=a===void 0?void 0:t(a);if(o!==void 0)return await Y(o,r,i)?o.v:void 0},close:()=>void 0}),items:()=>[...e.values()],serve:e=>{t=e}}}const Ft=`ybndrfg8ejkmcpqxot1uwisza345h769`;function It(e){let t=``,n=0,r=0;for(let i of e)for(n=n<<8|i,r+=8;r>=5;)t+=Ft[n>>>r-5&31],r-=5;return r>0&&(t+=Ft[n<<5-r&31]),t}function Q(e){return Uint8Array.from([e>>>8&255,e&255])}function Lt(e,t){let n=q([...[`_r`,It(e)].flatMap(e=>[Uint8Array.of(e.length),new TextEncoder().encode(e)]),Uint8Array.of(0)]),r=[];for(let e=0;e<t.length;e+=255){let n=t.subarray(e,e+255);r.push(Uint8Array.of(n.length),n)}t.length===0&&r.push(Uint8Array.of(0));let i=q(r);return q([Uint8Array.from([0,0,132,0,0,0,0,1,0,0,0,0]),n,Q(16),Q(1),Uint8Array.from([0,0,1,44]),Q(i.length),i])}function Rt(e){let t=t=>(e[t]??0)<<8|(e[t+1]??0);if(e.length<12)return;let n=t(4),r=t(6),i=12,a=()=>{for(let t=0;t<128;t+=1){let t=e[i];if(t===void 0)return!1;if(t===0)return i+=1,!0;if((t&192)==192)return i+=2,!0;i+=1+t}return!1};for(let e=0;e<n;e+=1){if(!a())return;i+=4}for(let n=0;n<r;n+=1){if(!a()||i+10>e.length)return;let n=t(i),r=t(i+8),o=i+10,s=o+r;if(s>e.length)return;if(n===16){let t=[],n=o;for(;n<s;){let r=e[n];if(n+1+r>s)return;t.push(e.subarray(n+1,n+1+r)),n+=1+r}return q(t)}i=s}}function zt(e){let t=new Uint8Array(8);return new DataView(t.buffer).setBigUint64(0,BigInt(e)),t}function Bt(e){let t=e.length;for(;t>0&&e[t-1]===`/`;)--t;return e.slice(0,t)}function Vt(e){let t=e.fetch??globalThis.fetch.bind(globalThis),n=(e,t)=>`${Bt(e)}/${It(t)}`;return{async put(r,i,a,o){let s=Lt(r.key.publicKey,i);if(s.length>999)throw Error(`record too large for a pkarr relay`);let c=Math.floor(a)*1e3,l=q([await r.key.sign(J(void 0,c,s)),zt(c),s]);if(!(await Promise.allSettled(e.relays.map(async e=>{let i=await t(n(e,r.key.publicKey),{method:`PUT`,headers:{"content-type":`application/pkarr.org/relays#payload`},body:l.slice().buffer,...o===void 0?{}:{signal:o}});if(!i.ok)throw Error(`pkarr relay refused (${i.status})`)}))).some(e=>e.status===`fulfilled`))throw Error(`no pkarr relay took the record`)},async get(r,i,a){let o=await Promise.all(e.relays.map(async e=>{try{let i=await t(n(e,r),{signal:a});if(!i.ok)return;let o=new Uint8Array(await i.arrayBuffer());if(o.length<=72||o.length>1200)return;let s=o.subarray(0,64),c=Number(new DataView(o.buffer,o.byteOffset+64,8).getBigUint64(0)),l=o.slice(72);if(!Number.isSafeInteger(c)||!await _t(r,J(void 0,c,l),s))return;let u=Rt(l);return u===void 0?void 0:{seq:c,value:u}}catch{return}})),s;for(let e of o)e!==void 0&&(s===void 0||e.seq>s.seq)&&(s=e);return s?.value},close:()=>void 0}}const Ht=Uint8Array.from([48,42,48,5,6,3,43,101,112,3,33,0]),Ut=(e,t,n)=>{if(n.length!==32||e.length!==64)return!1;try{return(0,p.verify)(null,t,(0,p.createPublicKey)({key:Buffer.from(q([Ht,n])),format:`der`,type:`spki`}),e)}catch{return!1}};async function Wt(e){return new(await(import(`bittorrent-dht`))).default({verify:e})}function $(e){return Buffer.from(e.buffer,e.byteOffset,e.byteLength)}function Gt(e){if(e!==void 0)return new Promise((t,n)=>{e.aborted&&n(Error(`aborted`)),e.addEventListener(`abort`,()=>n(Error(`aborted`)),{once:!0})})}async function Kt(e={}){let t=await(e.createDht??Wt)(Ut),n=t=>e.onError?.(t);t.on(`error`,n),t.on(`warning`,n);let r=!1;return{async put(e,n,i,a){if(r)throw Error(`DHT store closed`);if(n.length>999)throw Error(`record value exceeds 999 bytes`);let o=await vt(e.key,e.salt,Math.floor(i/1e3),n),s=new Promise((n,r)=>{t.put({k:$(o.k),salt:$(e.salt),seq:o.seq,v:$(o.v),sig:$(o.sig)},(e,t,i)=>{e===null?i===0?r(Error(`no DHT node took the record`)):n():r(e)})}),c=Gt(a);await(c===void 0?s:Promise.race([s,c]))},async get(e,n,i){if(r||i.aborted)return;let a=(0,p.createHash)(`sha1`).update(q([e,n])).digest(),o=new Promise(e=>{t.get(a,{salt:$(n),cache:!1},(t,n)=>e(t===null?n:null))});try{let t=await Promise.race([o,Gt(i)]);if(t?.v===void 0||t.k===void 0||t.sig===void 0)return;let r={k:new Uint8Array(t.k),salt:n,seq:t.seq??0,v:new Uint8Array(t.v),sig:new Uint8Array(t.sig)};return await Y(r,e,n)?r.v:void 0}catch{return}},close(){r||(r=!0,t.destroy())}}}const qt=/^[0-9a-f]{64}$/,Jt=/^[0-9a-f]{128}$/;function Yt(e){return 2e4+((e[0]??0)<<8|(e[1]??0))%1e4}function Xt(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t)}function Zt(e){try{let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e+=1)n[e]=t.charCodeAt(e);return n}catch{return}}function Qt(e){if(typeof e!=`object`||!e)return;let t=e;if(!(typeof t.id!=`string`||!qt.test(t.id))&&!(typeof t.pubkey!=`string`||!qt.test(t.pubkey))&&!(typeof t.sig!=`string`||!Jt.test(t.sig))&&!(typeof t.kind!=`number`||!Number.isInteger(t.kind))&&!(typeof t.created_at!=`number`||!Number.isInteger(t.created_at))&&!(typeof t.content!=`string`||t.content.length>49152)&&!(!Array.isArray(t.tags)||t.tags.length!==0))return{id:t.id,pubkey:t.pubkey,sig:t.sig,kind:t.kind,created_at:t.created_at,content:t.content,tags:[]}}var $t=class{options;messages=new Set;absents=new Set;outbound=new Map;inbound=new Map;subscription;chain=Promise.resolve();seen=new Set;routes=[];epoch;timer;closed=!1;constructor(e){this.options=e}now(){return(this.options.now??Date.now)()}report(e){this.options.onError?.(e instanceof Error?e:Error(String(e)))}enqueue(e){this.chain=this.chain.then(e).catch(e=>this.report(e))}whenIdle(){return this.chain}declarePresence(){}declarePeers(e){this.enqueue(()=>this.derive(e))}async derive(e){if(this.closed)return;this.routes=e;let t=(0,u.rendezvousEpoch)(this.now()),n=new Map,r=new Map;for(let i of e){let e=await i.rendezvous.signingSeed(`outbound`,t,`signal`),a=Yt(await i.rendezvous.tag(`nostr-kind`,`outbound`,t));n.set(i.outbound,{route:i,epoch:t,secretKey:e,kind:a});for(let e of[t-1,t,t+1]){let t=await i.rendezvous.signingSeed(`inbound`,e,`signal`),n=Yt(await i.rendezvous.tag(`nostr-kind`,`inbound`,e));r.set((0,m.getPublicKey)(t),{route:i,epoch:e,kind:n})}}this.closed||(this.outbound=n,this.inbound=r,this.epoch=t,this.subscription?.close(),this.subscription=r.size===0?void 0:this.options.pool.subscribe({kinds:[...new Set([...r.values()].map(e=>e.kind))],authors:[...r.keys()],since:Math.floor(this.now()/1e3)-(this.options.maxEventAgeS??120)},e=>this.enqueue(()=>this.receive(e))),this.timer!==void 0&&clearTimeout(this.timer),this.timer=setTimeout(()=>this.declarePeers(this.routes),Math.max(0,(t+1)*u.RENDEZVOUS_EPOCH_MS-this.now())),this.timer.unref?.())}send(e,t){this.closed||this.enqueue(async()=>{if(this.closed)return;this.epoch!==void 0&&(0,u.rendezvousEpoch)(this.now())!==this.epoch&&await this.derive(this.routes);let n=this.outbound.get(e);if(n===void 0){for(let t of this.absents)t(e);return}let r=new TextEncoder().encode(JSON.stringify(t)).length;if(r>32768)throw Error(`signal too large for a Nostr event`);let i=Tt(t,Math.ceil(r/512)*512),a=await n.route.rendezvous.sealRecord(i,n.epoch,`signal`),o=(0,m.finalizeEvent)({kind:n.kind,created_at:Math.floor(this.now()/1e3),tags:[],content:Xt(a)},n.secretKey);if(!await this.options.pool.publish(o))throw Error(`no Nostr relay took the signal`)})}async receive(e){if(this.closed)return;let t=Qt(e);if(t===void 0||this.seen.has(t.id))return;let n=this.inbound.get(t.pubkey);if(n===void 0||t.kind!==n.kind||Math.abs(Math.floor(this.now()/1e3)-t.created_at)>(this.options.maxEventAgeS??120)||!(0,m.verifyEvent)(t))return;this.remember(t.id);let r=Zt(t.content);if(r===void 0)return;let i=await n.route.rendezvous.openRecord(r,n.epoch,`signal`);if(i?.epoch!==n.epoch)return;let a=X(i.hints);if(!(a===void 0||this.closed))for(let e of this.messages)e(n.route.inbound,a)}remember(e){if(this.seen.add(e),this.seen.size<=2048)return;let t=this.seen.values().next().value;t!==void 0&&this.seen.delete(t)}onMessage(e){return this.messages.add(e),()=>this.messages.delete(e)}onAbsent(e){return this.absents.add(e),()=>this.absents.delete(e)}close(){this.closed||(this.closed=!0,this.timer!==void 0&&clearTimeout(this.timer),this.subscription?.close(),this.messages.clear(),this.absents.clear(),this.options.pool.close())}};function en(e,t){return e.kinds.includes(t.kind)&&e.authors.includes(t.pubkey)}function tn(){let e=new Set,t=[],n=t=>{let n=JSON.parse(JSON.stringify(t));queueMicrotask(()=>{for(let t of[...e])en(t.filter,n)&&t.onEvent(n)})};return{pool:()=>{let r=new Set;return{publish:e=>{let r=Qt(e);return r===void 0||!(0,m.verifyEvent)(r)?Promise.resolve(!1):(t.push(JSON.parse(JSON.stringify(e))),n(e),Promise.resolve(!0))},subscribe:(t,n)=>{let i={filter:t,onEvent:n};return e.add(i),r.add(i),{close:()=>{e.delete(i),r.delete(i)}}},close:()=>{for(let t of r)e.delete(t);r.clear()}}},events:t,inject:e=>n(e)}}function nn(e){if(e.length===0)throw Error(`a Nostr relay pool needs at least one relay`);let t=new ne.SimplePool,n=[...e];return{publish:async e=>(await Promise.allSettled(t.publish(n,e))).some(e=>e.status===`fulfilled`),subscribe:(e,r)=>t.subscribeMany(n,{kinds:[...e.kinds],authors:[...e.authors],...e.since===void 0?{}:{since:e.since}},{onevent:e=>r(e)}),close:()=>t.destroy()}}const rn=[`wss://relay.damus.io`,`wss://nos.lol`,`wss://relay.primal.net`],an=[`https://pkarr.pubky.app`,`https://pkarr.pubky.org`,`https://dns.iroh.link/pkarr`];exports.DEFAULT_MAX_PUBLISH_JITTER_MS=9e4,exports.DEFAULT_NOSTR_RELAYS=rn,exports.DEFAULT_PKARR_RELAYS=an,exports.DeviceMeshNode=Le,exports.DiscoveringMeshRelay=ut,exports.MAX_CACHED_CANDIDATES=3,exports.MAX_MESH_MESSAGE_CHARS=D,exports.MESH_MDNS_SERVICE=U,exports.MeshDht=jt,exports.MeshLinkEndedError=A,exports.MeshMdns=rt,exports.NostrMeshRelay=$t,exports.RtcChannel=y,exports.RtcPeer=b,exports.WebRtcTransport=Te,exports.WsMeshRelayClient=ze,exports.WsSignalingClient=Ae,exports.addressCacheSource=Be,exports.createInMemoryItemNetwork=Pt,exports.createInMemoryMdnsBus=it,exports.createInMemoryMeshAddressCache=Ue,exports.createInMemoryMeshRelayHub=Re,exports.createInMemoryNostrHub=tn,exports.createInMemorySignalingPair=De,exports.createNostrRelayPool=nn,exports.createPkarrRelayStore=Vt,exports.handoffGrantFrame=fe,exports.judgeHandoffGrant=de,exports.loadDataChannel=g,exports.localInterfaceAddresses=$e,exports.localProofFrame=he,exports.paddedInstanceCount=nt,exports.startLanMeshRelay=dt,exports.startMainlineDhtStore=Kt,exports.startMeshLanListener=Ze;
|