diff --git a/client/index.html b/client/index.html index 4c5fb15..cba0dc0 100644 --- a/client/index.html +++ b/client/index.html @@ -21,9 +21,17 @@ <h2>Server Information:</h2> <div style="margin-bottom: 15px;"> <div style="margin-bottom: 10px;"> - <label for="serverPubkey">Server Pubkey:</label><br> - <input type="text" id="serverPubkey" value="npub1r6knexka25dn9w9jnf5kf8xh6gfq7n3p38zfl7nn7cjjjsp4umcqnk0aun" style="width: 100%; padding: 8px;"> + <label for="serverPubkey">Server Pubkey or Search Term:</label><br> + <div class="server-input-container"> + <input type="text" id="serverPubkey" placeholder="npub, username, or NIP-05 identifier" value="npub1r6knexka25dn9w9jnf5kf8xh6gfq7n3p38zfl7nn7cjjjsp4umcqnk0aun" class="server-input"> + <button id="searchServerBtn" class="server-search-button">Search</button> + </div> + <div id="serverSearchResult" class="server-search-result" style="display: none;"> + <!-- Search results will be shown here --> + </div> </div> + + <div> <label for="relay">Response Relay (optional):</label><br> <input type="text" id="relay" value="wss://relay.damus.io" style="width: 100%; padding: 8px;"> @@ -42,6 +50,23 @@ User-Agent: Browser/1.0 <div id="output" hidden> <h2>Converted Event:</h2> <pre id="eventOutput"></pre> + + <div class="publish-container"> + <h2>Publish to Relay:</h2> + <div class="publish-input-container"> + <input type="text" id="publishRelay" value="wss://relay.nostrdev.com" placeholder="wss://relay.example.com" class="publish-input"> + <button id="publishButton" class="publish-button">Publish Event</button> + </div> + <div id="publishResult" class="publish-result" style="display: none;"> + <!-- Publish results will be shown here --> + </div> + </div> + + <div class="qr-container"> + <h2>QR Code:</h2> + <div id="qrCode"></div> + <p><small>Scan this QR code to share the Nostr event</small></p> + </div> </div> </body> </html> \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index de1d90c..94b148e 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,9 +9,13 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "nostr-tools": "^2.12.0" + "nostr-tools": "^2.12.0", + "qrcode": "^1.5.4", + "qrcode-generator": "^1.4.4" }, "devDependencies": { + "@types/qrcode": "^1.5.5", + "@types/qrcode-generator": "^1.0.6", "@typescript-eslint/eslint-plugin": "^8.29.0", "@typescript-eslint/parser": "^8.29.0", "buffer": "^6.0.3", @@ -21,6 +25,7 @@ "eslint": "^9.24.0", "eslint-plugin-import": "^2.31.0", "husky": "^9.1.7", + "node-polyfill-webpack-plugin": "^4.1.0", "process": "^0.11.10", "serve": "^14.0.0", "stream-browserify": "^3.0.0", @@ -728,6 +733,25 @@ "@types/node": "*" } }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qrcode-generator": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/qrcode-generator/-/qrcode-generator-1.0.6.tgz", + "integrity": "sha512-XasuPjhHBC4hyOJ/pHaUNTj+tNxA1SyZpXaS/FOUxEVX03D1gFM8UmMKSIs+pPHLAmRZpU6j9KYxvo+lfsvhKw==", + "deprecated": "This is a stub types definition for qrcode-generator (https://github.com/kazuhikoarase/qrcode-generator). qrcode-generator provides its own type definitions, so you don't need @types/qrcode-generator installed!", + "dev": true, + "dependencies": { + "qrcode-generator": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -1661,6 +1685,19 @@ "dev": true, "license": "MIT" }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -1836,6 +1873,15 @@ "dev": true, "license": "MIT" }, + "node_modules/browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "dependencies": { + "resolve": "^1.17.0" + } + }, "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -2002,6 +2048,15 @@ "dev": true, "license": "MIT" }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, "node_modules/browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -2074,6 +2129,12 @@ "dev": true, "license": "MIT" }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -2358,6 +2419,80 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -2377,7 +2512,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2388,8 +2522,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/colorette": { "version": "2.0.20", @@ -2451,6 +2584,18 @@ "node": ">=0.8" } }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, "node_modules/content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -2565,6 +2710,12 @@ "sha.js": "^2.4.8" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2715,6 +2866,14 @@ "ms": "2.0.0" } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2865,6 +3024,11 @@ "dev": true, "license": "MIT" }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -2890,6 +3054,18 @@ "node": ">=0.10.0" } }, + "node_modules/domain-browser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", + "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3961,6 +4137,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4368,6 +4552,12 @@ } } }, + "node_modules/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 + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4551,6 +4741,22 @@ "node": ">= 10" } }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -4734,7 +4940,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -4816,6 +5021,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-network-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", @@ -5074,6 +5295,15 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-timers-promises": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", + "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -5506,6 +5736,34 @@ "node": ">= 6.13.0" } }, + "node_modules/node-polyfill-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-b4ei444EKkOagG/yFqojrD3QTYM5IOU1f8tn9o6uwrG4qL+brI7oVhjPVd0ZL2xy+Z6CP5bu9w8XTvlWgiXHcw==", + "dev": true, + "dependencies": { + "node-stdlib-browser": "^1.3.0", + "type-fest": "^4.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "webpack": ">=5" + } + }, + "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": { + "version": "4.39.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz", + "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -5513,6 +5771,86 @@ "dev": true, "license": "MIT" }, + "node_modules/node-stdlib-browser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz", + "integrity": "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==", + "dev": true, + "dependencies": { + "assert": "^2.0.0", + "browser-resolve": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^5.7.1", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "create-require": "^1.1.1", + "crypto-browserify": "^3.12.1", + "domain-browser": "4.22.0", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "isomorphic-timers-promises": "^1.0.1", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "pkg-dir": "^5.0.0", + "process": "^0.11.10", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.1", + "url": "^0.11.4", + "util": "^0.12.4", + "vm-browserify": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-stdlib-browser/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/node-stdlib-browser/node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-stdlib-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5579,6 +5917,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -5754,6 +6108,12 @@ "node": ">= 0.8.0" } }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -5823,12 +6183,17 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5890,11 +6255,16 @@ "node": ">= 0.8" } }, + "node_modules/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 + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -6031,6 +6401,14 @@ "node": ">=8" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -6234,6 +6612,27 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode-generator": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", + "integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==" + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -6250,6 +6649,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6446,6 +6854,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -6455,6 +6871,11 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -6907,6 +7328,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6953,6 +7379,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -7255,6 +7687,18 @@ "readable-stream": "^3.5.0" } }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -7526,6 +7970,18 @@ "dev": true, "license": "MIT" }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -7695,6 +8151,12 @@ "dev": true, "license": "0BSD" }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7905,6 +8367,38 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "dev": true, + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7941,6 +8435,12 @@ "node": ">= 0.8" } }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -8316,6 +8816,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", @@ -8407,6 +8912,146 @@ } } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -8923,6 +9568,24 @@ "@types/node": "*" } }, + "@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/qrcode-generator": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/qrcode-generator/-/qrcode-generator-1.0.6.tgz", + "integrity": "sha512-XasuPjhHBC4hyOJ/pHaUNTj+tNxA1SyZpXaS/FOUxEVX03D1gFM8UmMKSIs+pPHLAmRZpU6j9KYxvo+lfsvhKw==", + "dev": true, + "requires": { + "qrcode-generator": "*" + } + }, "@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -9387,9 +10050,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "requires": { - "ajv": "^8.0.0" - } + "requires": {} }, "ajv-keywords": { "version": "5.1.0", @@ -9592,6 +10253,19 @@ } } }, + "assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, "async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -9716,6 +10390,15 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, + "browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, + "requires": { + "resolve": "^1.17.0" + } + }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -9844,6 +10527,15 @@ } } }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, "browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -9878,6 +10570,12 @@ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", "dev": true }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, "bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -10047,6 +10745,64 @@ "is-wsl": "^2.2.0" } }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -10062,7 +10818,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -10070,8 +10825,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "colorette": { "version": "2.0.20", @@ -10121,6 +10875,18 @@ "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -10209,6 +10975,12 @@ "sha.js": "^2.4.8" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -10304,6 +11076,11 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -10407,6 +11184,11 @@ } } }, + "dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + }, "dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -10425,6 +11207,12 @@ "esutils": "^2.0.2" } }, + "domain-browser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", + "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "dev": true + }, "dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -11217,6 +12005,11 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, "get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -11503,6 +12296,12 @@ "micromatch": "^4.0.2" } }, + "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 + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -11610,6 +12409,16 @@ "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true }, + "is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, "is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -11722,8 +12531,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-generator-function": { "version": "1.1.0", @@ -11769,6 +12577,16 @@ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "is-network-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", @@ -11927,6 +12745,12 @@ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, + "isomorphic-timers-promises": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", + "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", + "dev": true + }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -12242,12 +13066,92 @@ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true }, + "node-polyfill-webpack-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-b4ei444EKkOagG/yFqojrD3QTYM5IOU1f8tn9o6uwrG4qL+brI7oVhjPVd0ZL2xy+Z6CP5bu9w8XTvlWgiXHcw==", + "dev": true, + "requires": { + "node-stdlib-browser": "^1.3.0", + "type-fest": "^4.27.0" + }, + "dependencies": { + "type-fest": { + "version": "4.39.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz", + "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==", + "dev": true + } + } + }, "node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true }, + "node-stdlib-browser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz", + "integrity": "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==", + "dev": true, + "requires": { + "assert": "^2.0.0", + "browser-resolve": "^2.0.0", + "browserify-zlib": "^0.2.0", + "buffer": "^5.7.1", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "create-require": "^1.1.1", + "crypto-browserify": "^3.12.1", + "domain-browser": "4.22.0", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "isomorphic-timers-promises": "^1.0.1", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "pkg-dir": "^5.0.0", + "process": "^0.11.10", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.1", + "url": "^0.11.4", + "util": "^0.12.4", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "requires": { + "find-up": "^5.0.0" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -12289,6 +13193,16 @@ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true }, + "object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + } + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -12411,6 +13325,12 @@ "word-wrap": "^1.2.5" } }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, "own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -12454,7 +13374,12 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, "parent-module": { @@ -12494,11 +13419,16 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, + "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 + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-inside": { "version": "1.0.2", @@ -12597,6 +13527,11 @@ } } }, + "pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" + }, "possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -12730,6 +13665,21 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, + "qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "requires": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + } + }, + "qrcode-generator": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", + "integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==" + }, "qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -12739,6 +13689,12 @@ "side-channel": "^1.0.6" } }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -12880,12 +13836,22 @@ "rc": "^1.0.1" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -13209,6 +14175,11 @@ "send": "0.19.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -13246,6 +14217,12 @@ "es-object-atoms": "^1.0.0" } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -13464,6 +14441,18 @@ "readable-stream": "^3.5.0" } }, + "stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -13623,6 +14612,15 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, "tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -13729,6 +14727,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -13866,6 +14870,37 @@ "punycode": "^2.1.0" } }, + "url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "dev": true, + "requires": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + } + } + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -13890,6 +14925,12 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, "watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -14134,6 +15175,11 @@ "is-weakset": "^2.0.3" } }, + "which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, "which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", @@ -14188,6 +15234,114 @@ "dev": true, "requires": {} }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + } + } + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/client/package.json b/client/package.json index 28f7ff1..ae4a305 100644 --- a/client/package.json +++ b/client/package.json @@ -22,6 +22,8 @@ "author": "", "license": "MIT", "devDependencies": { + "@types/qrcode": "^1.5.5", + "@types/qrcode-generator": "^1.0.6", "@typescript-eslint/eslint-plugin": "^8.29.0", "@typescript-eslint/parser": "^8.29.0", "buffer": "^6.0.3", @@ -31,6 +33,7 @@ "eslint": "^9.24.0", "eslint-plugin-import": "^2.31.0", "husky": "^9.1.7", + "node-polyfill-webpack-plugin": "^4.1.0", "process": "^0.11.10", "serve": "^14.0.0", "stream-browserify": "^3.0.0", @@ -42,6 +45,8 @@ "webpack-dev-server": "^5.2.1" }, "dependencies": { - "nostr-tools": "^2.12.0" + "nostr-tools": "^2.12.0", + "qrcode": "^1.5.4", + "qrcode-generator": "^1.4.4" } } diff --git a/client/src/client.ts b/client/src/client.ts index 9a77177..ef5ca45 100644 --- a/client/src/client.ts +++ b/client/src/client.ts @@ -1,16 +1,258 @@ // client.ts - External TypeScript file for HTTP to Nostr converter // This follows strict CSP policies by avoiding inline scripts -// Import the converter function +// Import functions from other modules import { displayConvertedEvent } from './converter'; +import { lookupNip05, searchUsers } from './search'; +import { publishToRelay, convertNpubToHex, verifyEvent } from './relay'; +import { + setDefaultHttpRequest, + sanitizeText, + processTags, + standardizeEvent, + showError, + showSuccess, + showLoading +} from './utils'; + +/** + * Handle the server search button click + */ +async function handleServerSearch(): Promise<void> { + const serverPubkeyInput = document.getElementById('serverPubkey') as HTMLInputElement; + const resultDiv = document.getElementById('serverSearchResult'); + + if (!serverPubkeyInput || !resultDiv) { + return; + } + + const searchTerm = serverPubkeyInput.value.trim(); + if (!searchTerm) { + showError(resultDiv, 'Please enter a search term'); + return; + } + + // If it's a valid npub, no need to search + if (searchTerm.startsWith('npub')) { + try { + const hexPubkey = convertNpubToHex(searchTerm); + if (hexPubkey) { + // It's a valid npub, hide any existing results + resultDiv.style.display = 'none'; + return; + } + } catch (error) { + // Not a valid npub, continue with search + } + } + + // Display loading state + showLoading(resultDiv, 'Searching relays...'); + + try { + const results = await searchUsers(searchTerm); + + if (results.length > 0) { + // If there's only one result and it's a valid npub, use it directly + if (results.length === 1 && results[0].name === 'Valid npub') { + serverPubkeyInput.value = results[0].npub; + resultDiv.style.display = 'none'; + return; + } + + // Create the results list + let resultsHtml = '<div class="search-results-list">'; + + results.forEach(result => { + const truncatedNpub = `${result.npub.substring(0, 10)}...${result.npub.substring(result.npub.length - 5)}`; + resultsHtml += ` + <div class="search-result-item" data-npub="${result.npub}"> + <div class="result-name">${result.name}</div> + <div class="result-npub">${truncatedNpub}</div> + ${result.nip05 ? `<div class="result-nip05">${result.nip05}</div>` : ''} + <button class="use-npub-btn">Use</button> + </div> + `; + }); + + resultsHtml += '</div>'; + resultDiv.innerHTML = resultsHtml; + + // Add click handlers for the "Use" buttons + document.querySelectorAll('.use-npub-btn').forEach(button => { + button.addEventListener('click', (e) => { + const resultItem = (e.target as HTMLElement).closest('.search-result-item'); + if (resultItem) { + const npub = resultItem.getAttribute('data-npub'); + if (npub) { + serverPubkeyInput.value = npub; + resultDiv.innerHTML += '<br><span style="color: #008800;">✓ Applied!</span>'; + } + } + }); + }); + } else { + showError(resultDiv, 'No users found matching your search term'); + } + } catch (error) { + showError(resultDiv, String(error)); + } +} + +/** + * Handle the publish button click + */ +async function handlePublishEvent(): Promise<void> { + const eventOutputPre = document.getElementById('eventOutput') as HTMLElement; + const publishRelayInput = document.getElementById('publishRelay') as HTMLInputElement; + const publishResultDiv = document.getElementById('publishResult') as HTMLElement; + + if (!eventOutputPre || !publishRelayInput || !publishResultDiv) { + return; + } + + // Check if we have a stored event from the creation process + if ((window as any).currentSignedEvent) { + console.log("Using stored signed event from creation process"); + + try { + const event = (window as any).currentSignedEvent; + const relayUrl = publishRelayInput.value.trim(); + + if (!relayUrl || !relayUrl.startsWith('wss://')) { + showError(publishResultDiv, 'Please enter a valid relay URL (must start with wss://)'); + return; + } + + // Display loading state + showLoading(publishResultDiv, 'Publishing to relay...'); + + try { + const result = await publishToRelay(event, relayUrl); + console.log('Publish success:', result); + showSuccess(publishResultDiv, result); + } catch (publishError) { + console.error('Publish error:', publishError); + showError(publishResultDiv, String(publishError)); + // Don't rethrow, just handle it here so the UI shows the error + } + return; + } catch (error) { + console.error("Error using stored event, falling back to parsed event"); + // Continue with normal flow if this fails + } + } + + const eventText = eventOutputPre.textContent || ''; + if (!eventText) { + showError(publishResultDiv, 'No event to publish'); + return; + } + + console.log('Raw event text:', eventText); + + let event; + try { + // Check for non-printable characters or hidden characters that might cause issues + const sanitizedEventText = sanitizeText(eventText); + console.log('Sanitized event text:', sanitizedEventText); + + event = JSON.parse(sanitizedEventText); + console.log('Parsed event:', event); + + // Validate that it's a proper Nostr event + if (!event || typeof event !== 'object') { + throw new Error('Invalid event object'); + } + + // Check if it has id, pubkey, and sig properties which are required for a valid Nostr event + if (!event.id || !event.pubkey || !event.sig) { + throw new Error('Event is missing required properties (id, pubkey, or sig)'); + } + + // Check if pubkey is in npub format and convert it if needed + if (event.pubkey.startsWith('npub')) { + const hexPubkey = convertNpubToHex(event.pubkey); + if (hexPubkey) { + console.log('Converting npub to hex pubkey...'); + event.pubkey = hexPubkey; + } else { + throw new Error('Invalid npub format in pubkey'); + } + } + + // Create a clean event with exactly the fields we need + console.log("Creating a clean event for publishing..."); + event = standardizeEvent(event); + console.log("Clean event created:", JSON.stringify(event, null, 2)); + + } catch (error) { + showError(publishResultDiv, `Invalid event: ${String(error)}`); + return; + } + + const relayUrl = publishRelayInput.value.trim(); + if (!relayUrl || !relayUrl.startsWith('wss://')) { + showError(publishResultDiv, 'Please enter a valid relay URL (must start with wss://)'); + return; + } + + // Display loading state + showLoading(publishResultDiv, 'Publishing to relay...'); + + try { + // Check the event structure + console.log('Full event object before publish:', JSON.stringify(event, null, 2)); + + // Verify event first + console.log('Verifying event...'); + const isValid = verifyEvent(event); + console.log('Event verification result:', isValid); + + if (!isValid) { + console.log('Event verification failed. Proceeding anyway as the relay will validate.'); + } + + // Proceed with publish even if verification failed - the relay will validate + publishResultDiv.innerHTML += '<br><span>Attempting to publish...</span>'; + console.log('Attempting to publish event to relay:', relayUrl); + + try { + const result = await publishToRelay(event, relayUrl); + console.log('Publish success:', result); + showSuccess(publishResultDiv, result); + } catch (publishError) { + console.error('Publish error:', publishError); + showError(publishResultDiv, String(publishError)); + // Don't rethrow, just handle it here so the UI shows the error + } + } catch (error) { + console.error('Error preparing event for publish:', error); + showError(publishResultDiv, `Error preparing event: ${String(error)}`); + } +} // Initialize the event handlers when the DOM is loaded document.addEventListener('DOMContentLoaded', function(): void { // Set up the convert button click handler const convertButton = document.getElementById('convertButton'); + const searchButton = document.getElementById('searchServerBtn'); + const publishButton = document.getElementById('publishButton'); + if (convertButton) { convertButton.addEventListener('click', displayConvertedEvent); } + if (searchButton) { + searchButton.addEventListener('click', handleServerSearch); + } + + if (publishButton) { + publishButton.addEventListener('click', handlePublishEvent); + } + + // Set default HTTP request + setDefaultHttpRequest(); + console.log('HTTP to Nostr converter initialized'); }); \ No newline at end of file diff --git a/client/src/converter.ts b/client/src/converter.ts index 7126110..bf5171c 100644 --- a/client/src/converter.ts +++ b/client/src/converter.ts @@ -7,6 +7,9 @@ declare global { } import { defaultServerConfig, appSettings } from './config'; import * as nostrTools from 'nostr-tools'; +import qrcode from 'qrcode-generator'; +import { convertNpubToHex } from './relay'; +import { processTags, showSuccess } from './utils'; // Generate a keypair for standalone mode (when no extension is available) let standaloneSecretKey: Uint8Array | null = null; @@ -60,16 +63,13 @@ export function convertToEvent( // The second argument MUST be the server's public key in hex format let serverPubkeyHex = serverPubkey; - // Convert npub to hex if needed using nostr-tools + // Convert npub to hex if needed if (serverPubkey.startsWith('npub')) { - try { - const decoded = nostrTools.nip19.decode(serverPubkey); - if (decoded.type === 'npub' && decoded.data) { - serverPubkeyHex = decoded.data; - console.log("Converted npub to hex format:", serverPubkeyHex); - } - } catch (decodeError) { - console.error("Failed to decode npub:", decodeError); + const hexPubkey = convertNpubToHex(serverPubkey); + if (hexPubkey) { + serverPubkeyHex = hexPubkey; + console.log("Converted npub to hex format:", serverPubkeyHex); + } else { throw new Error("Failed to decode npub. Please use a valid npub format."); } } @@ -89,15 +89,31 @@ export function convertToEvent( console.warn("NIP-44 encryption not available. Content will not be encrypted properly."); } + // Convert serverPubkey to hex if it's an npub + let pTagValue = serverPubkey; + if (serverPubkey.startsWith('npub')) { + try { + console.log(`Converting p tag npub to hex: ${serverPubkey}`); + const decoded = nostrTools.nip19.decode(serverPubkey); + if (decoded.type === 'npub' && decoded.data) { + pTagValue = decoded.data as string; + console.log(`Converted to hex pubkey: ${pTagValue}`); + } + } catch (error) { + console.error(`Error decoding npub: ${serverPubkey}`, error); + } + } + + // Create the event with the proper structure const event = { kind: 21120, pubkey: pubkey, content: encryptedContent, // Encrypted HTTP request using NIP-44 tags: [ // Required tags per README specification - ["p", serverPubkey], // P tag to indicate this is a REQUEST + ["p", pTagValue], // P tag with hex pubkey (converted from npub if needed) ["key", decryptkey], // Key for decryption - ["expiration", Math.floor(Date.now() / 1000) + appSettings.expirationTime] + ["expiration", String(Math.floor(Date.now() / 1000) + appSettings.expirationTime)] ] }; @@ -105,6 +121,9 @@ export function convertToEvent( if (relay) { event.tags.push(["r", relay]); } + + // Process tags to ensure proper format (convert any npub in tags to hex, etc.) + event.tags = processTags(event.tags); return JSON.stringify(event, null, 2); } @@ -156,6 +175,9 @@ export async function displayConvertedEvent(): Promise<void> { ); if (convertedEvent) { + // Store the original event in case we need to reference it + (window as any).originalEvent = convertedEvent; + // Parse the event to create a proper Nostr event object for signing const parsedEvent = JSON.parse(convertedEvent); const nostrEvent = { @@ -165,6 +187,9 @@ export async function displayConvertedEvent(): Promise<void> { created_at: Math.floor(Date.now() / 1000), pubkey: parsedEvent.pubkey }; + + // Log the event being signed + console.log("Event to be signed:", JSON.stringify(nostrEvent, null, 2)); let signedEvent; @@ -172,6 +197,7 @@ export async function displayConvertedEvent(): Promise<void> { try { // Try to sign with the NIP-07 extension signedEvent = await window.nostr.signEvent(nostrEvent); + console.log("Event signed with extension:", signedEvent); } catch (error) { console.error("Error signing event with extension:", error); // Fall back to signing with nostr-tools @@ -191,9 +217,211 @@ export async function displayConvertedEvent(): Promise<void> { outputDiv.hidden = false; return; } +// Store the event in a global variable for easier access during publishing +(window as any).currentSignedEvent = signedEvent; + +// Display the event JSON +eventOutputPre.textContent = JSON.stringify(signedEvent, null, 2); + +// Add a helpful message about publishing the event +const publishRelayInput = document.getElementById('publishRelay') as HTMLInputElement; +if (publishRelayInput) { + const publishResult = document.getElementById('publishResult'); + if (publishResult) { + showSuccess(publishResult, 'Event created successfully. Ready to publish!'); + } +} - eventOutputPre.textContent = JSON.stringify(signedEvent, null, 2); + // Generate animated QR code using multiple frames + const qrCodeContainer = document.getElementById('qrCode') as HTMLElement; + if (qrCodeContainer) { + try { + // Convert the event to a JSON string + const eventJson = JSON.stringify(signedEvent); + + // Calculate how many QR codes we need based on the data size + // Use a much smaller chunk size to ensure it fits in the QR code + const maxChunkSize = 500; // Significantly reduced chunk size + const chunks = []; + + // Split the data into chunks + for (let i = 0; i < eventJson.length; i += maxChunkSize) { + chunks.push(eventJson.slice(i, i + maxChunkSize)); + } + + // Prepare container for the animated QR code + qrCodeContainer.innerHTML = ''; + const qrFrameContainer = document.createElement('div'); + qrFrameContainer.className = 'qr-frame-container'; + qrCodeContainer.appendChild(qrFrameContainer); + + // Create QR codes for each chunk with chunk number and total chunks + const qrFrames: HTMLElement[] = []; + chunks.forEach((chunk, index) => { + // Add metadata to identify part of sequence: [current/total]:[data] + const dataWithMeta = `${index + 1}/${chunks.length}:${chunk}`; + + try { + // Create QR code with maximum version and lower error correction + const qr = qrcode(15, 'L'); // Version 15 (higher capacity), Low error correction + qr.addData(dataWithMeta); + qr.make(); + + // Create frame + const frameDiv = document.createElement('div'); + frameDiv.className = 'qr-frame'; + frameDiv.style.display = index === 0 ? 'block' : 'none'; + frameDiv.innerHTML = qr.createSvgTag({ + cellSize: 3, // Smaller cell size + margin: 2 + }); + + qrFrameContainer.appendChild(frameDiv); + qrFrames.push(frameDiv); + } catch (qrError) { + console.error(`Error generating QR code for chunk ${index + 1}:`, qrError); + // Create an error frame instead + const errorDiv = document.createElement('div'); + errorDiv.className = 'qr-frame qr-error'; + errorDiv.style.display = index === 0 ? 'block' : 'none'; + errorDiv.innerHTML = ` + <div class="error-message"> + <p>Error in frame ${index + 1}</p> + <p>Chunk too large for QR code</p> + </div> + `; + qrFrameContainer.appendChild(errorDiv); + qrFrames.push(errorDiv); + } + + }); + + // Add information about the animated QR code + const infoElement = document.createElement('div'); + infoElement.innerHTML = ` + <p class="qr-info">Animated QR code: ${chunks.length} frames containing full event data</p> + <p class="qr-info current-frame">Showing frame 1 of ${chunks.length}</p> + <p class="qr-info"><small>The QR code will cycle through all frames automatically</small></p> + `; + qrCodeContainer.appendChild(infoElement); + + // Animation controls + const controlsDiv = document.createElement('div'); + controlsDiv.className = 'qr-controls'; + controlsDiv.innerHTML = ` + <button id="qrPauseBtn">Pause</button> + <button id="qrPrevBtn">◀ Prev</button> + <button id="qrNextBtn">Next ▶</button> + `; + qrCodeContainer.appendChild(controlsDiv); + + // Set up animation + let currentFrame = 0; + let animationInterval: number | null = null; + let isPaused = false; + + const updateFrameInfo = () => { + const frameInfo = qrCodeContainer.querySelector('.current-frame'); + if (frameInfo) { + frameInfo.textContent = `Showing frame ${currentFrame + 1} of ${chunks.length}`; + } + }; + + const showFrame = (index: number) => { + qrFrames.forEach((frame, i) => { + frame.style.display = i === index ? 'block' : 'none'; + }); + currentFrame = index; + updateFrameInfo(); + }; + + const nextFrame = () => { + showFrame((currentFrame + 1) % qrFrames.length); + }; + + const prevFrame = () => { + showFrame((currentFrame - 1 + qrFrames.length) % qrFrames.length); + }; + + // Start the animation + animationInterval = window.setInterval(nextFrame, 2000); // Change frame every 2 seconds + + // Set up button event handlers + const pauseBtn = document.getElementById('qrPauseBtn'); + const prevBtn = document.getElementById('qrPrevBtn'); + const nextBtn = document.getElementById('qrNextBtn'); + + if (pauseBtn) { + pauseBtn.addEventListener('click', () => { + if (isPaused) { + animationInterval = window.setInterval(nextFrame, 2000); + pauseBtn.textContent = 'Pause'; + } else { + if (animationInterval !== null) { + clearInterval(animationInterval); + animationInterval = null; + } + pauseBtn.textContent = 'Play'; + } + isPaused = !isPaused; + }); + } + + if (prevBtn) { + prevBtn.addEventListener('click', () => { + if (isPaused) { + prevFrame(); + } + }); + } + + if (nextBtn) { + nextBtn.addEventListener('click', () => { + if (isPaused) { + nextFrame(); + } + }); + } + } catch (error) { + console.error("Error generating QR code:", error); + + // Create a fallback display with error information and a more compact representation + qrCodeContainer.innerHTML = ''; + + // Create a backup QR code with just the event ID and relay + try { + const eventId = signedEvent.id || ''; + const relay = encodeURIComponent(defaultServerConfig.defaultRelay); + const nostrUri = `nostr:${eventId}?relay=${relay}`; + + const qr = qrcode(10, 'M'); + qr.addData(nostrUri); + qr.make(); + + qrCodeContainer.innerHTML = ` + <div class="qr-error-container"> + <h3>Event Too Large for Animated QR</h3> + <p>Error: ${String(error)}</p> + <p>Using event reference instead:</p> + ${qr.createSvgTag({ cellSize: 4, margin: 4 })} + <p class="qr-info">This QR code contains a reference to the event: ${eventId.substring(0, 8)}...</p> + </div> + `; + } catch (fallbackError) { + qrCodeContainer.innerHTML = ` + <div class="qr-error-container"> + <h3>QR Generation Failed</h3> + <p>Error: ${String(error)}</p> + <p>Try sharing the event ID manually: ${signedEvent.id || 'Unknown'}</p> + </div> + `; + } + qrCodeContainer.innerHTML = `<p>Error generating QR code: ${error}</p> + <p>Try using a URL shortener with the event ID instead.</p>`; + } + } + outputDiv.hidden = false; } } @@ -201,9 +429,28 @@ export async function displayConvertedEvent(): Promise<void> { // Initialize event listeners when the DOM is fully loaded document.addEventListener('DOMContentLoaded', () => { - // Only set up the click event handler without any automatic encryption + // Set up the click event handler without any automatic encryption const convertButton = document.getElementById('convertButton'); + const publishButton = document.getElementById('publishButton'); + if (convertButton) { convertButton.addEventListener('click', displayConvertedEvent); } + + // Add a handler for the publish button to check if an event is available + if (publishButton) { + publishButton.addEventListener('click', () => { + const eventOutput = document.getElementById('eventOutput'); + const publishResult = document.getElementById('publishResult'); + + if (!eventOutput || !publishResult) { + return; + } + + if (!eventOutput.textContent || eventOutput.textContent.trim() === '') { + publishResult.innerHTML = '<span style="color: #cc0000;">You need to convert an HTTP request first</span>'; + publishResult.style.display = 'block'; + } + }); + } }); \ No newline at end of file diff --git a/client/src/relay.ts b/client/src/relay.ts new file mode 100644 index 0000000..bd96255 --- /dev/null +++ b/client/src/relay.ts @@ -0,0 +1,258 @@ +// relay.ts - Functions for communicating with Nostr relays +import * as nostrTools from 'nostr-tools'; + +/** + * Validate a hex string + * @param str The string to validate + * @param expectedLength The expected length of the hex string + * @returns true if valid, false otherwise + */ +export function isValidHexString(str: string, expectedLength?: number): boolean { + const hexRegex = /^[0-9a-f]*$/i; + if (!hexRegex.test(str)) { + return false; + } + + // Check if it has even length (hex strings should have even length) + if (str.length % 2 !== 0) { + return false; + } + + // Check expected length if provided + if (expectedLength !== undefined && str.length !== expectedLength) { + return false; + } + + return true; +} + +/** + * Publish an event to a Nostr relay + * @param event The Nostr event to publish + * @param relayUrl The URL of the relay to publish to + * @returns Promise that resolves to a success or error message + */ +export async function publishToRelay(event: any, relayUrl: string): Promise<string> { + return new Promise<string>((resolve, reject) => { + try { + // Additional validation specifically for publishing + if (!event || typeof event !== 'object') { + reject(new Error('Invalid event object')); + return; + } + + // Check required fields + if (!event.id || !event.pubkey || !event.sig) { + reject(new Error('Event missing required fields (id, pubkey, sig)')); + return; + } + + // Validate hex strings + if (!isValidHexString(event.id, 64)) { + reject(new Error(`Invalid event ID: ${event.id} (must be 64 hex chars)`)); + return; + } + + if (!isValidHexString(event.pubkey, 64)) { + reject(new Error(`Invalid pubkey: ${event.pubkey} (must be 64 hex chars)`)); + return; + } + + if (!isValidHexString(event.sig, 128)) { + reject(new Error(`Invalid signature: ${event.sig} (must be 128 hex chars)`)); + return; + } + + // Create a relay pool for publishing + const relayPool = new nostrTools.SimplePool(); + + // Set a timeout for the publish operation + const timeout = setTimeout(() => { + relayPool.close([relayUrl]); + reject(new Error(`Timed out connecting to relay: ${relayUrl}`)); + }, 10000); // 10 second timeout + + // Publish the event to the relay + console.log(`Publishing event to relay: ${relayUrl}`); + + // Create a standard formatted event to ensure it follows the relay protocol + const standardEvent = { + id: event.id, + pubkey: event.pubkey, + created_at: Number(event.created_at), + kind: Number(event.kind), + tags: event.tags, + content: event.content, + sig: event.sig + }; + + // Make sure all fields are in the correct format + if (typeof standardEvent.created_at !== 'number') { + standardEvent.created_at = Math.floor(Date.now() / 1000); + } + + if (typeof standardEvent.kind !== 'number') { + standardEvent.kind = 21120; + } + + console.log('Standard event format:', JSON.stringify(standardEvent, null, 2)); + + // Debug log the important hex values + console.log('ID:', standardEvent.id, 'length:', standardEvent.id.length, 'valid hex?', isValidHexString(standardEvent.id, 64)); + console.log('Pubkey:', standardEvent.pubkey, 'length:', standardEvent.pubkey.length, 'valid hex?', isValidHexString(standardEvent.pubkey, 64)); + console.log('Sig:', standardEvent.sig, 'length:', standardEvent.sig.length, 'valid hex?', isValidHexString(standardEvent.sig, 128)); + + // Use the standardized event for publishing + event = standardEvent; + + // Ensure all hex strings are valid (this is a sanity check beyond our validation) + if (!isValidHexString(event.id, 64)) { + reject(new Error(`ID is not a valid hex string: ${event.id}`)); + return; + } + + if (!isValidHexString(event.pubkey, 64)) { + reject(new Error(`Pubkey is not a valid hex string: ${event.pubkey}`)); + return; + } + + if (!isValidHexString(event.sig, 128)) { + reject(new Error(`Sig is not a valid hex string: ${event.sig}`)); + return; + } + + // Try direct WebSocket approach as a fallback + try { + // Use the WebSocket API directly + const ws = new WebSocket(relayUrl); + + let wsTimeout = setTimeout(() => { + try { + ws.close(); + } catch (e) {} + reject(new Error("WebSocket connection timed out")); + }, 10000); + + // Create a flag to track if we've handled response + let responseHandled = false; + + ws.onopen = () => { + // Send the event directly using the relay protocol format ["EVENT", event] + const messageToSend = JSON.stringify(["EVENT", event]); + console.log("Sending WebSocket message:", messageToSend); + ws.send(messageToSend); + }; + + ws.onmessage = (msg) => { + console.log("WebSocket message received:", msg.data); + + if (responseHandled) { + return; // Skip if we've already handled a response + } + + if (typeof msg.data === 'string' && msg.data.startsWith('["OK"')) { + responseHandled = true; + clearTimeout(wsTimeout); + resolve(`Event published successfully via WebSocket`); + try { + ws.close(); + } catch (e) {} + } else if (typeof msg.data === 'string' && (msg.data.includes('invalid') || msg.data.includes('error'))) { + responseHandled = true; + clearTimeout(wsTimeout); + console.error("WebSocket error response:", msg.data); + reject(new Error(`Relay rejected event: ${msg.data}`)); + try { + ws.close(); + } catch (e) {} + } else { + console.log("Received other message, waiting for OK or error response"); + } + }; + + ws.onerror = (error) => { + if (responseHandled) { + return; // Skip if we've already handled a response + } + + responseHandled = true; + clearTimeout(wsTimeout); + console.error("WebSocket error:", error); + reject(new Error(`WebSocket error: ${String(error)}`)); + try { + ws.close(); + } catch (e) {} + }; + + ws.onclose = () => { + clearTimeout(wsTimeout); + console.log("WebSocket closed"); + }; + } catch (wsError) { + // If WebSocket fails, try the regular method + console.error("WebSocket approach failed:", wsError); + + // Use the nostr-tools publish method as a fallback + console.log("Trying nostr-tools publish method..."); + + const publishPromises = relayPool.publish([relayUrl], event); + + // Use Promise.all to wait for all promises to resolve + Promise.all(publishPromises) + .then((relayResults: string[]) => { + clearTimeout(timeout); + relayPool.close([relayUrl]); + if (relayResults && relayResults.length > 0) { + resolve(`Event published to ${relayResults[0]}`); + } else { + resolve(`Event published successfully`); + } + }) + .catch((error: Error) => { + clearTimeout(timeout); + relayPool.close([relayUrl]); + console.error('Error details:', error); + reject(new Error(`Failed to publish event: ${error.message}`)); + }); + } + } catch (error) { + reject(new Error(`Error setting up relay connection: ${String(error)}`)); + } + }); +} + +/** + * Convert an npub to a hex pubkey + * @param npub The npub to convert + * @returns The hex pubkey + */ +export function convertNpubToHex(npub: string): string | null { + if (!npub.startsWith('npub')) { + return null; + } + + try { + const decoded = nostrTools.nip19.decode(npub); + if (decoded.type === 'npub') { + return decoded.data as string; + } + } catch (error) { + console.error('Error decoding npub:', error); + } + + return null; +} + +/** + * Verify a Nostr event + * @param event The event to verify + * @returns True if valid, false otherwise + */ +export function verifyEvent(event: any): boolean { + try { + return nostrTools.verifyEvent(event); + } catch (error) { + console.error('Error verifying event:', error); + return false; + } +} \ No newline at end of file diff --git a/client/src/search.ts b/client/src/search.ts new file mode 100644 index 0000000..5847e0e --- /dev/null +++ b/client/src/search.ts @@ -0,0 +1,154 @@ +// search.ts - Functions for searching for users on Nostr + +import * as nostrTools from 'nostr-tools'; + +// Define popular relays to search for users +export const POPULAR_RELAYS = [ + "wss://relay.damus.io", + "wss://relay.nostr.band", + "wss://nos.lol", + "wss://nostr.wine" +]; + +/** + * Look up a NIP-05 address to find the corresponding public key + * @param nip05Address The NIP-05 address to look up (e.g. user@example.com) + * @returns Promise that resolves to the public key if found, or null if not found + */ +export async function lookupNip05(nip05Address: string): Promise<{pubkey: string, relays?: string[]} | null> { + try { + // Use the NIP-05 lookup function from nostr-tools + const result = await nostrTools.nip05.queryProfile(nip05Address); + + // If the result has a pubkey, return it + if (result && result.pubkey) { + return result; + } + + return null; + } catch (error) { + console.error("Error looking up NIP-05 address:", error); + return null; + } +} + +/** + * Search for users based on a search term across popular relays + * @param searchTerm The username or NIP-05 identifier to search for + * @returns Promise that resolves to an array of user profiles + */ +export async function searchUsers(searchTerm: string): Promise<Array<{name: string, pubkey: string, npub: string, nip05?: string}>> { + const results: Array<{name: string, pubkey: string, npub: string, nip05?: string}> = []; + const processedPubkeys = new Set<string>(); + + // First, check if the input might already be an npub + if (searchTerm.startsWith('npub')) { + try { + const decoded = nostrTools.nip19.decode(searchTerm); + if (decoded.type === 'npub' && decoded.data) { + // It's a valid npub, no need to search + return [{ + name: 'Valid npub', + pubkey: decoded.data, + npub: searchTerm + }]; + } + } catch (error) { + // Not a valid npub, continue with search + console.log("Not a valid npub, continuing with search"); + } + } + + // Check if the search term looks like a NIP-05 identifier + if (searchTerm.includes('@')) { + try { + const nip05Result = await lookupNip05(searchTerm); + if (nip05Result && nip05Result.pubkey) { + const npub = nostrTools.nip19.npubEncode(nip05Result.pubkey); + results.push({ + name: searchTerm, + pubkey: nip05Result.pubkey, + npub: npub, + nip05: searchTerm + }); + processedPubkeys.add(nip05Result.pubkey); + } + } catch (error) { + console.error("Error looking up NIP-05:", error); + } + } + + // Create a pool of relays to search + const relayPool = new nostrTools.SimplePool(); + + try { + // Create a filter for the subscription + const filter = { + kinds: [0], // Only metadata events + limit: 20 // Limit to 20 results + }; + + // Set a timeout to ensure we don't wait forever + const timeoutPromise = new Promise<void>((resolve) => { + setTimeout(() => resolve(), 5000); // 5 second timeout + }); + + // Create an event handler + const eventHandler = (event: any) => { + try { + // Skip if we've already processed this pubkey + if (processedPubkeys.has(event.pubkey)) { + return; + } + + // Parse the profile metadata from content + const profile = JSON.parse(event.content); + + // Check if the profile matches our search term + const searchTermLower = searchTerm.toLowerCase(); + const nameLower = (profile.name || '').toLowerCase(); + const displayNameLower = (profile.display_name || '').toLowerCase(); + const nip05Lower = (profile.nip05 || '').toLowerCase(); + + if ( + nameLower.includes(searchTermLower) || + displayNameLower.includes(searchTermLower) || + nip05Lower.includes(searchTermLower) + ) { + // Add to results + const npub = nostrTools.nip19.npubEncode(event.pubkey); + results.push({ + name: profile.display_name || profile.name || 'Unknown', + pubkey: event.pubkey, + npub: npub, + nip05: profile.nip05 + }); + + // Mark as processed + processedPubkeys.add(event.pubkey); + } + } catch (error) { + console.error("Error processing event:", error); + } + }; + + // Subscribe to events matching the filter + const sub = relayPool.subscribeMany( + POPULAR_RELAYS, + [filter], + { onevent: eventHandler } + ); + + // Wait for timeout + await timeoutPromise; + + // Close the subscription + sub.close(); + relayPool.close(POPULAR_RELAYS); + + } catch (error) { + console.error("Error searching relays:", error); + } + + return results; +} \ No newline at end of file diff --git a/client/src/styles.css b/client/src/styles.css index d5de8d9..a15ff58 100644 --- a/client/src/styles.css +++ b/client/src/styles.css @@ -1,3 +1,213 @@ +/* Styles for the HTTP to Nostr converter */ + +/* General layout */ +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + max-width: 800px; + margin: 0 auto; + padding: 20px; + background-color: #f8f9fa; + color: #212529; + line-height: 1.6; +} + +/* Headings */ +h1 { + color: #343a40; + margin-bottom: 20px; + border-bottom: 2px solid #6c757d; + padding-bottom: 10px; +} + +h2 { + color: #495057; + margin-top: 25px; + margin-bottom: 15px; +} + +/* Info box */ +.info-box { + background-color: #e2f0fd; + border-left: 4px solid #0d6efd; + padding: 10px 15px; + margin-bottom: 20px; + border-radius: 0 4px 4px 0; +} + +/* Form elements */ +input[type="text"], textarea { + width: 100%; + padding: 8px 10px; + margin-bottom: 15px; + border: 1px solid #ced4da; + border-radius: 4px; + font-size: 14px; +} + +textarea { + height: 150px; + font-family: monospace; +} + +button { + background-color: #0d6efd; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s; +} + +button:hover { + background-color: #0b5ed7; +} + +/* Server input section */ +.server-input-container { + display: flex; + gap: 8px; + margin-top: 5px; +} + +.server-input { + flex-grow: 1; + margin-bottom: 0; +} + +.server-search-button { + padding: 8px 15px; +} + +.server-search-result { + margin-top: 10px; + padding: 10px; + background-color: #ffffff; + border-radius: 4px; + border: 1px solid #dee2e6; +} + +/* Output section */ +#output { + margin-top: 30px; + padding: 15px; + background-color: #ffffff; + border-radius: 4px; + border: 1px solid #dee2e6; + box-shadow: 0 2px 4px rgba(0,0,0,0.05); +} + +pre { + background-color: #f8f9fa; + padding: 15px; + border-radius: 4px; + overflow-x: auto; + font-family: 'Courier New', Courier, monospace; + font-size: 14px; + line-height: 1.4; + border: 1px solid #dee2e6; +} + +/* Publish container */ +.publish-container { + margin-top: 20px; + margin-bottom: 20px; + padding: 15px; + background-color: #f1f8ff; + border-radius: 4px; + border: 1px solid #b3d7ff; +} + +.publish-input-container { + display: flex; + gap: 8px; + margin-top: 10px; + margin-bottom: 10px; +} + +.publish-input { + flex-grow: 1; + padding: 8px 10px; + border: 1px solid #ced4da; + border-radius: 4px; + font-size: 14px; +} + +.publish-button { + background-color: #28a745; + color: white; + border: none; + padding: 8px 15px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s; +} + +.publish-button:hover { + background-color: #218838; +} + +.publish-result { + margin-top: 10px; + padding: 10px; + background-color: #ffffff; + border-radius: 4px; + border: 1px solid #dee2e6; +} + +/* QR code container */ +.qr-container { + margin-top: 20px; + text-align: center; +} + +#qrCode { + margin: 0 auto; + max-width: 100%; +} + +/* Search results styling */ +.search-results-list { + max-height: 300px; + overflow-y: auto; +} + +.search-result-item { + padding: 10px; + border-bottom: 1px solid #dee2e6; + display: flex; + flex-direction: column; + gap: 5px; + position: relative; +} + +.search-result-item:last-child { + border-bottom: none; +} + +.result-name { + font-weight: bold; +} + +.result-npub { + font-family: monospace; + font-size: 12px; + color: #6c757d; +} + +.result-nip05 { + font-size: 12px; + color: #0d6efd; +} + +.use-npub-btn { + align-self: flex-end; + padding: 5px 10px; + font-size: 12px; + margin-top: 5px; +} body { font-family: Arial, sans-serif; max-width: 800px; @@ -38,6 +248,91 @@ button:hover { background-color: #f9f9f9; } +.qr-container { + margin-top: 30px; + text-align: center; +} + +#qrCode { + margin: 0 auto; + display: block; + background-color: white; + padding: 15px; + border-radius: 4px; + text-align: center; +} + +.qr-info { + margin-top: 8px; + font-size: 14px; + color: #555; + text-align: center; +} + +.qr-frame-container { + width: 300px; + height: 300px; + margin: 0 auto; + position: relative; +} + +.qr-frame { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + transition: opacity 0.5s ease; +} + +.qr-controls { + margin: 15px auto; + text-align: center; +} + +.qr-controls button { + margin: 0 5px; + padding: 8px 15px; + background-color: #4CAF50; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.qr-controls button:hover { + background-color: #45a049; +} + +.qr-error { + background-color: #ffeeee; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #ffcccc; + border-radius: 4px; +} + +.error-message { + text-align: center; + color: #cc0000; + padding: 20px; +} + +.qr-error-container { + padding: 20px; + background-color: #f8f8f8; + border: 1px solid #ddd; + border-radius: 4px; + text-align: center; +} + +.qr-error-container h3 { + color: #cc0000; + margin-top: 0; +} + pre { background-color: #f5f5f5; padding: 10px; @@ -60,4 +355,70 @@ h1 { h2 { color: #555; +} + +/* NIP-05 lookup styles */ +.nip05-section { + margin: 10px 0; + padding: 10px; + background-color: #f5f5f5; + border-radius: 4px; + border-left: 3px solid #4CAF50; +} + +.nip05-input-container { + display: flex; + margin-top: 5px; +} + +.nip05-input { + flex-grow: 1; + padding: 8px; + margin-right: 8px; + border: 1px solid #ddd; + border-radius: 4px; +} + +.nip05-button { + white-space: nowrap; + background-color: #4CAF50; + color: white; + border: none; + border-radius: 4px; + padding: 8px 15px; + cursor: pointer; +} + +.nip05-button:hover { + background-color: #45a049; +} + +.nip05-result { + margin-top: 8px; + font-size: 14px; + padding: 5px; + border-radius: 4px; +} + +.nip05-success { + color: #008800; +} + +.nip05-error { + color: #cc0000; +} + +.use-pubkey-button { + margin-left: 10px; + font-size: 12px; + padding: 3px 8px; + background-color: #4CAF50; + color: white; + border: none; + border-radius: 3px; + cursor: pointer; +} + +.use-pubkey-button:hover { + background-color: #45a049; } \ No newline at end of file diff --git a/client/src/utils.ts b/client/src/utils.ts new file mode 100644 index 0000000..6072183 --- /dev/null +++ b/client/src/utils.ts @@ -0,0 +1,124 @@ +// utils.ts - Utility functions for the HTTP to Nostr converter + +/** + * Populate the HTTP request textarea with a default example + */ +export function setDefaultHttpRequest(): void { + const httpRequestBox = document.getElementById('httpRequest') as HTMLTextAreaElement; + if (httpRequestBox) { + // Only set default if the textarea is empty + if (!httpRequestBox.value.trim()) { + const defaultRequest = `GET /index.html HTTP/1.1 +Host: example.com +User-Agent: NostrClient/1.0 +Accept: text/html,application/xhtml+xml,application/xml +Connection: keep-alive + +`; + httpRequestBox.value = defaultRequest; + } + } +} + +/** + * Sanitize text to remove non-printable characters + * @param text Text to sanitize + * @returns Sanitized text + */ +export function sanitizeText(text: string): string { + return text.replace(/[^\x20-\x7E]/g, ''); +} + +/** + * Process event tags to convert any npub values in "p" tags to hex pubkeys + * @param tags The event tags to process + * @returns The processed tags + */ +export function processTags(tags: string[][]): string[][] { + if (!tags || !Array.isArray(tags)) { + return tags; + } + + // Make a deep copy of tags to avoid modifying the original + const processedTags = JSON.parse(JSON.stringify(tags)); + + for (let i = 0; i < processedTags.length; i++) { + const tag = processedTags[i]; + if (!tag || !Array.isArray(tag) || tag.length < 2) { + continue; + } + + // Convert npub in "p" tags to hex pubkeys + if (tag[0] === 'p' && typeof tag[1] === 'string' && tag[1].startsWith('npub')) { + try { + console.log(`Processing p tag: ${tag[1]}`); + const { convertNpubToHex } = require('./relay'); + const hexPubkey = convertNpubToHex(tag[1]); + if (hexPubkey) { + processedTags[i][1] = hexPubkey; + console.log(`Converted npub to hex: ${hexPubkey}`); + } + } catch (error) { + console.error(`Error processing p tag: ${tag[1]}`, error); + } + } + + // Ensure expiration tag value is a string + if (tag[0] === 'expiration' && tag[1]) { + processedTags[i][1] = String(tag[1]); + } + } + + return processedTags; +} + +/** + * Create a standardized event object with proper types + * @param event The event to standardize + * @returns Standardized event + */ +export function standardizeEvent(event: any): any { + if (!event || typeof event !== 'object') { + return event; + } + + return { + id: event.id, + pubkey: event.pubkey, + created_at: Number(event.created_at), + kind: Number(event.kind), + tags: processTags(event.tags), + content: event.content, + sig: event.sig + }; +} + +/** + * Display an error message in the specified element + * @param element The element to display the error in + * @param message The error message + */ +export function showError(element: HTMLElement, message: string): void { + element.innerHTML = `<span style="color: #cc0000;">Error: ${message}</span>`; + element.style.display = 'block'; +} + +/** + * Display a success message in the specified element + * @param element The element to display the success message in + * @param message The success message + */ +export function showSuccess(element: HTMLElement, message: string): void { + element.innerHTML = `<span style="color: #008800;">✓ ${message}</span>`; + element.style.display = 'block'; +} + +/** + * Display a loading message in the specified element + * @param element The element to display the loading message in + * @param message The loading message + */ +export function showLoading(element: HTMLElement, message: string = 'Processing...'): void { + element.innerHTML = `<span>${message}</span>`; + element.style.display = 'block'; +} \ No newline at end of file diff --git a/client/webpack.config.js b/client/webpack.config.js index 24be23e..d2b9796 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -1,6 +1,8 @@ const path = require('path'); const CopyPlugin = require('copy-webpack-plugin'); const webpack = require('webpack'); +// Polyfills for Node.js core modules in the browser +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); module.exports = { mode: 'development', @@ -21,10 +23,7 @@ module.exports = { ], }, plugins: [ - new webpack.ProvidePlugin({ - Buffer: ['buffer', 'Buffer'], - process: 'process/browser', - }), + new NodePolyfillPlugin(), new CopyPlugin({ patterns: [ { from: 'src/styles.css', to: 'styles.css' }, @@ -33,20 +32,12 @@ module.exports = { ], }), ], - resolve: { - extensions: ['.tsx', '.ts', '.js'], - }, output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, resolve: { extensions: ['.tsx', '.ts', '.js'], - fallback: { - "crypto": false, - "buffer": false, - "stream": false - } }, devServer: { static: {