├── .gitignore ├── README.md ├── favicon.svg ├── index.html ├── main.js ├── package-lock.json ├── package.json └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Video Chat with WebRTC and Firebase 2 | 3 | Build a 1-to-1 video chat feature with WebRTC, Firestore, and JavaScript. 4 | 5 | Watch the [WebRTC Explanation on YouTube](https://youtu.be/WmR9IMUD_CY) and follow the full [WebRTC Firebase Tutorial](https://fireship.io/lessons/webrtc-firebase-video-chat) on Fireship.io. 6 | 7 | 8 | ## Usage 9 | 10 | Update the firebase project config in the main.js file. 11 | 12 | ``` 13 | git clone 14 | npm install 15 | 16 | npm run dev 17 | ``` 18 | -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebRTC Demo 8 | 9 | 10 |

1. Start your Webcam

11 |
12 | 13 |

Local Stream

14 | 15 |
16 | 17 |

Remote Stream

18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 |

2. Create a new Call

26 | 27 | 28 |

3. Join a Call

29 |

Answer the call from a different browser window or device

30 | 31 | 32 | 33 | 34 |

4. Hangup

35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | 3 | import firebase from 'firebase/app'; 4 | import 'firebase/firestore'; 5 | 6 | const firebaseConfig = { 7 | // your config 8 | }; 9 | 10 | if (!firebase.apps.length) { 11 | firebase.initializeApp(firebaseConfig); 12 | } 13 | const firestore = firebase.firestore(); 14 | 15 | const servers = { 16 | iceServers: [ 17 | { 18 | urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'], 19 | }, 20 | ], 21 | iceCandidatePoolSize: 10, 22 | }; 23 | 24 | // Global State 25 | const pc = new RTCPeerConnection(servers); 26 | let localStream = null; 27 | let remoteStream = null; 28 | 29 | // HTML elements 30 | const webcamButton = document.getElementById('webcamButton'); 31 | const webcamVideo = document.getElementById('webcamVideo'); 32 | const callButton = document.getElementById('callButton'); 33 | const callInput = document.getElementById('callInput'); 34 | const answerButton = document.getElementById('answerButton'); 35 | const remoteVideo = document.getElementById('remoteVideo'); 36 | const hangupButton = document.getElementById('hangupButton'); 37 | 38 | // 1. Setup media sources 39 | 40 | webcamButton.onclick = async () => { 41 | localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); 42 | remoteStream = new MediaStream(); 43 | 44 | // Push tracks from local stream to peer connection 45 | localStream.getTracks().forEach((track) => { 46 | pc.addTrack(track, localStream); 47 | }); 48 | 49 | // Pull tracks from remote stream, add to video stream 50 | pc.ontrack = (event) => { 51 | event.streams[0].getTracks().forEach((track) => { 52 | remoteStream.addTrack(track); 53 | }); 54 | }; 55 | 56 | webcamVideo.srcObject = localStream; 57 | remoteVideo.srcObject = remoteStream; 58 | 59 | callButton.disabled = false; 60 | answerButton.disabled = false; 61 | webcamButton.disabled = true; 62 | }; 63 | 64 | // 2. Create an offer 65 | callButton.onclick = async () => { 66 | // Reference Firestore collections for signaling 67 | const callDoc = firestore.collection('calls').doc(); 68 | const offerCandidates = callDoc.collection('offerCandidates'); 69 | const answerCandidates = callDoc.collection('answerCandidates'); 70 | 71 | callInput.value = callDoc.id; 72 | 73 | // Get candidates for caller, save to db 74 | pc.onicecandidate = (event) => { 75 | event.candidate && offerCandidates.add(event.candidate.toJSON()); 76 | }; 77 | 78 | // Create offer 79 | const offerDescription = await pc.createOffer(); 80 | await pc.setLocalDescription(offerDescription); 81 | 82 | const offer = { 83 | sdp: offerDescription.sdp, 84 | type: offerDescription.type, 85 | }; 86 | 87 | await callDoc.set({ offer }); 88 | 89 | // Listen for remote answer 90 | callDoc.onSnapshot((snapshot) => { 91 | const data = snapshot.data(); 92 | if (!pc.currentRemoteDescription && data?.answer) { 93 | const answerDescription = new RTCSessionDescription(data.answer); 94 | pc.setRemoteDescription(answerDescription); 95 | } 96 | }); 97 | 98 | // When answered, add candidate to peer connection 99 | answerCandidates.onSnapshot((snapshot) => { 100 | snapshot.docChanges().forEach((change) => { 101 | if (change.type === 'added') { 102 | const candidate = new RTCIceCandidate(change.doc.data()); 103 | pc.addIceCandidate(candidate); 104 | } 105 | }); 106 | }); 107 | 108 | hangupButton.disabled = false; 109 | }; 110 | 111 | // 3. Answer the call with the unique ID 112 | answerButton.onclick = async () => { 113 | const callId = callInput.value; 114 | const callDoc = firestore.collection('calls').doc(callId); 115 | const answerCandidates = callDoc.collection('answerCandidates'); 116 | const offerCandidates = callDoc.collection('offerCandidates'); 117 | 118 | pc.onicecandidate = (event) => { 119 | event.candidate && answerCandidates.add(event.candidate.toJSON()); 120 | }; 121 | 122 | const callData = (await callDoc.get()).data(); 123 | 124 | const offerDescription = callData.offer; 125 | await pc.setRemoteDescription(new RTCSessionDescription(offerDescription)); 126 | 127 | const answerDescription = await pc.createAnswer(); 128 | await pc.setLocalDescription(answerDescription); 129 | 130 | const answer = { 131 | type: answerDescription.type, 132 | sdp: answerDescription.sdp, 133 | }; 134 | 135 | await callDoc.update({ answer }); 136 | 137 | offerCandidates.onSnapshot((snapshot) => { 138 | snapshot.docChanges().forEach((change) => { 139 | console.log(change); 140 | if (change.type === 'added') { 141 | let data = change.doc.data(); 142 | pc.addIceCandidate(new RTCIceCandidate(data)); 143 | } 144 | }); 145 | }); 146 | }; 147 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtc-demo", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@firebase/analytics": { 8 | "version": "0.6.4", 9 | "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.6.4.tgz", 10 | "integrity": "sha512-Lhnk5pXeDKoPf1b2cggWQaqCtNq+jn6IkhHMIo+7VztVt3i8ovnGuhAn/0hLC+XxvVWJ9q6CXIoGStkwvecDoA==", 11 | "requires": { 12 | "@firebase/analytics-types": "0.4.0", 13 | "@firebase/component": "0.2.0", 14 | "@firebase/installations": "0.4.20", 15 | "@firebase/logger": "0.2.6", 16 | "@firebase/util": "0.3.4", 17 | "tslib": "^1.11.1" 18 | } 19 | }, 20 | "@firebase/analytics-types": { 21 | "version": "0.4.0", 22 | "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.4.0.tgz", 23 | "integrity": "sha512-Jj2xW+8+8XPfWGkv9HPv/uR+Qrmq37NPYT352wf7MvE9LrstpLVmFg3LqG6MCRr5miLAom5sen2gZ+iOhVDeRA==" 24 | }, 25 | "@firebase/app": { 26 | "version": "0.6.15", 27 | "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.6.15.tgz", 28 | "integrity": "sha512-VfULP09cci4xk+iAU6CB2CUNU/vbpAOoUleDdspXFphV9Yw36anb8RjaHGcN1DRR7LNb7vznNJXHk8FJhC8VWQ==", 29 | "requires": { 30 | "@firebase/app-types": "0.6.1", 31 | "@firebase/component": "0.2.0", 32 | "@firebase/logger": "0.2.6", 33 | "@firebase/util": "0.3.4", 34 | "dom-storage": "2.1.0", 35 | "tslib": "^1.11.1", 36 | "xmlhttprequest": "1.8.0" 37 | } 38 | }, 39 | "@firebase/app-types": { 40 | "version": "0.6.1", 41 | "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.1.tgz", 42 | "integrity": "sha512-L/ZnJRAq7F++utfuoTKX4CLBG5YR7tFO3PLzG1/oXXKEezJ0kRL3CMRoueBEmTCzVb/6SIs2Qlaw++uDgi5Xyg==" 43 | }, 44 | "@firebase/auth": { 45 | "version": "0.16.4", 46 | "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.16.4.tgz", 47 | "integrity": "sha512-zgHPK6/uL6+nAyG9zqammHTF1MQpAN7z/jVRLYkDZS4l81H08b2SzApLbRfW/fmy665xqb5MK7sVH0V1wsiCNw==", 48 | "requires": { 49 | "@firebase/auth-types": "0.10.2" 50 | } 51 | }, 52 | "@firebase/auth-interop-types": { 53 | "version": "0.1.5", 54 | "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.5.tgz", 55 | "integrity": "sha512-88h74TMQ6wXChPA6h9Q3E1Jg6TkTHep2+k63OWg3s0ozyGVMeY+TTOti7PFPzq5RhszQPQOoCi59es4MaRvgCw==" 56 | }, 57 | "@firebase/auth-types": { 58 | "version": "0.10.2", 59 | "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.10.2.tgz", 60 | "integrity": "sha512-0GMWVWh5TBCYIQfVerxzDsuvhoFpK0++O9LtP3FWkwYo7EAxp6w0cftAg/8ntU1E5Wg56Ry0b6ti/YGP6g0jlg==" 61 | }, 62 | "@firebase/component": { 63 | "version": "0.2.0", 64 | "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.2.0.tgz", 65 | "integrity": "sha512-QJJxMEzLRMWjujPBrrS32BScg1wmdY/dZWR8nAEzyC8WKQsNevYR9ZKLbBYxaN0umH9yf5C40kwmy+gI8jStPw==", 66 | "requires": { 67 | "@firebase/util": "0.3.4", 68 | "tslib": "^1.11.1" 69 | } 70 | }, 71 | "@firebase/database": { 72 | "version": "0.9.4", 73 | "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.9.4.tgz", 74 | "integrity": "sha512-Fw2bA4OyxAMqLy8xtoZKlUAuDWjjH/z4AInRTAzHxgegXDbu0UK+VM0pmY44RuM2cmnVY4aW6xLXAdDKTY1aJQ==", 75 | "requires": { 76 | "@firebase/auth-interop-types": "0.1.5", 77 | "@firebase/component": "0.2.0", 78 | "@firebase/database-types": "0.7.0", 79 | "@firebase/logger": "0.2.6", 80 | "@firebase/util": "0.3.4", 81 | "faye-websocket": "0.11.3", 82 | "tslib": "^1.11.1" 83 | } 84 | }, 85 | "@firebase/database-types": { 86 | "version": "0.7.0", 87 | "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.0.tgz", 88 | "integrity": "sha512-FduQmPpUUOHgbOt7/vWlC1ntSLMEqqYessdQ/ODd7RFWm53iVa0T1mpIDtNwqd8gW3k7cajjSjcLjfQGtvLGDg==", 89 | "requires": { 90 | "@firebase/app-types": "0.6.1" 91 | } 92 | }, 93 | "@firebase/firestore": { 94 | "version": "2.1.7", 95 | "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-2.1.7.tgz", 96 | "integrity": "sha512-sh+aX6udS8a+rwmlJO4zJwvoMC1D2x1CiPvj0nFYWhILRKWvztk/bOA3lT2FWcHY4kr/7x+CnqfwtiOSaufdNQ==", 97 | "requires": { 98 | "@firebase/component": "0.2.0", 99 | "@firebase/firestore-types": "2.1.0", 100 | "@firebase/logger": "0.2.6", 101 | "@firebase/util": "0.3.4", 102 | "@firebase/webchannel-wrapper": "0.4.1", 103 | "@grpc/grpc-js": "^1.0.0", 104 | "@grpc/proto-loader": "^0.5.0", 105 | "node-fetch": "2.6.1", 106 | "tslib": "^1.11.1" 107 | } 108 | }, 109 | "@firebase/firestore-types": { 110 | "version": "2.1.0", 111 | "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-2.1.0.tgz", 112 | "integrity": "sha512-jietErBWihMvJkqqEquQy5GgoEwzHnMXXC/TsVoe9FPysXm1/AeJS12taS7ZYvenAtyvL/AEJyKrRKRh4adcJQ==" 113 | }, 114 | "@firebase/functions": { 115 | "version": "0.6.2", 116 | "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.6.2.tgz", 117 | "integrity": "sha512-f+NxRd0k5RBPZUPj8lykeNMku8TpJTgZaRd3T6cXb8HbmSg/VY7uxQSGOXe11X2gSv/TvcwTQttViFzRMDs7CQ==", 118 | "requires": { 119 | "@firebase/component": "0.2.0", 120 | "@firebase/functions-types": "0.4.0", 121 | "@firebase/messaging-types": "0.5.0", 122 | "node-fetch": "2.6.1", 123 | "tslib": "^1.11.1" 124 | } 125 | }, 126 | "@firebase/functions-types": { 127 | "version": "0.4.0", 128 | "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.4.0.tgz", 129 | "integrity": "sha512-3KElyO3887HNxtxNF1ytGFrNmqD+hheqjwmT3sI09FaDCuaxGbOnsXAXH2eQ049XRXw9YQpHMgYws/aUNgXVyQ==" 130 | }, 131 | "@firebase/installations": { 132 | "version": "0.4.20", 133 | "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.4.20.tgz", 134 | "integrity": "sha512-ddUPZQKHWJzqg3g11hxHIPhp3oMEmsPo0GSxtp9Xd2VLRU64MFNAAt5PiBbq1K92ukZVoNh6Ki6J6+hJEQcGQw==", 135 | "requires": { 136 | "@firebase/component": "0.2.0", 137 | "@firebase/installations-types": "0.3.4", 138 | "@firebase/util": "0.3.4", 139 | "idb": "3.0.2", 140 | "tslib": "^1.11.1" 141 | } 142 | }, 143 | "@firebase/installations-types": { 144 | "version": "0.3.4", 145 | "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.3.4.tgz", 146 | "integrity": "sha512-RfePJFovmdIXb6rYwtngyxuEcWnOrzdZd9m7xAW0gRxDIjBT20n3BOhjpmgRWXo/DAxRmS7bRjWAyTHY9cqN7Q==" 147 | }, 148 | "@firebase/logger": { 149 | "version": "0.2.6", 150 | "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz", 151 | "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw==" 152 | }, 153 | "@firebase/messaging": { 154 | "version": "0.7.4", 155 | "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.7.4.tgz", 156 | "integrity": "sha512-xIZ1vHnOcHAaj+H/gS8tu3QolR9up+fyTfgVLEFbZRsbAiLuIvuaoJwTHLAUTs/nJF6GtEGscRD4Jx/aFmEJRw==", 157 | "requires": { 158 | "@firebase/component": "0.2.0", 159 | "@firebase/installations": "0.4.20", 160 | "@firebase/messaging-types": "0.5.0", 161 | "@firebase/util": "0.3.4", 162 | "idb": "3.0.2", 163 | "tslib": "^1.11.1" 164 | } 165 | }, 166 | "@firebase/messaging-types": { 167 | "version": "0.5.0", 168 | "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.5.0.tgz", 169 | "integrity": "sha512-QaaBswrU6umJYb/ZYvjR5JDSslCGOH6D9P136PhabFAHLTR4TWjsaACvbBXuvwrfCXu10DtcjMxqfhdNIB1Xfg==" 170 | }, 171 | "@firebase/performance": { 172 | "version": "0.4.6", 173 | "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.4.6.tgz", 174 | "integrity": "sha512-fy9YPYnvePFxme7euyi8B658gF8JUQhAB1qv6hVw+HqjVZQIANhwM9AUBi9+5jikD7gD1CnU7EjREvoy8MJiAw==", 175 | "requires": { 176 | "@firebase/component": "0.2.0", 177 | "@firebase/installations": "0.4.20", 178 | "@firebase/logger": "0.2.6", 179 | "@firebase/performance-types": "0.0.13", 180 | "@firebase/util": "0.3.4", 181 | "tslib": "^1.11.1" 182 | } 183 | }, 184 | "@firebase/performance-types": { 185 | "version": "0.0.13", 186 | "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.0.13.tgz", 187 | "integrity": "sha512-6fZfIGjQpwo9S5OzMpPyqgYAUZcFzZxHFqOyNtorDIgNXq33nlldTL/vtaUZA8iT9TT5cJlCrF/jthKU7X21EA==" 188 | }, 189 | "@firebase/polyfill": { 190 | "version": "0.3.36", 191 | "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.36.tgz", 192 | "integrity": "sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg==", 193 | "requires": { 194 | "core-js": "3.6.5", 195 | "promise-polyfill": "8.1.3", 196 | "whatwg-fetch": "2.0.4" 197 | } 198 | }, 199 | "@firebase/remote-config": { 200 | "version": "0.1.31", 201 | "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.1.31.tgz", 202 | "integrity": "sha512-QWjDsrLSqQnq/YTb3TSOJtIv7z2GXB7mTiwXE43NxYmsOX2z8KBlBBEHf9TcWk+90MKUTFfurDgYJN4rlIOxPg==", 203 | "requires": { 204 | "@firebase/component": "0.2.0", 205 | "@firebase/installations": "0.4.20", 206 | "@firebase/logger": "0.2.6", 207 | "@firebase/remote-config-types": "0.1.9", 208 | "@firebase/util": "0.3.4", 209 | "tslib": "^1.11.1" 210 | } 211 | }, 212 | "@firebase/remote-config-types": { 213 | "version": "0.1.9", 214 | "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.1.9.tgz", 215 | "integrity": "sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA==" 216 | }, 217 | "@firebase/storage": { 218 | "version": "0.4.3", 219 | "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.4.3.tgz", 220 | "integrity": "sha512-53IIt6Z3BltPBPmvxF/RGlvW8nBl4wI9GMR52CjRAIj438tPNxbpIvkLB956VVB9gfZhDcPIKxg7hNtC7hXrkA==", 221 | "requires": { 222 | "@firebase/component": "0.2.0", 223 | "@firebase/storage-types": "0.3.13", 224 | "@firebase/util": "0.3.4", 225 | "tslib": "^1.11.1" 226 | } 227 | }, 228 | "@firebase/storage-types": { 229 | "version": "0.3.13", 230 | "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.3.13.tgz", 231 | "integrity": "sha512-pL7b8d5kMNCCL0w9hF7pr16POyKkb3imOW7w0qYrhBnbyJTdVxMWZhb0HxCFyQWC0w3EiIFFmxoz8NTFZDEFog==" 232 | }, 233 | "@firebase/util": { 234 | "version": "0.3.4", 235 | "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.3.4.tgz", 236 | "integrity": "sha512-VwjJUE2Vgr2UMfH63ZtIX9Hd7x+6gayi6RUXaTqEYxSbf/JmehLmAEYSuxS/NckfzAXWeGnKclvnXVibDgpjQQ==", 237 | "requires": { 238 | "tslib": "^1.11.1" 239 | } 240 | }, 241 | "@firebase/webchannel-wrapper": { 242 | "version": "0.4.1", 243 | "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.4.1.tgz", 244 | "integrity": "sha512-0yPjzuzGMkW1GkrC8yWsiN7vt1OzkMIi9HgxRmKREZl2wnNPOKo/yScTjXf/O57HM8dltqxPF6jlNLFVtc2qdw==" 245 | }, 246 | "@grpc/grpc-js": { 247 | "version": "1.2.10", 248 | "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.2.10.tgz", 249 | "integrity": "sha512-wj6GkNiorWYaPiIZ767xImmw7avMMVUweTvPFg4mJWOxz2180DKwfuxhJJZ7rpc1+7D3mX/v8vJdxTuIo71Ieg==", 250 | "requires": { 251 | "@types/node": ">=12.12.47", 252 | "google-auth-library": "^6.1.1", 253 | "semver": "^6.2.0" 254 | } 255 | }, 256 | "@grpc/proto-loader": { 257 | "version": "0.5.6", 258 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.6.tgz", 259 | "integrity": "sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ==", 260 | "requires": { 261 | "lodash.camelcase": "^4.3.0", 262 | "protobufjs": "^6.8.6" 263 | } 264 | }, 265 | "@protobufjs/aspromise": { 266 | "version": "1.1.2", 267 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 268 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" 269 | }, 270 | "@protobufjs/base64": { 271 | "version": "1.1.2", 272 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 273 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 274 | }, 275 | "@protobufjs/codegen": { 276 | "version": "2.0.4", 277 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 278 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 279 | }, 280 | "@protobufjs/eventemitter": { 281 | "version": "1.1.0", 282 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 283 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" 284 | }, 285 | "@protobufjs/fetch": { 286 | "version": "1.1.0", 287 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 288 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", 289 | "requires": { 290 | "@protobufjs/aspromise": "^1.1.1", 291 | "@protobufjs/inquire": "^1.1.0" 292 | } 293 | }, 294 | "@protobufjs/float": { 295 | "version": "1.0.2", 296 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 297 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" 298 | }, 299 | "@protobufjs/inquire": { 300 | "version": "1.1.0", 301 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 302 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" 303 | }, 304 | "@protobufjs/path": { 305 | "version": "1.1.2", 306 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 307 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" 308 | }, 309 | "@protobufjs/pool": { 310 | "version": "1.1.0", 311 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 312 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" 313 | }, 314 | "@protobufjs/utf8": { 315 | "version": "1.1.0", 316 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 317 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" 318 | }, 319 | "@types/long": { 320 | "version": "4.0.1", 321 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", 322 | "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" 323 | }, 324 | "@types/node": { 325 | "version": "14.14.33", 326 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.33.tgz", 327 | "integrity": "sha512-oJqcTrgPUF29oUP8AsUqbXGJNuPutsetaa9kTQAQce5Lx5dTYWV02ScBiT/k1BX/Z7pKeqedmvp39Wu4zR7N7g==" 328 | }, 329 | "abort-controller": { 330 | "version": "3.0.0", 331 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 332 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 333 | "requires": { 334 | "event-target-shim": "^5.0.0" 335 | } 336 | }, 337 | "agent-base": { 338 | "version": "6.0.2", 339 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 340 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 341 | "requires": { 342 | "debug": "4" 343 | } 344 | }, 345 | "arrify": { 346 | "version": "2.0.1", 347 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", 348 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" 349 | }, 350 | "base64-js": { 351 | "version": "1.5.1", 352 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 353 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 354 | }, 355 | "bignumber.js": { 356 | "version": "9.0.1", 357 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", 358 | "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" 359 | }, 360 | "buffer-equal-constant-time": { 361 | "version": "1.0.1", 362 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 363 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 364 | }, 365 | "colorette": { 366 | "version": "1.2.2", 367 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", 368 | "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", 369 | "dev": true 370 | }, 371 | "core-js": { 372 | "version": "3.6.5", 373 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", 374 | "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" 375 | }, 376 | "debug": { 377 | "version": "4.3.1", 378 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 379 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 380 | "requires": { 381 | "ms": "2.1.2" 382 | } 383 | }, 384 | "dom-storage": { 385 | "version": "2.1.0", 386 | "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", 387 | "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" 388 | }, 389 | "ecdsa-sig-formatter": { 390 | "version": "1.0.11", 391 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 392 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 393 | "requires": { 394 | "safe-buffer": "^5.0.1" 395 | } 396 | }, 397 | "esbuild": { 398 | "version": "0.8.57", 399 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.8.57.tgz", 400 | "integrity": "sha512-j02SFrUwFTRUqiY0Kjplwjm1psuzO1d6AjaXKuOR9hrY0HuPsT6sV42B6myW34h1q4CRy+Y3g4RU/cGJeI/nNA==", 401 | "dev": true 402 | }, 403 | "event-target-shim": { 404 | "version": "5.0.1", 405 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 406 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 407 | }, 408 | "extend": { 409 | "version": "3.0.2", 410 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 411 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 412 | }, 413 | "fast-text-encoding": { 414 | "version": "1.0.3", 415 | "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", 416 | "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==" 417 | }, 418 | "faye-websocket": { 419 | "version": "0.11.3", 420 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", 421 | "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", 422 | "requires": { 423 | "websocket-driver": ">=0.5.1" 424 | } 425 | }, 426 | "firebase": { 427 | "version": "8.2.10", 428 | "resolved": "https://registry.npmjs.org/firebase/-/firebase-8.2.10.tgz", 429 | "integrity": "sha512-fGDrVWEDbFf4uaRhOMmbLf4CfW3D98GsMsbnvfd/5lPw5wTpUUcVjHyhXxcB+qfu66WeNW5kEKlEKLJmQXTkYw==", 430 | "requires": { 431 | "@firebase/analytics": "0.6.4", 432 | "@firebase/app": "0.6.15", 433 | "@firebase/app-types": "0.6.1", 434 | "@firebase/auth": "0.16.4", 435 | "@firebase/database": "0.9.4", 436 | "@firebase/firestore": "2.1.7", 437 | "@firebase/functions": "0.6.2", 438 | "@firebase/installations": "0.4.20", 439 | "@firebase/messaging": "0.7.4", 440 | "@firebase/performance": "0.4.6", 441 | "@firebase/polyfill": "0.3.36", 442 | "@firebase/remote-config": "0.1.31", 443 | "@firebase/storage": "0.4.3", 444 | "@firebase/util": "0.3.4" 445 | } 446 | }, 447 | "fsevents": { 448 | "version": "2.3.2", 449 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 450 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 451 | "dev": true, 452 | "optional": true 453 | }, 454 | "function-bind": { 455 | "version": "1.1.1", 456 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 457 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 458 | "dev": true 459 | }, 460 | "gaxios": { 461 | "version": "4.2.0", 462 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.2.0.tgz", 463 | "integrity": "sha512-Ms7fNifGv0XVU+6eIyL9LB7RVESeML9+cMvkwGS70xyD6w2Z80wl6RiqiJ9k1KFlJCUTQqFFc8tXmPQfSKUe8g==", 464 | "requires": { 465 | "abort-controller": "^3.0.0", 466 | "extend": "^3.0.2", 467 | "https-proxy-agent": "^5.0.0", 468 | "is-stream": "^2.0.0", 469 | "node-fetch": "^2.3.0" 470 | } 471 | }, 472 | "gcp-metadata": { 473 | "version": "4.2.1", 474 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", 475 | "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", 476 | "requires": { 477 | "gaxios": "^4.0.0", 478 | "json-bigint": "^1.0.0" 479 | } 480 | }, 481 | "google-auth-library": { 482 | "version": "6.1.6", 483 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz", 484 | "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==", 485 | "requires": { 486 | "arrify": "^2.0.0", 487 | "base64-js": "^1.3.0", 488 | "ecdsa-sig-formatter": "^1.0.11", 489 | "fast-text-encoding": "^1.0.0", 490 | "gaxios": "^4.0.0", 491 | "gcp-metadata": "^4.2.0", 492 | "gtoken": "^5.0.4", 493 | "jws": "^4.0.0", 494 | "lru-cache": "^6.0.0" 495 | } 496 | }, 497 | "google-p12-pem": { 498 | "version": "3.0.3", 499 | "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", 500 | "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", 501 | "requires": { 502 | "node-forge": "^0.10.0" 503 | } 504 | }, 505 | "gtoken": { 506 | "version": "5.2.1", 507 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", 508 | "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", 509 | "requires": { 510 | "gaxios": "^4.0.0", 511 | "google-p12-pem": "^3.0.3", 512 | "jws": "^4.0.0" 513 | } 514 | }, 515 | "has": { 516 | "version": "1.0.3", 517 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 518 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 519 | "dev": true, 520 | "requires": { 521 | "function-bind": "^1.1.1" 522 | } 523 | }, 524 | "http-parser-js": { 525 | "version": "0.5.3", 526 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", 527 | "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" 528 | }, 529 | "https-proxy-agent": { 530 | "version": "5.0.0", 531 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 532 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 533 | "requires": { 534 | "agent-base": "6", 535 | "debug": "4" 536 | } 537 | }, 538 | "idb": { 539 | "version": "3.0.2", 540 | "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", 541 | "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" 542 | }, 543 | "is-core-module": { 544 | "version": "2.2.0", 545 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", 546 | "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", 547 | "dev": true, 548 | "requires": { 549 | "has": "^1.0.3" 550 | } 551 | }, 552 | "is-stream": { 553 | "version": "2.0.0", 554 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 555 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" 556 | }, 557 | "json-bigint": { 558 | "version": "1.0.0", 559 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", 560 | "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", 561 | "requires": { 562 | "bignumber.js": "^9.0.0" 563 | } 564 | }, 565 | "jwa": { 566 | "version": "2.0.0", 567 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 568 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 569 | "requires": { 570 | "buffer-equal-constant-time": "1.0.1", 571 | "ecdsa-sig-formatter": "1.0.11", 572 | "safe-buffer": "^5.0.1" 573 | } 574 | }, 575 | "jws": { 576 | "version": "4.0.0", 577 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 578 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 579 | "requires": { 580 | "jwa": "^2.0.0", 581 | "safe-buffer": "^5.0.1" 582 | } 583 | }, 584 | "lodash.camelcase": { 585 | "version": "4.3.0", 586 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 587 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 588 | }, 589 | "long": { 590 | "version": "4.0.0", 591 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", 592 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" 593 | }, 594 | "lru-cache": { 595 | "version": "6.0.0", 596 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 597 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 598 | "requires": { 599 | "yallist": "^4.0.0" 600 | } 601 | }, 602 | "ms": { 603 | "version": "2.1.2", 604 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 605 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 606 | }, 607 | "nanoid": { 608 | "version": "3.1.20", 609 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", 610 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", 611 | "dev": true 612 | }, 613 | "node-fetch": { 614 | "version": "2.6.1", 615 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 616 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 617 | }, 618 | "node-forge": { 619 | "version": "0.10.0", 620 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", 621 | "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" 622 | }, 623 | "path-parse": { 624 | "version": "1.0.6", 625 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 626 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 627 | "dev": true 628 | }, 629 | "postcss": { 630 | "version": "8.2.8", 631 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.8.tgz", 632 | "integrity": "sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw==", 633 | "dev": true, 634 | "requires": { 635 | "colorette": "^1.2.2", 636 | "nanoid": "^3.1.20", 637 | "source-map": "^0.6.1" 638 | } 639 | }, 640 | "promise-polyfill": { 641 | "version": "8.1.3", 642 | "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz", 643 | "integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g==" 644 | }, 645 | "protobufjs": { 646 | "version": "6.10.2", 647 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", 648 | "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", 649 | "requires": { 650 | "@protobufjs/aspromise": "^1.1.2", 651 | "@protobufjs/base64": "^1.1.2", 652 | "@protobufjs/codegen": "^2.0.4", 653 | "@protobufjs/eventemitter": "^1.1.0", 654 | "@protobufjs/fetch": "^1.1.0", 655 | "@protobufjs/float": "^1.0.2", 656 | "@protobufjs/inquire": "^1.1.0", 657 | "@protobufjs/path": "^1.1.2", 658 | "@protobufjs/pool": "^1.1.0", 659 | "@protobufjs/utf8": "^1.1.0", 660 | "@types/long": "^4.0.1", 661 | "@types/node": "^13.7.0", 662 | "long": "^4.0.0" 663 | }, 664 | "dependencies": { 665 | "@types/node": { 666 | "version": "13.13.46", 667 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.46.tgz", 668 | "integrity": "sha512-dqpbzK/KDsOlEt+oyB3rv+u1IxlLFziZu/Z0adfRKoelkr+sTd6QcgiQC+HWq/vkYkHwG5ot2LxgV05aAjnhcg==" 669 | } 670 | } 671 | }, 672 | "resolve": { 673 | "version": "1.20.0", 674 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 675 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 676 | "dev": true, 677 | "requires": { 678 | "is-core-module": "^2.2.0", 679 | "path-parse": "^1.0.6" 680 | } 681 | }, 682 | "rollup": { 683 | "version": "2.41.0", 684 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.0.tgz", 685 | "integrity": "sha512-Gk76XHTggulWPH95q8V62bw6uqDH6UGvbD6LOa3QUyhuMF3eOuaeDHR7SLm1T9faitkpNrqzUAVYx47klcMnlA==", 686 | "dev": true, 687 | "requires": { 688 | "fsevents": "~2.3.1" 689 | } 690 | }, 691 | "safe-buffer": { 692 | "version": "5.2.1", 693 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 694 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 695 | }, 696 | "semver": { 697 | "version": "6.3.0", 698 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 699 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 700 | }, 701 | "source-map": { 702 | "version": "0.6.1", 703 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 704 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 705 | "dev": true 706 | }, 707 | "tslib": { 708 | "version": "1.14.1", 709 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 710 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 711 | }, 712 | "vite": { 713 | "version": "2.0.5", 714 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.0.5.tgz", 715 | "integrity": "sha512-QTgEDbq1WsTtr6j+++ewjhBFEk6c8v0xz4fb/OWJQKNYU8ZZtphOshwOqAlnarSstPBtWCBR0tsugXx6ajfoUg==", 716 | "dev": true, 717 | "requires": { 718 | "esbuild": "^0.8.52", 719 | "fsevents": "~2.3.1", 720 | "postcss": "^8.2.1", 721 | "resolve": "^1.19.0", 722 | "rollup": "^2.38.5" 723 | } 724 | }, 725 | "websocket-driver": { 726 | "version": "0.7.4", 727 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", 728 | "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", 729 | "requires": { 730 | "http-parser-js": ">=0.5.1", 731 | "safe-buffer": ">=5.1.0", 732 | "websocket-extensions": ">=0.1.1" 733 | } 734 | }, 735 | "websocket-extensions": { 736 | "version": "0.1.4", 737 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", 738 | "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" 739 | }, 740 | "whatwg-fetch": { 741 | "version": "2.0.4", 742 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", 743 | "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" 744 | }, 745 | "xmlhttprequest": { 746 | "version": "1.8.0", 747 | "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", 748 | "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" 749 | }, 750 | "yallist": { 751 | "version": "4.0.0", 752 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 753 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 754 | } 755 | } 756 | } 757 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtc-demo", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^2.0.5" 11 | }, 12 | "dependencies": { 13 | "firebase": "^8.2.10" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Syne+Mono&display=swap'); 2 | 3 | body { 4 | font-family: 'Syne Mono', monospace; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | text-align: center; 8 | color: #2c3e50; 9 | margin: 80px 10px; 10 | } 11 | 12 | video { 13 | width: 40vw; 14 | height: 30vw; 15 | margin: 2rem; 16 | background: #2c3e50; 17 | } 18 | 19 | .videos { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } --------------------------------------------------------------------------------