refactor: use secure-file-transfer@0.0.1

This commit is contained in:
Jeremy Kahn 2023-02-19 12:33:45 -06:00
parent 67ee15c40e
commit ebb8efb3f6
5 changed files with 81 additions and 554 deletions

176
package-lock.json generated
View File

@ -36,19 +36,17 @@
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-syntax-highlighter": "^15.5.0",
"readable-stream-node-to-web": "^1.0.1",
"readable-web-to-node-stream": "^3.0.2",
"remark-gfm": "^3.0.1",
"sass": "^1.54.3",
"secure-file-transfer": "^0.0.1",
"streamsaver": "^2.0.6",
"trystero": "github:jeremyckahn/trystero#bugfix/29__clean-up-peers",
"typeface-public-sans": "^1.1.13",
"typeface-roboto": "^1.1.13",
"typescript": "^4.7.4",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4",
"webtorrent": "^1.9.7",
"wormhole-crypto": "^0.3.1"
"web-vitals": "^2.1.4"
},
"devDependencies": {
"@types/react-syntax-highlighter": "^15.5.5",
@ -60,24 +58,19 @@
"autoprefixer": "^10.4.8",
"bittorrent-tracker": "^9.19.0",
"cross-env": "^7.0.3",
"crypto": "npm:crypto-browserify@^3.12.0",
"eslint": "^8.21.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"http": "npm:http-browserify@^1.7.0",
"https": "npm:https-browserify@^1.0.0",
"husky": "^8.0.1",
"mprocs": "^0.6.4",
"path": "npm:path-browserify@^1.0.1",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
"process": "^0.11.10",
"serve": "^14.1.2",
"stream": "npm:stream-browserify@^3.0.0",
"tailwindcss": "^3.1.8",
"url": "^0.11.0",
"util": "^0.12.5"
@ -5320,7 +5313,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/bittorrent-protocol/-/bittorrent-protocol-3.1.2.tgz",
"integrity": "sha512-7k9nivNeG7Sc8wVuBs+XjBp2u7pH8tqW3BB93/SAg3xht/cZEK+Rqkj79xSyJqyj86eA0F6n85EKkkyGki8afg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
@ -5642,7 +5634,6 @@
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@types/magnet-uri/-/magnet-uri-5.1.3.tgz",
"integrity": "sha512-FvJN1yYdLhvU6zWJ2YnWQ2GnpFLsA8bt+85WY0tLh6ehzGNrvBorjlcc53/zY43r/IKn+ctFs1nt7andwGnQCQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
@ -5689,7 +5680,6 @@
"version": "5.8.4",
"resolved": "https://registry.npmjs.org/@types/parse-torrent/-/parse-torrent-5.8.4.tgz",
"integrity": "sha512-FdKs5yN5iYO5Cu9gVz1Zl30CbZe6HTsqloWmCf+LfbImgSzlsUkov2+npQWCQSQ3zi/a2G5C824K0UpZ2sRufA==",
"dev": true,
"dependencies": {
"@types/magnet-uri": "*",
"@types/node": "*",
@ -5700,7 +5690,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/parse-torrent-file/-/parse-torrent-file-4.0.3.tgz",
"integrity": "sha512-dFkPnJPKiFWiGX+HXmyTVt2js3k0d9dThmUxX8nfGC22hbyZ5BTmetsEl45sQhHLcFo43njVrIKMXM3F1ahXRw==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
@ -5812,7 +5801,6 @@
"version": "9.11.5",
"resolved": "https://registry.npmjs.org/@types/simple-peer/-/simple-peer-9.11.5.tgz",
"integrity": "sha512-haXgWcAa3Y3Sn+T8lzkE4ErQUpYzhW6Cz2lh00RhQTyWt+xZ3s87wJPztUxlqSdFRqGhe2MQIBd0XsyHP3No4w==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
@ -5833,8 +5821,7 @@
"node_modules/@types/streamsaver": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/streamsaver/-/streamsaver-2.0.1.tgz",
"integrity": "sha512-I49NtT8w6syBI3Zg3ixCyygTHoTVMY0z2TMRcTgccdIsVd2MwlKk7ITLHLsJtgchUHcOd7QEARG9h0ifcA6l2Q==",
"dev": true
"integrity": "sha512-I49NtT8w6syBI3Zg3ixCyygTHoTVMY0z2TMRcTgccdIsVd2MwlKk7ITLHLsJtgchUHcOd7QEARG9h0ifcA6l2Q=="
},
"node_modules/@types/testing-library__jest-dom": {
"version": "5.14.5",
@ -5864,7 +5851,6 @@
"version": "0.109.3",
"resolved": "https://registry.npmjs.org/@types/webtorrent/-/webtorrent-0.109.3.tgz",
"integrity": "sha512-EJLsxMEcEjPXHcBqL6TRAbUwIpxAul5ULrXHJ0zwig7Oe70FS6dAzCWLq4MBafX3QrQG1DzGAS0fS8iJEOjD0g==",
"dev": true,
"dependencies": {
"@types/bittorrent-protocol": "*",
"@types/node": "*",
@ -7470,8 +7456,7 @@
"node_modules/Base64": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
"integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw==",
"dev": true
"integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
@ -8063,7 +8048,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"dependencies": {
"buffer-xor": "^1.0.3",
"cipher-base": "^1.0.0",
@ -8077,7 +8061,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
"integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
"dev": true,
"dependencies": {
"browserify-aes": "^1.0.4",
"browserify-des": "^1.0.0",
@ -8088,7 +8071,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
"integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
"dev": true,
"dependencies": {
"cipher-base": "^1.0.1",
"des.js": "^1.0.0",
@ -8105,7 +8087,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
"integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
"dev": true,
"dependencies": {
"bn.js": "^5.0.0",
"randombytes": "^2.0.1"
@ -8114,14 +8095,12 @@
"node_modules/browserify-rsa/node_modules/bn.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"dev": true
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
},
"node_modules/browserify-sign": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
"integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
"dev": true,
"dependencies": {
"bn.js": "^5.1.1",
"browserify-rsa": "^4.0.1",
@ -8137,14 +8116,12 @@
"node_modules/browserify-sign/node_modules/bn.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"dev": true
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
},
"node_modules/browserify-sign/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@ -8253,8 +8230,7 @@
"node_modules/buffer-xor": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
"dev": true
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
},
"node_modules/bufferutil": {
"version": "4.0.6",
@ -8691,7 +8667,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
"dev": true,
"dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -9129,7 +9104,6 @@
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
"integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
"dev": true,
"dependencies": {
"bn.js": "^4.1.0",
"elliptic": "^6.5.3"
@ -9139,7 +9113,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"dependencies": {
"cipher-base": "^1.0.1",
"inherits": "^2.0.1",
@ -9152,7 +9125,6 @@
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"dependencies": {
"cipher-base": "^1.0.3",
"create-hash": "^1.1.0",
@ -9198,7 +9170,6 @@
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
"integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
"dev": true,
"dependencies": {
"browserify-cipher": "^1.0.0",
"browserify-sign": "^4.0.0",
@ -9899,7 +9870,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0"
@ -9957,9 +9927,9 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/detectincognitojs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/detectincognitojs/-/detectincognitojs-1.1.2.tgz",
"integrity": "sha512-7b+0/zJ76ZTeEtNl9MCPEKgFauEw/LerdlwnEnnrgYCFhp5FL+RJGD13EF/R9FYqrVxL7H5fgCyBdzJ6BtkPhg=="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/detectincognitojs/-/detectincognitojs-1.3.0.tgz",
"integrity": "sha512-/uAMG+6Ipa8izluYQ45HqARjeYgtJLLBvpAiX1Zd3zWJ1MhEne6FDSjkkLaC3pdNeHfT3qhpvFRqB6dwLbdnng=="
},
"node_modules/detective": {
"version": "5.2.1",
@ -10002,7 +9972,6 @@
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"dependencies": {
"bn.js": "^4.1.0",
"miller-rabin": "^4.0.0",
@ -11481,7 +11450,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
"dev": true,
"dependencies": {
"md5.js": "^1.3.4",
"safe-buffer": "^5.1.1"
@ -12638,7 +12606,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "^3.6.0",
@ -12652,7 +12619,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@ -12925,7 +12891,6 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
"integrity": "sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==",
"dev": true,
"dependencies": {
"Base64": "~0.2.0",
"inherits": "~2.0.1"
@ -13023,8 +12988,7 @@
"name": "https-browserify",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
"dev": true
"integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
@ -17858,7 +17822,6 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
"dev": true,
"dependencies": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1",
@ -18749,7 +18712,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
"integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
"dev": true,
"dependencies": {
"bn.js": "^4.0.0",
"brorand": "^1.0.1"
@ -19775,7 +19737,6 @@
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
"integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
"dev": true,
"dependencies": {
"asn1.js": "^5.2.0",
"browserify-aes": "^1.0.0",
@ -19858,8 +19819,7 @@
"name": "path-browserify",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
"dev": true
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
},
"node_modules/path-exists": {
"version": "4.0.0",
@ -19913,7 +19873,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
"dev": true,
"dependencies": {
"create-hash": "^1.1.2",
"create-hmac": "^1.1.4",
@ -21760,7 +21719,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
"integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
"dev": true,
"dependencies": {
"bn.js": "^4.1.0",
"browserify-rsa": "^4.0.0",
@ -21920,7 +21878,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
"integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
"dev": true,
"dependencies": {
"randombytes": "^2.0.5",
"safe-buffer": "^5.1.0"
@ -22933,7 +22890,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
"dev": true,
"dependencies": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1"
@ -23217,6 +23173,29 @@
"node": ">=10.0.0"
}
},
"node_modules/secure-file-transfer": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/secure-file-transfer/-/secure-file-transfer-0.0.1.tgz",
"integrity": "sha512-biTN2faslbvKUMA+vax9z1a/8suJpga1fgXHSTrWaKTKnH/sbFRH+eT3LB1LFywZVl6tUMHM+IacauqTrdnQcg==",
"dependencies": {
"@types/streamsaver": "^2.0.1",
"@types/webtorrent": "^0.109.3",
"crypto": "npm:crypto-browserify@^3.12.0",
"detectincognitojs": "^1.3.0",
"http": "npm:http-browserify@^1.7.0",
"https": "npm:https-browserify@^1.0.0",
"idb-chunk-store": "^1.0.1",
"path": "npm:path-browserify@^1.0.1",
"readable-stream-node-to-web": "^1.0.1",
"stream": "npm:stream-browserify@^3.0.0",
"streamsaver": "^2.0.6",
"webtorrent": "^1.9.7",
"wormhole-crypto": "^0.3.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -23532,7 +23511,6 @@
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -24033,7 +24011,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
"integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
"dev": true,
"dependencies": {
"inherits": "~2.0.4",
"readable-stream": "^3.5.0"
@ -30669,7 +30646,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/bittorrent-protocol/-/bittorrent-protocol-3.1.2.tgz",
"integrity": "sha512-7k9nivNeG7Sc8wVuBs+XjBp2u7pH8tqW3BB93/SAg3xht/cZEK+Rqkj79xSyJqyj86eA0F6n85EKkkyGki8afg==",
"dev": true,
"requires": {
"@types/node": "*"
}
@ -30953,7 +30929,6 @@
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@types/magnet-uri/-/magnet-uri-5.1.3.tgz",
"integrity": "sha512-FvJN1yYdLhvU6zWJ2YnWQ2GnpFLsA8bt+85WY0tLh6ehzGNrvBorjlcc53/zY43r/IKn+ctFs1nt7andwGnQCQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
@ -31000,7 +30975,6 @@
"version": "5.8.4",
"resolved": "https://registry.npmjs.org/@types/parse-torrent/-/parse-torrent-5.8.4.tgz",
"integrity": "sha512-FdKs5yN5iYO5Cu9gVz1Zl30CbZe6HTsqloWmCf+LfbImgSzlsUkov2+npQWCQSQ3zi/a2G5C824K0UpZ2sRufA==",
"dev": true,
"requires": {
"@types/magnet-uri": "*",
"@types/node": "*",
@ -31011,7 +30985,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/parse-torrent-file/-/parse-torrent-file-4.0.3.tgz",
"integrity": "sha512-dFkPnJPKiFWiGX+HXmyTVt2js3k0d9dThmUxX8nfGC22hbyZ5BTmetsEl45sQhHLcFo43njVrIKMXM3F1ahXRw==",
"dev": true,
"requires": {
"@types/node": "*"
}
@ -31123,7 +31096,6 @@
"version": "9.11.5",
"resolved": "https://registry.npmjs.org/@types/simple-peer/-/simple-peer-9.11.5.tgz",
"integrity": "sha512-haXgWcAa3Y3Sn+T8lzkE4ErQUpYzhW6Cz2lh00RhQTyWt+xZ3s87wJPztUxlqSdFRqGhe2MQIBd0XsyHP3No4w==",
"dev": true,
"requires": {
"@types/node": "*"
}
@ -31144,8 +31116,7 @@
"@types/streamsaver": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/streamsaver/-/streamsaver-2.0.1.tgz",
"integrity": "sha512-I49NtT8w6syBI3Zg3ixCyygTHoTVMY0z2TMRcTgccdIsVd2MwlKk7ITLHLsJtgchUHcOd7QEARG9h0ifcA6l2Q==",
"dev": true
"integrity": "sha512-I49NtT8w6syBI3Zg3ixCyygTHoTVMY0z2TMRcTgccdIsVd2MwlKk7ITLHLsJtgchUHcOd7QEARG9h0ifcA6l2Q=="
},
"@types/testing-library__jest-dom": {
"version": "5.14.5",
@ -31175,7 +31146,6 @@
"version": "0.109.3",
"resolved": "https://registry.npmjs.org/@types/webtorrent/-/webtorrent-0.109.3.tgz",
"integrity": "sha512-EJLsxMEcEjPXHcBqL6TRAbUwIpxAul5ULrXHJ0zwig7Oe70FS6dAzCWLq4MBafX3QrQG1DzGAS0fS8iJEOjD0g==",
"dev": true,
"requires": {
"@types/bittorrent-protocol": "*",
"@types/node": "*",
@ -32311,8 +32281,7 @@
"Base64": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
"integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw==",
"dev": true
"integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw=="
},
"base64-js": {
"version": "1.5.1",
@ -32713,7 +32682,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
"buffer-xor": "^1.0.3",
"cipher-base": "^1.0.0",
@ -32727,7 +32695,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
"integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
"dev": true,
"requires": {
"browserify-aes": "^1.0.4",
"browserify-des": "^1.0.0",
@ -32738,7 +32705,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
"integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
"dev": true,
"requires": {
"cipher-base": "^1.0.1",
"des.js": "^1.0.0",
@ -32755,7 +32721,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
"integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
"dev": true,
"requires": {
"bn.js": "^5.0.0",
"randombytes": "^2.0.1"
@ -32764,8 +32729,7 @@
"bn.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"dev": true
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
}
}
},
@ -32773,7 +32737,6 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
"integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
"dev": true,
"requires": {
"bn.js": "^5.1.1",
"browserify-rsa": "^4.0.1",
@ -32789,14 +32752,12 @@
"bn.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"dev": true
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}
}
},
@ -32860,8 +32821,7 @@
"buffer-xor": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
"dev": true
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="
},
"bufferutil": {
"version": "4.0.6",
@ -33133,7 +33093,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
"dev": true,
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -33460,7 +33419,6 @@
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
"integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
"dev": true,
"requires": {
"bn.js": "^4.1.0",
"elliptic": "^6.5.3"
@ -33470,7 +33428,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
"cipher-base": "^1.0.1",
"inherits": "^2.0.1",
@ -33483,7 +33440,6 @@
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
"cipher-base": "^1.0.3",
"create-hash": "^1.1.0",
@ -33516,7 +33472,6 @@
"version": "npm:crypto-browserify@3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
"integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
"dev": true,
"requires": {
"browserify-cipher": "^1.0.0",
"browserify-sign": "^4.0.0",
@ -34016,7 +33971,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
"dev": true,
"requires": {
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0"
@ -34062,9 +34016,9 @@
}
},
"detectincognitojs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/detectincognitojs/-/detectincognitojs-1.1.2.tgz",
"integrity": "sha512-7b+0/zJ76ZTeEtNl9MCPEKgFauEw/LerdlwnEnnrgYCFhp5FL+RJGD13EF/R9FYqrVxL7H5fgCyBdzJ6BtkPhg=="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/detectincognitojs/-/detectincognitojs-1.3.0.tgz",
"integrity": "sha512-/uAMG+6Ipa8izluYQ45HqARjeYgtJLLBvpAiX1Zd3zWJ1MhEne6FDSjkkLaC3pdNeHfT3qhpvFRqB6dwLbdnng=="
},
"detective": {
"version": "5.2.1",
@ -34095,7 +34049,6 @@
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"requires": {
"bn.js": "^4.1.0",
"miller-rabin": "^4.0.0",
@ -35186,7 +35139,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
"dev": true,
"requires": {
"md5.js": "^1.3.4",
"safe-buffer": "^5.1.1"
@ -36052,7 +36004,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
"dev": true,
"requires": {
"inherits": "^2.0.4",
"readable-stream": "^3.6.0",
@ -36062,8 +36013,7 @@
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}
}
},
@ -36270,7 +36220,6 @@
"version": "npm:http-browserify@1.7.0",
"resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
"integrity": "sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==",
"dev": true,
"requires": {
"Base64": "~0.2.0",
"inherits": "~2.0.1"
@ -36343,8 +36292,7 @@
"https": {
"version": "npm:https-browserify@1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
"dev": true
"integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="
},
"https-proxy-agent": {
"version": "5.0.1",
@ -40115,7 +40063,6 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
"dev": true,
"requires": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1",
@ -40679,7 +40626,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
"integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
"dev": true,
"requires": {
"bn.js": "^4.0.0",
"brorand": "^1.0.1"
@ -41425,7 +41371,6 @@
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
"integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
"dev": true,
"requires": {
"asn1.js": "^5.2.0",
"browserify-aes": "^1.0.0",
@ -41492,8 +41437,7 @@
"path": {
"version": "npm:path-browserify@1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
"dev": true
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
},
"path-exists": {
"version": "4.0.0",
@ -41535,7 +41479,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
"integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
"dev": true,
"requires": {
"create-hash": "^1.1.2",
"create-hmac": "^1.1.4",
@ -42699,7 +42642,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
"integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
"dev": true,
"requires": {
"bn.js": "^4.1.0",
"browserify-rsa": "^4.0.0",
@ -42819,7 +42761,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
"integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
"dev": true,
"requires": {
"randombytes": "^2.0.5",
"safe-buffer": "^5.1.0"
@ -43580,7 +43521,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
"dev": true,
"requires": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1"
@ -43755,6 +43695,26 @@
"node-gyp-build": "^4.2.0"
}
},
"secure-file-transfer": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/secure-file-transfer/-/secure-file-transfer-0.0.1.tgz",
"integrity": "sha512-biTN2faslbvKUMA+vax9z1a/8suJpga1fgXHSTrWaKTKnH/sbFRH+eT3LB1LFywZVl6tUMHM+IacauqTrdnQcg==",
"requires": {
"@types/streamsaver": "^2.0.1",
"@types/webtorrent": "^0.109.3",
"crypto": "npm:crypto-browserify@^3.12.0",
"detectincognitojs": "^1.3.0",
"http": "npm:http-browserify@^1.7.0",
"https": "npm:https-browserify@^1.0.0",
"idb-chunk-store": "^1.0.1",
"path": "npm:path-browserify@^1.0.1",
"readable-stream-node-to-web": "^1.0.1",
"stream": "npm:stream-browserify@^3.0.0",
"streamsaver": "^2.0.6",
"webtorrent": "^1.9.7",
"wormhole-crypto": "^0.3.1"
}
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -44022,7 +43982,6 @@
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -44387,7 +44346,6 @@
"version": "npm:stream-browserify@3.0.0",
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
"integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
"dev": true,
"requires": {
"inherits": "~2.0.4",
"readable-stream": "^3.5.0"

View File

@ -32,19 +32,17 @@
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-syntax-highlighter": "^15.5.0",
"readable-stream-node-to-web": "^1.0.1",
"readable-web-to-node-stream": "^3.0.2",
"remark-gfm": "^3.0.1",
"sass": "^1.54.3",
"secure-file-transfer": "^0.0.1",
"streamsaver": "^2.0.6",
"trystero": "github:jeremyckahn/trystero#bugfix/29__clean-up-peers",
"typeface-public-sans": "^1.1.13",
"typeface-roboto": "^1.1.13",
"typescript": "^4.7.4",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4",
"webtorrent": "^1.9.7",
"wormhole-crypto": "^0.3.1"
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
@ -89,24 +87,19 @@
"autoprefixer": "^10.4.8",
"bittorrent-tracker": "^9.19.0",
"cross-env": "^7.0.3",
"crypto": "npm:crypto-browserify@^3.12.0",
"eslint": "^8.21.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"http": "npm:http-browserify@^1.7.0",
"https": "npm:https-browserify@^1.0.0",
"husky": "^8.0.1",
"mprocs": "^0.6.4",
"path": "npm:path-browserify@^1.0.1",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
"process": "^0.11.10",
"serve": "^14.1.2",
"stream": "npm:stream-browserify@^3.0.0",
"tailwindcss": "^3.1.8",
"url": "^0.11.0",
"util": "^0.12.5"

215
src/react-app-env.d.ts vendored
View File

@ -1,216 +1 @@
/// <reference types="react-scripts" />
declare module 'wormhole-crypto' {
/**
* The encrypted byte range that is needed to decrypt the client's specified range.
*/
export type ByteRange = {
offset: number
length: number
}
/**
* The metadata buffer to encrypt.
*/
export type Meta = Uint8Array
/**
* A WHATWG readable stream used as a data source for the plaintext stream.
*/
export type EncryptedStream = ReadableStream
/**
* @param streams An array of `ReadableStream` objects, one for each of the requested ranges.
* @returns Contains the plaintext data for the client's desired byte range.
*/
export type DecryptFn = (streams: ReadableStream[]) => ReadableStream
export type DecryptedStreamRange = {
ranges: ByteRange[]
decrypt: DecryptFn
}
export class Keychain {
/**
* Create a new keychain object. The keychain can be used to create encryption streams, decryption streams, and to encrypt or decrypt a "metadata" buffer.
*
* @param key The main key. This should be 16 bytes in length. If a string is given, then it should be a base64-encoded string. If the argument is null, then a key will be automatically generated.
* @param salt The salt. This should be 16 bytes in length. If a string is given, then it should be a base64-encoded string. If this argument is null, then a salt will be automatically generated.
*/
constructor(
key: Uint8Array | string | null = null,
salt: Uint8Array | string | null = null
)
/**
* The main key.
*/
key: Uint8Array
/**
* The main key as a base64-encoded string.
*/
keyB64: string
/**
* The salt.
*
* Implementation note: The salt is used to derive the (internal) metadata key and authentication token.
*/
salt: Uint8Array
/**
* The salt as a base64-encoded string.
*/
saltB64: string
/**
* Returns a `Promise` which resolves to the authentication token. By default, the authentication token is automatically derived from the main key using HKDF SHA-256.
*
* In Wormhole, the authentication token is used to communicate with the server and prove that the client has permission to fetch data for a room. Without a valid authentication token, the server will not return the encrypted room metadata or allow downloading the encrypted file data.
*
* Since the authentication token is derived from the main key, the client presents it to the Wormhole server as a "reader token" to prove that it is in possession of the main key without revealing the main key to the server.
*
* For destructive operations, like modifying the room, the client instead presents a "writer token", which is not derived from the main key but is provided by the server to the room creator who overrides the keychain authentication token by calling `keychain.setAuthToken(authToken)` with the "writer token".
*/
authToken(): Promise<Uint8Array>
/**
* Returns a `Promise` that resolves to the authentication token as a base64-encoded string.
*/
authTokenB64(): Promise<string>
/**
* Returns a `Promise` that resolves to the HTTP header value to be provided to the Wormhole server. It contains the authentication token.
*/
authHeader(): Promise<string>
/**
* Update the keychain authentication token to `authToken`.
*
* @param authToken The authentication token. This should be 16 bytes in length. If a `string` is given, then it should be a base64-encoded string. If this argument is `null`, then an authentication token will be automatically generated.
*/
setAuthToken(authToken: Uint8Array | string | null = null): void
/**
* @param stream A WHATWG readable stream used as a data source for the encrypted stream.
*
* @returns A `Promise` that resolves to a `ReadableStream` encryption stream that consumes the data in `stream` and returns an encrypted version. Data is encrypted with [Encrypted Content-Encoding for HTTP (RFC 8188)](https://tools.ietf.org/html/rfc8188).
*/
encryptStream(stream: ReadableStream): Promise<EncryptedStream>
/**
* Returns a `Promise` that resolves to a `ReadableStream` decryption stream that consumes the data in `encryptedStream` and returns a plaintext version.
*
* @param encryptedStream A WHATWG readable stream that was returned from encryptStream.
* @returns A `Promise` that resolves to a `ReadableStream` decryption stream that
consumes the data in `encryptedStream` and returns a plaintext version.
*/
decryptStream(encryptedStream: EncryptedStream): Promise<ReadableStream>
/**
* Returns a `Promise` that resolves to a object containing `ranges`, which is an array of objects containing `offset` and `length` integers specifying the encrypted byte ranges that are needed to decrypt the client's specified range, and a `decrypt` function.
*
* Once the client has gathered a stream for each byte range in `ranges`, the client should call `decrypt(streams)`, where `streams` is an array of `ReadableStream` objects, one for each of the requested ranges. `decrypt` will then return a `ReadableStream` containing the plaintext data for the client's desired byte range.
*/
decryptStreamRange(
offset: number,
length: number,
totalEncryptedLength: number
): Promise<DecryptedStreamRange>
/**
* Implementation note: The metadata key is automatically derived from the main key using HKDF SHA-256. The value is not user-controlled.
*
* Implementation note: The initialization vector (IV) is automatically generated and included in the encrypted output. No need to generate it or to manage it separately from the encrypted output.
*
* @returns A `Promise` that resolves to an encrypted version of `meta`. The metadata is encrypted with AES-GCM.
*/
encryptMeta(meta: Meta): Promise<Uint8Array>
/**
* @param encryptedMeta The encrypted metadata buffer to decrypt.
* @returns A `Promise` that resolves to a decrypted version of `encryptedMeta`.
*/
decryptMeta(encryptedMeta: Uint8Array): Promise<Uint8Array>
}
/**
* Given an encrypted size, return the corresponding plaintext size.
*/
export function plaintextSize(encryptedSize: number): number
/**
* Given a plaintext size, return the corresponding encrypted size.
*/
export function encryptedSize(plaintextSize: number): number
}
declare module 'abstract-chunk-store' {
type GetCallback = (err: Error | null, buffer: Buffer) => void
export interface ChunkStore {
/**
* Create a new chunk store. Chunks must have a length of `chunkLength`.
*/
constructor(chunkLength: number)
/**
* Add a new chunk to the storage. `index` should be an integer.
*/
put(
index: number,
chunkBuffer: Buffer,
cb: (err: Error | null) => void
): void
/**
* Retrieve a chunk stored. `index` should be an integer.
*/
get(index: number, cb: GetCallback): void
get(
index: number,
options: { offset?: number; length?: number },
cb: GetCallback
): void
/**
* Close the underlying resource, e.g. if the store is backed by a file, this would close the file descriptor.
*/
close(cb: (err: Error | null) => void)
/**
* Destroy the file data, e.g. if the store is backed by a file, this would delete the file from the filesystem.
*/
destroy(cb: (err: Error | null) => void)
/**
* Expose the chunk length from the constructor so that code that receives a chunk store can know what size of chunks to write.
*/
chunkLength: number
}
}
declare module 'idb-chunk-store' {
import { TorrentOptions } from 'webtorrent'
import { ChunkStore } from 'abstract-chunk-store'
export default function idbChunkStore(
chunkLength: number,
opts?: Partial<{
/**
* A name to separate the contents of different stores
*/
name: string
}> &
TorrentOptions.store
): ChunkStore
}
declare module 'readable-stream-node-to-web' {
export const WEBSTREAM_SUPPORT: boolean
export default function nodeToWeb(
readableStream: NodeJS.ReadableStream
): ReadableStream
}

View File

@ -1,219 +1,12 @@
import WebTorrent, { Torrent, TorrentFile } from 'webtorrent'
import streamSaver from 'streamsaver'
import { Keychain, plaintextSize, encryptedSize } from 'wormhole-crypto'
import idbChunkStore from 'idb-chunk-store'
import { detectIncognito } from 'detectincognitojs'
import nodeToWebStream from 'readable-stream-node-to-web'
import { FileTransfer, setStreamSaverMitm } from 'secure-file-transfer'
import { trackerUrls } from 'config/trackerUrls'
import { streamSaverUrl } from 'config/streamSaverUrl'
streamSaver.mitm = streamSaverUrl
setStreamSaverMitm(streamSaverUrl)
const getKeychain = (password: string) => {
const encoder = new TextEncoder()
const keyLength = 16
const padding = new Array(keyLength).join('0')
const key = password.concat(padding).slice(0, keyLength)
const salt = window.location.origin.concat(padding).slice(0, keyLength)
const keychain = new Keychain(encoder.encode(key), encoder.encode(salt))
return keychain
}
interface DownloadOpts {
doSave?: boolean
onProgress?: (progress: number) => void
}
export class FileTransfer {
private webTorrentClient = new WebTorrent()
private torrents: Record<Torrent['magnetURI'], Torrent> = {}
private async saveTorrentFiles(torrent: Torrent, password: string) {
for (const file of torrent.files) {
try {
const readStream = await this.getDecryptedFileReadStream(file, password)
const writeStream = streamSaver.createWriteStream(file.name, {
size: plaintextSize(file.length),
})
await readStream.pipeTo(writeStream)
} catch (e) {
console.error(e)
throw new Error('Download aborted')
}
}
}
constructor() {
window.addEventListener('beforeunload', this.handleBeforePageUnload)
}
async getDecryptedFileReadStream(file: TorrentFile, password: string) {
const keychain = getKeychain(password)
const decryptedStream: ReadableStream = await keychain.decryptStream(
nodeToWebStream(file.createReadStream())
)
return decryptedStream
}
async download(
magnetURI: string,
password: string,
{ onProgress, doSave }: DownloadOpts = {}
) {
let torrent = this.torrents[magnetURI]
if (!torrent) {
const { isPrivate } = await detectIncognito()
torrent = await new Promise<Torrent>(res => {
this.webTorrentClient.add(
magnetURI,
{
announce: trackerUrls,
// If the user is using their browser's private mode, IndexedDB
// will be unavailable and idbChunkStore will break all transfers.
// In that case, fall back to the default in-memory data store.
store: isPrivate ? undefined : idbChunkStore,
destroyStoreOnDestroy: true,
},
torrent => {
res(torrent)
}
)
})
this.torrents[torrent.magnetURI] = torrent
}
const handleDownload = () => {
onProgress?.(torrent.progress)
}
torrent.on('download', handleDownload)
if (doSave) {
try {
await this.saveTorrentFiles(torrent, password)
} catch (e) {
torrent.off('download', handleDownload)
// Propagate error to the UI
throw e
}
}
return torrent.files
}
async offer(files: File[] | FileList, password: string) {
const { isPrivate } = await detectIncognito()
const filesToSeed: File[] =
files instanceof FileList ? Array.from(files) : files
const encryptedFiles = await Promise.all(
filesToSeed.map(async file => {
// Force a type conversion here to prevent stream from being typed as a
// NodeJS.ReadableStream, which is the default overloaded return type
// for file.stream().
const stream = file.stream() as any as ReadableStream
const encryptedStream = await getKeychain(password).encryptStream(
stream
)
// WebTorrent internally opens the ReadableStream for file data twice.
// Normally this would not be an issue for File instances provided to
// WebTorrent for seeding. `encryptedFile` is implemented as a facade
// for a File instance, with the key difference being that stream() is
// overridden to return an encrypted instance of the file's stream
// data. If this stream is reopened, an error would be thrown and the
// operation would fail. To avoid this, `encryptedFile` streams are
// tee'd and pooled beforehand so that reopening of the encrypted
// stream data directly is avoided.
//
// See:
// - https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/tee
const streamPool = encryptedStream.tee()
// Providing the file data as a File instance rather than a
// ReadableStream directly (which WebTorrent would accept) prevents
// WebTorrent from loading the entire contents of the file into memory.
//
// See:
// - https://github.com/webtorrent/webtorrent/blob/e26b64c0d0b4bdd8222e19d90bfcf7a688203e3c/index.js#L376-L384
// - https://github.com/feross/simple-concat/blob/44134bf16667b6006a254135d5c8c76ea96823d4/index.js#L3-L8
const encryptedFile = Object.setPrototypeOf(
{
lastModified: file.lastModified,
name: file.name,
size: encryptedSize(file.size),
stream: () => streamPool.pop(),
type: file.type,
},
File.prototype
)
return encryptedFile
})
)
const offer = await new Promise<Torrent>(res => {
this.webTorrentClient.seed(
encryptedFiles,
{
announce: trackerUrls,
// If the user is using their browser's private mode, IndexedDB will
// be unavailable and idbChunkStore will break all transfers. In that
// case, fall back to the default in-memory data store.
store: isPrivate ? undefined : idbChunkStore,
destroyStoreOnDestroy: true,
},
torrent => {
res(torrent)
}
)
})
const { magnetURI } = offer
this.torrents[magnetURI] = offer
return magnetURI
}
rescind(magnetURI: string) {
const torrent = this.torrents[magnetURI]
if (torrent) {
torrent.destroy()
} else {
console.error(`Attempted to clean up nonexistent torrent: ${magnetURI}`)
}
delete this.torrents[magnetURI]
}
rescindAll() {
for (const magnetURI in this.torrents) {
this.rescind(magnetURI)
}
}
isOffering(magnetURI: string) {
return magnetURI in this.torrents
}
handleBeforePageUnload = () => {
this.rescindAll()
}
}
export const fileTransfer = new FileTransfer()
export const fileTransfer = new FileTransfer({
torrentOpts: {
announce: trackerUrls,
},
})

View File

@ -8,12 +8,10 @@ afterEach(() => {
jest.restoreAllMocks()
})
jest.mock('webtorrent', () => ({
jest.mock('secure-file-transfer', () => ({
__esModule: true,
default: class WebTorrent {},
}))
jest.mock('wormhole-crypto', () => ({
__esModule: true,
Keychain: class Keychain {},
FileTransfer: class FileTransfer {
rescindAll() {}
},
setStreamSaverMitm: () => {},
}))