diff --git a/packages/app/package.json b/packages/app/package.json index c7d99aea..5742d707 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -38,6 +38,7 @@ "react-tag-input-component": "^2.0.2", "react-textarea-autosize": "^8.4.0", "recharts": "^2.8.0", + "socket.io-client": "^4.7.2", "three": "^0.157.0", "use-long-press": "^3.2.0", "use-sync-external-store": "^1.2.0", diff --git a/packages/app/src/Element/Event/Create/util.ts b/packages/app/src/Element/Event/Create/util.ts index 684ce961..91b880c3 100644 --- a/packages/app/src/Element/Event/Create/util.ts +++ b/packages/app/src/Element/Event/Create/util.ts @@ -1,5 +1,6 @@ import { NostrEvent, OkResponse, SystemInterface } from "@snort/system"; import { removeUndefined } from "@snort/shared"; +import {getWebRtcPool} from "@/webrtc"; export async function sendEventToRelays( system: SystemInterface, @@ -8,6 +9,7 @@ export async function sendEventToRelays( setResults?: (x: Array) => void, ) { console.log("sendEventToRelays", ev, customRelays); + getWebRtcPool()?.send(ev); if (customRelays) { return removeUndefined( await Promise.all( diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 2b7fdd6e..347a8a4a 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -1,6 +1,7 @@ import "./index.css"; import "@szhsin/react-menu/dist/index.css"; import "./fonts/inter.css"; +import "./webrtc"; import { compress, diff --git a/packages/app/src/webrtc/WebRTCConnection.ts b/packages/app/src/webrtc/WebRTCConnection.ts new file mode 100644 index 00000000..74658f40 --- /dev/null +++ b/packages/app/src/webrtc/WebRTCConnection.ts @@ -0,0 +1,106 @@ +import { Socket } from "socket.io-client"; +import EventEmitter from "eventemitter3"; + +export class WebRTCConnection extends EventEmitter { + private peerConnection: RTCPeerConnection; + private dataChannel: RTCDataChannel; + + constructor(private socket: Socket, configuration: RTCConfiguration, public peerId: string) { + super(); + this.peerConnection = new RTCPeerConnection(configuration); + this.dataChannel = this.peerConnection.createDataChannel("data"); + this.registerPeerConnectionEvents(); + this.setupDataChannel(); + } + + private log(...args: any[]): void { + console.log(this.peerId, ...args); + } + + public async handleOffer(offer: RTCSessionDescriptionInit): Promise { + this.log('Received offer', offer); + await this.peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); + await this.sendLocalDescription('answer'); + } + + public async handleAnswer(answer: RTCSessionDescriptionInit): Promise { + this.log('Received answer', answer); + await this.peerConnection.setRemoteDescription(new RTCSessionDescription(answer)); + } + + public handleCandidate(candidate: RTCIceCandidateInit): void { + this.log('Received ICE candidate', candidate); + this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); + } + + private async sendLocalDescription(type: 'offer' | 'answer'): Promise { + let description; + if (type === 'offer') { + description = await this.peerConnection.createOffer(); + } else { + description = await this.peerConnection.createAnswer(); + } + await this.peerConnection.setLocalDescription(description); + this.socket.emit(type, { [type]: description, recipient: this.peerId }); + this.log(`Sent ${type}`, description); + } + + private setupDataChannel(): void { + this.dataChannel.onopen = () => this.log('Data channel opened'); + this.dataChannel.onclose = () => this.log('Data channel closed'); + this.dataChannel.onmessage = event => this.handleDataChannelMessage(event); + } + + private handleDataChannelMessage(event: MessageEvent): void { + this.log(`-> "${event.data}"`); + if (event.data === 'ping') { + this.send('pong'); + } else { + try { + const data = JSON.parse(event.data); + this.emit('event', data); + } catch (e) { + // Ignore + } + } + } + + public send(data: any): void { + if (this.dataChannel.readyState === 'open') { + this.log(`<- "${data}"`); + this.dataChannel.send(data); + } + } + + public async handleHello(): Promise { + if (this.peerConnection.connectionState === 'new') { + await this.sendLocalDescription('offer'); + } + } + + private registerPeerConnectionEvents(): void { + this.peerConnection.onicecandidate = event => { + if (event.candidate) { + this.log('Local ICE candidate:', event.candidate); + this.socket.emit('candidate', { candidate: event.candidate.toJSON(), recipient: this.peerId }); + } + }; + + this.peerConnection.oniceconnectionstatechange = () => { + this.log('ICE Connection State Change:', this.peerConnection.iceConnectionState); + }; + + this.peerConnection.onconnectionstatechange = () => { + this.log('WebRTC Connection State Change:', this.peerConnection.connectionState); + }; + + this.peerConnection.ondatachannel = event => { + this.dataChannel = event.channel; + this.setupDataChannel(); + }; + } + + public close(): void { + this.peerConnection.close(); + } +} diff --git a/packages/app/src/webrtc/WebRTCPool.ts b/packages/app/src/webrtc/WebRTCPool.ts new file mode 100644 index 00000000..fc89b3c0 --- /dev/null +++ b/packages/app/src/webrtc/WebRTCPool.ts @@ -0,0 +1,86 @@ +import {io, Socket} from 'socket.io-client'; +import {WebRTCConnection} from '@/webrtc/WebRTCConnection'; +import EventEmitter from "eventemitter3"; + +const MAX_CONNECTIONS = 5; + +class WebRTCPool extends EventEmitter { + private signalingServer: Socket; + private peers: Map = new Map(); + private configuration: RTCConfiguration; + private peerId: string; + + constructor(serverUrl: string, configuration: RTCConfiguration = {}, peerId: string) { + super(); + this.signalingServer = io(serverUrl); + this.configuration = configuration; + this.peerId = peerId; + this.registerSocketEvents(); + } + + private sayHello(): void { + this.signalingServer.emit('hello', this.peerId); + } + + public send(data: any, recipients?: string[]): void { + this.peers.forEach(conn => { + if (!recipients || recipients.includes(conn.peerId)) { + try { + conn.send(typeof data === 'string' ? data : JSON.stringify(data)); + } catch (e) { + console.error(e); + } + } + }); + } + + public createConnection(peerId: string): WebRTCConnection { + if (this.peers.size >= MAX_CONNECTIONS) { + throw new Error('Maximum connections reached'); + } + const connection = new WebRTCConnection(this.signalingServer, this.configuration, peerId); + connection.on('event', (event: any) => this.emit('event', event)); + this.peers.set(peerId, connection); + return connection; + } + + private handleConnectionEvent(sender: string, action: (connection: WebRTCConnection) => Promise): void { + if (sender === this.peerId || this.peers.size >= MAX_CONNECTIONS) return; + const connection = this.peers.get(sender) ?? this.createConnection(sender); + action(connection); + } + + private registerSocketEvents(): void { + this.signalingServer.on('connect', () => { + console.log('Connected to signaling server'); + this.sayHello(); + }); + + this.signalingServer.on('offer', ({offer, sender}: { offer: RTCSessionDescriptionInit; sender: string }) => { + this.handleConnectionEvent(sender, async conn => await conn.handleOffer(offer)); + }); + + this.signalingServer.on('answer', ({answer, sender}: { answer: RTCSessionDescriptionInit; sender: string }) => { + this.handleConnectionEvent(sender, async conn => await conn.handleAnswer(answer)); + }); + + this.signalingServer.on('candidate', ({candidate, sender}: { candidate: RTCIceCandidateInit; sender: string }) => { + this.handleConnectionEvent(sender, conn => conn.handleCandidate(candidate)); + }); + + this.signalingServer.on('hello', (sender: string) => { + console.log('Received hello from', sender); + this.handleConnectionEvent(sender, conn => conn.handleHello()); + }); + } + + public close(): void { + console.log('closing pool'); + this.signalingServer.close(); + for (const conn of this.peers.values()) { + conn.close(); + } + } +} + +export default WebRTCPool; \ No newline at end of file diff --git a/packages/app/src/webrtc/index.ts b/packages/app/src/webrtc/index.ts new file mode 100644 index 00000000..a34f2393 --- /dev/null +++ b/packages/app/src/webrtc/index.ts @@ -0,0 +1,25 @@ +import {LoginStore} from "@/Login"; +import WebRTCPool from "@/webrtc/WebRTCPool"; + +let publicKey: string | undefined; +let pool: WebRTCPool | undefined; +let interval: NodeJS.Timeout | undefined; + +LoginStore.hook(() => { + const login = LoginStore.takeSnapshot(); + if (login.publicKey && !login.readonly && login.publicKey !== publicKey) { + publicKey = login.publicKey; + if (location.hostname === 'localhost') { + pool?.close(); + interval && clearInterval(interval); + pool = new WebRTCPool('http://localhost:3000', { + iceServers: [{ urls: 'stun:localhost:3478' }], + }, login.publicKey); + interval = setInterval(() => pool?.send('ping'), 10000); + } + } +}); + +export function getWebRtcPool(): WebRTCPool | undefined { + return pool; +} diff --git a/packages/system/src/nostr-system.ts b/packages/system/src/nostr-system.ts index d4d8038e..6d9c8326 100644 --- a/packages/system/src/nostr-system.ts +++ b/packages/system/src/nostr-system.ts @@ -193,7 +193,6 @@ export class NostrSystem extends EventEmitter implements Syst #onEvent(sub: string, ev: TaggedNostrEvent) { this.#relayMetrics.onEvent(ev.relays[0]); - this.emit("event", ev); if (!EventExt.isValid(ev)) { this.#log("Rejecting invalid event %O", ev); @@ -207,6 +206,8 @@ export class NostrSystem extends EventEmitter implements Syst } } + this.emit("event", ev); + for (const [, v] of this.Queries) { v.handleEvent(sub, ev); } diff --git a/packages/webrtc-server/README.md b/packages/webrtc-server/README.md new file mode 100644 index 00000000..4c8ed413 --- /dev/null +++ b/packages/webrtc-server/README.md @@ -0,0 +1,8 @@ +# webrtc-server + +``` +yarn +yarn start +``` + +Websocket (socket.io) based signaling server for WebRTC. \ No newline at end of file diff --git a/packages/webrtc-server/index.js b/packages/webrtc-server/index.js new file mode 100644 index 00000000..acb02086 --- /dev/null +++ b/packages/webrtc-server/index.js @@ -0,0 +1,56 @@ +const PORT = process.env.PORT || 3000; +const io = require('socket.io')(PORT, { + cors: { + origin: "*", + methods: ["GET", "POST"] + } +}); + +const peerSocketMap = new Map(); +const socketPeerMap = new Map(); + +io.on('connection', socket => { + console.log(`New client connected, socket ID: ${socket.id}`); + + const emitToTarget = (eventType, data, recipient) => { + const targetSocketId = peerSocketMap.get(recipient); + const sender = socketPeerMap.get(socket.id); + if (sender && targetSocketId) { + io.to(targetSocketId).emit(eventType, {...data, sender}); + } + }; + + socket.on('offer', data => emitToTarget('offer', data, data.recipient)); + socket.on('answer', data => emitToTarget('answer', data, data.recipient)); + socket.on('candidate', data => emitToTarget('candidate', data, data.recipient)); + + socket.on('hello', peerId => { + console.log(`Received hello from ${peerId}`); + peerSocketMap.set(peerId, socket.id); + socketPeerMap.set(socket.id, peerId); + socket.broadcast.emit('hello', peerId); + }); + + socket.on('disconnect', () => { + peerSocketMap.delete(socketPeerMap.get(socket.id)); + socketPeerMap.delete(socket.id); + console.log(`Client disconnected, socket ID: ${socket.id}`); + }); +}); + +console.log(`Signaling server running on port ${PORT}`); + +const Ministun = require("ministun"); + +const stunConfig = { + udp4: true, + udp6: true, + port: 3478, + log: console.log, + err: console.err, + sw: true +}; + +const server = new Ministun(stunConfig); + +console.log(`STUN server running on port ${stunConfig.port}`) \ No newline at end of file diff --git a/packages/webrtc-server/package.json b/packages/webrtc-server/package.json new file mode 100644 index 00000000..512ccba4 --- /dev/null +++ b/packages/webrtc-server/package.json @@ -0,0 +1,11 @@ +{ + "name": "nostr-webrtc-server", + "dependencies": { + "ministun": "^1.0.6", + "nostr-tools": "^2.0.2", + "socket.io": "^4.7.2" + }, + "scripts": { + "start": "node index.js" + } +} diff --git a/yarn.lock b/yarn.lock index ad3f3349..973e39c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2557,7 +2557,14 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:~1.2.0": +"@noble/ciphers@npm:0.2.0": + version: 0.2.0 + resolution: "@noble/ciphers@npm:0.2.0" + checksum: fa6c14b66865cee83ee779a4ca4c8b39a6397425fae3f805d1766a2daf887d950d3771a68bb95e89cc3afbd595a219b44e8529aa4aaaad0ea5c030c1fb589ef4 + languageName: node + linkType: hard + +"@noble/curves@npm:1.2.0, @noble/curves@npm:^1.0.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" dependencies: @@ -2566,6 +2573,22 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:~1.1.0": + version: 1.1.0 + resolution: "@noble/curves@npm:1.1.0" + dependencies: + "@noble/hashes": 1.3.1 + checksum: 2658cdd3f84f71079b4e3516c47559d22cf4b55c23ac8ee9d2b1f8e5b72916d9689e59820e0f9d9cb4a46a8423af5b56dc6bb7782405c88be06a015180508db5 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.1": + version: 1.3.1 + resolution: "@noble/hashes@npm:1.3.1" + checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" @@ -2573,6 +2596,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:~1.3.1": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b + languageName: node + linkType: hard + "@noble/secp256k1@npm:^1.7.0": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" @@ -2832,6 +2862,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.1": + version: 1.3.1 + resolution: "@scure/bip32@npm:1.3.1" + dependencies: + "@noble/curves": ~1.1.0 + "@noble/hashes": ~1.3.1 + "@scure/base": ~1.1.0 + checksum: 394d65f77a40651eba21a5096da0f4233c3b50d422864751d373fcf142eeedb94a1149f9ab1dbb078086dab2d0bc27e2b1afec8321bf22d4403c7df2fea5bfe2 + languageName: node + linkType: hard + "@scure/bip32@npm:^1.3.0": version: 1.3.2 resolution: "@scure/bip32@npm:1.3.2" @@ -2843,7 +2884,7 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:^1.1.1": +"@scure/bip39@npm:1.2.1, @scure/bip39@npm:^1.1.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" dependencies: @@ -2943,6 +2984,7 @@ __metadata: react-textarea-autosize: ^8.4.0 recharts: ^2.8.0 rollup-plugin-visualizer: ^5.9.2 + socket.io-client: ^4.7.2 tailwindcss: ^3.3.3 three: ^0.157.0 tinybench: ^2.5.1 @@ -3045,6 +3087,13 @@ __metadata: languageName: unknown linkType: soft +"@socket.io/component-emitter@npm:~3.1.0": + version: 3.1.0 + resolution: "@socket.io/component-emitter@npm:3.1.0" + checksum: db069d95425b419de1514dffe945cc439795f6a8ef5b9465715acf5b8b50798e2c91b8719cbf5434b3fe7de179d6cdcd503c277b7871cb3dd03febb69bdd50fa + languageName: node + linkType: hard + "@stablelib/binary@npm:^1.0.1": version: 1.0.1 resolution: "@stablelib/binary@npm:1.0.1" @@ -3340,6 +3389,22 @@ __metadata: languageName: node linkType: hard +"@types/cookie@npm:^0.4.1": + version: 0.4.1 + resolution: "@types/cookie@npm:0.4.1" + checksum: 3275534ed69a76c68eb1a77d547d75f99fedc80befb75a3d1d03662fb08d697e6f8b1274e12af1a74c6896071b11510631ba891f64d30c78528d0ec45a9c1a18 + languageName: node + linkType: hard + +"@types/cors@npm:^2.8.12": + version: 2.8.17 + resolution: "@types/cors@npm:2.8.17" + dependencies: + "@types/node": "*" + checksum: 469bd85e29a35977099a3745c78e489916011169a664e97c4c3d6538143b0a16e4cc72b05b407dc008df3892ed7bf595f9b7c0f1f4680e169565ee9d64966bde + languageName: node + linkType: hard + "@types/d3-array@npm:^3.0.3": version: 3.2.1 resolution: "@types/d3-array@npm:3.2.1" @@ -3553,6 +3618,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:>=10.0.0": + version: 20.10.5 + resolution: "@types/node@npm:20.10.5" + dependencies: + undici-types: ~5.26.4 + checksum: e216b679f545a8356960ce985a0e53c3a58fff0eacd855e180b9e223b8db2b5bd07b744a002b8c1f0c37f9194648ab4578533b5c12df2ec10cc02f61d20948d2 + languageName: node + linkType: hard + "@types/parse-torrent-file@npm:*": version: 4.0.6 resolution: "@types/parse-torrent-file@npm:4.0.6" @@ -3997,6 +4071,16 @@ __metadata: languageName: node linkType: hard +"accepts@npm:~1.3.4": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: ~2.1.34 + negotiator: 0.6.3 + checksum: 50c43d32e7b50285ebe84b613ee4a3aa426715a7d131b65b786e2ead0fd76b6b60091b9916d3478a75f11f162628a2139991b6c03ab3f1d9ab7c86075dc8eab4 + languageName: node + linkType: hard + "accessor-fn@npm:1": version: 1.5.0 resolution: "accessor-fn@npm:1.5.0" @@ -4444,6 +4528,13 @@ __metadata: languageName: node linkType: hard +"base64id@npm:2.0.0, base64id@npm:~2.0.0": + version: 2.0.0 + resolution: "base64id@npm:2.0.0" + checksum: 581b1d37e6cf3738b7ccdd4d14fe2bfc5c238e696e2720ee6c44c183b838655842e22034e53ffd783f872a539915c51b0d4728a49c7cc678ac5a758e00d62168 + languageName: node + linkType: hard + "bech32@npm:^1.1.2": version: 1.1.4 resolution: "bech32@npm:1.1.4" @@ -4851,6 +4942,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:~0.4.1": + version: 0.4.2 + resolution: "cookie@npm:0.4.2" + checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b + languageName: node + linkType: hard + "core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.33.1": version: 3.33.2 resolution: "core-js-compat@npm:3.33.2" @@ -4860,6 +4958,16 @@ __metadata: languageName: node linkType: hard +"cors@npm:~2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: ^4 + vary: ^1 + checksum: ced838404ccd184f61ab4fdc5847035b681c90db7ac17e428f3d81d69e2989d2b680cc254da0e2554f5ed4f8a341820a1ce3d1c16b499f6e2f47a1b9b07b5006 + languageName: node + linkType: hard + "create-jest@npm:^29.7.0": version: 29.7.0 resolution: "create-jest@npm:29.7.0" @@ -5169,7 +5277,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5420,6 +5528,44 @@ __metadata: languageName: node linkType: hard +"engine.io-client@npm:~6.5.2": + version: 6.5.3 + resolution: "engine.io-client@npm:6.5.3" + dependencies: + "@socket.io/component-emitter": ~3.1.0 + debug: ~4.3.1 + engine.io-parser: ~5.2.1 + ws: ~8.11.0 + xmlhttprequest-ssl: ~2.0.0 + checksum: a72596fae99afbdb899926fccdb843f8fa790c69085b881dde121285a6935da2c2c665ebe88e0e6aa4285637782df84ac882084ff4892ad2430b059fc0045db0 + languageName: node + linkType: hard + +"engine.io-parser@npm:~5.2.1": + version: 5.2.1 + resolution: "engine.io-parser@npm:5.2.1" + checksum: 55b0e8e18500f50c1573675c53597c5552554ead08d3f30ff19fde6409e48f882a8e01f84e9772cd155c18a1d653d06f6bf57b4e1f8b834c63c9eaf3b657b88e + languageName: node + linkType: hard + +"engine.io@npm:~6.5.2": + version: 6.5.4 + resolution: "engine.io@npm:6.5.4" + dependencies: + "@types/cookie": ^0.4.1 + "@types/cors": ^2.8.12 + "@types/node": ">=10.0.0" + accepts: ~1.3.4 + base64id: 2.0.0 + cookie: ~0.4.1 + cors: ~2.8.5 + debug: ~4.3.1 + engine.io-parser: ~5.2.1 + ws: ~8.11.0 + checksum: d5b55cbac718c5b1c10800314379923f8c7ef9e3a8a60c6827ed86303d1154b81d354a89fdecf4cbb773515c82c84a98d3c791ff88279393b53625dd67299d30 + languageName: node + linkType: hard + "entities@npm:^4.4.0": version: 4.5.0 resolution: "entities@npm:4.5.0" @@ -7991,7 +8137,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12": +"mime-types@npm:^2.1.12, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -8108,6 +8254,13 @@ __metadata: languageName: node linkType: hard +"ministun@npm:^1.0.6": + version: 1.0.6 + resolution: "ministun@npm:1.0.6" + checksum: 8eeb3d47a1d0cdd4d348a23d32811ab629c3e566fe88481a5d2dc9cc07afb0b8a34c353b1d16b5a02b01a1a694e9f8e447f81544621bcc6c962adf41f6833d34 + languageName: node + linkType: hard + "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -8173,7 +8326,7 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:^0.6.3": +"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 @@ -8280,6 +8433,43 @@ __metadata: languageName: node linkType: hard +"nostr-tools@npm:^2.0.2": + version: 2.0.2 + resolution: "nostr-tools@npm:2.0.2" + dependencies: + "@noble/ciphers": 0.2.0 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.1 + "@scure/base": 1.1.1 + "@scure/bip32": 1.3.1 + "@scure/bip39": 1.2.1 + nostr-wasm: v0.0.3 + peerDependencies: + typescript: ">=5.0.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 2112eb37e4a710f2fc2d33b40290a28324d213d9d7051bb364ecba634d92eb1cb24ec37303876cde855cd2353213d273a8a49b31b400f9abb1b301ee54d71c37 + languageName: node + linkType: hard + +"nostr-wasm@npm:v0.0.3": + version: 0.0.3 + resolution: "nostr-wasm@npm:0.0.3" + checksum: 28100b46610293be9f0cc0036f8dce1ef69493a7d007f79c7187e60ae0f9b5c21a124738cbe6a876e503dd422185eb6ae7863a4b6a1d3fc8ad10c5a827a3c212 + languageName: node + linkType: hard + +"nostr-webrtc-server@workspace:packages/webrtc-server": + version: 0.0.0-use.local + resolution: "nostr-webrtc-server@workspace:packages/webrtc-server" + dependencies: + ministun: ^1.0.6 + nostr-tools: ^2.0.2 + socket.io: ^4.7.2 + languageName: unknown + linkType: soft + "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -8296,7 +8486,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": +"object-assign@npm:^4, object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f @@ -9939,6 +10129,52 @@ __metadata: languageName: node linkType: hard +"socket.io-adapter@npm:~2.5.2": + version: 2.5.2 + resolution: "socket.io-adapter@npm:2.5.2" + dependencies: + ws: ~8.11.0 + checksum: 481251c3547221e57eb5cb247d0b1a3cde4d152a4c1c9051cc887345a7770e59f3b47f1011cac4499e833f01fcfc301ed13c4ec6e72f7dbb48a476375a6344cd + languageName: node + linkType: hard + +"socket.io-client@npm:^4.7.2": + version: 4.7.2 + resolution: "socket.io-client@npm:4.7.2" + dependencies: + "@socket.io/component-emitter": ~3.1.0 + debug: ~4.3.2 + engine.io-client: ~6.5.2 + socket.io-parser: ~4.2.4 + checksum: 8f0ab6b623e014d889bae0cd847ef7826658e8f131bd9367ee5ae4404bb52a6d7b1755b8fbe8e68799b60e92149370a732b381f913b155e40094facb135cd088 + languageName: node + linkType: hard + +"socket.io-parser@npm:~4.2.4": + version: 4.2.4 + resolution: "socket.io-parser@npm:4.2.4" + dependencies: + "@socket.io/component-emitter": ~3.1.0 + debug: ~4.3.1 + checksum: 61540ef99af33e6a562b9effe0fad769bcb7ec6a301aba5a64b3a8bccb611a0abdbe25f469933ab80072582006a78ca136bf0ad8adff9c77c9953581285e2263 + languageName: node + linkType: hard + +"socket.io@npm:^4.7.2": + version: 4.7.2 + resolution: "socket.io@npm:4.7.2" + dependencies: + accepts: ~1.3.4 + base64id: ~2.0.0 + cors: ~2.8.5 + debug: ~4.3.2 + engine.io: ~6.5.2 + socket.io-adapter: ~2.5.2 + socket.io-parser: ~4.2.4 + checksum: 2dfac8983a75e100e889c3dafc83b21b75a9863d0d1ee79cdc60c4391d5d9dffcf3a86fc8deca7568032bc11c2572676335fd2e469c7982f40d19f1141d4b266 + languageName: node + linkType: hard + "socks-proxy-agent@npm:^8.0.1": version: 8.0.2 resolution: "socks-proxy-agent@npm:8.0.2" @@ -10975,6 +11211,13 @@ __metadata: languageName: node linkType: hard +"vary@npm:^1": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: ae0123222c6df65b437669d63dfa8c36cee20a504101b2fcd97b8bf76f91259c17f9f2b4d70a1e3c6bbcee7f51b28392833adb6b2770b23b01abec84e369660b + languageName: node + linkType: hard + "victory-vendor@npm:^36.6.8": version: 36.6.12 resolution: "victory-vendor@npm:36.6.12" @@ -11548,6 +11791,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:~8.11.0": + version: 8.11.0 + resolution: "ws@npm:8.11.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 316b33aba32f317cd217df66dbfc5b281a2f09ff36815de222bc859e3424d83766d9eb2bd4d667de658b6ab7be151f258318fb1da812416b30be13103e5b5c67 + languageName: node + linkType: hard + "xml-name-validator@npm:^4.0.0": version: 4.0.0 resolution: "xml-name-validator@npm:4.0.0" @@ -11562,6 +11820,13 @@ __metadata: languageName: node linkType: hard +"xmlhttprequest-ssl@npm:~2.0.0": + version: 2.0.0 + resolution: "xmlhttprequest-ssl@npm:2.0.0" + checksum: 1e98df67f004fec15754392a131343ea92e6ab5ac4d77e842378c5c4e4fd5b6a9134b169d96842cc19422d77b1606b8df84a5685562b3b698cb68441636f827e + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8"