diff --git a/package-lock.json b/package-lock.json index 5d0dd09..0b02744 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" diff --git a/package.json b/package.json index 128a4d8..cb18e47 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 85775b7..6431bc5 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1,216 +1 @@ /// - -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 - - /** - * Returns a `Promise` that resolves to the authentication token as a base64-encoded string. - */ - authTokenB64(): Promise - - /** - * Returns a `Promise` that resolves to the HTTP header value to be provided to the Wormhole server. It contains the authentication token. - */ - authHeader(): Promise - - /** - * 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 - - /** - * 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 - - /** - * 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 - - /** - * 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 - - /** - * @param encryptedMeta The encrypted metadata buffer to decrypt. - * @returns A `Promise` that resolves to a decrypted version of `encryptedMeta`. - */ - decryptMeta(encryptedMeta: Uint8Array): Promise - } - - /** - * 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 -} diff --git a/src/services/FileTransfer/FileTransfer.ts b/src/services/FileTransfer/FileTransfer.ts index b016b5a..dcd899b 100644 --- a/src/services/FileTransfer/FileTransfer.ts +++ b/src/services/FileTransfer/FileTransfer.ts @@ -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 = {} - - 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(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(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, + }, +}) diff --git a/src/setupTests.ts b/src/setupTests.ts index bee9a8e..80ce0c6 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -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: () => {}, }))