diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index d9f95256..54a9165e 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -508,6 +508,9 @@ importers: '@types/react-dom': specifier: ^18.3.0 version: 18.3.1 + surge: + specifier: ^0.24.6 + version: 0.24.6 typescript: specifier: ^5.5.3 version: 5.6.3 @@ -2557,6 +2560,13 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -2581,10 +2591,19 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + aws-sign2@0.7.0: + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} + + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + axe-core@4.10.1: resolution: {integrity: sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==} engines: {node: '>=4'} + axios@1.7.3: + resolution: {integrity: sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==} + axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} @@ -2592,6 +2611,9 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + babar@0.2.3: + resolution: {integrity: sha512-1hmYKLj+7m5qHsJ3hosOlO7Z5BYe3E8u9u/W2BEqB4kytysuHYuGe5OIrEr7q4Zyg3y3EytFb4YrPZokYSix8g==} + babel-jest@27.5.1: resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -2691,6 +2713,9 @@ packages: batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bfj@7.1.0: resolution: {integrity: sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==} engines: {node: '>= 8.0.0'} @@ -2702,6 +2727,10 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + block-stream@0.0.9: + resolution: {integrity: sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==} + engines: {node: 0.4 || >=0.5.8} + bluebird@1.0.8: resolution: {integrity: sha512-e8rlJcByuxPdMiiwU3lGtENflMtUncAblzSlN7rBAZ9ygb75D/rng3Xt5FbZpYqVCzK+sFHVIMht4G6fbfUfbA==} @@ -2791,6 +2820,9 @@ packages: resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} engines: {node: '>=4'} + caseless@0.12.0: + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2836,6 +2868,10 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} + cli-table3@0.6.1: + resolution: {integrity: sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==} + engines: {node: 10.* || >= 12.*} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2870,6 +2906,10 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2950,6 +2990,9 @@ packages: core-js@3.39.0: resolution: {integrity: sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==} + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3096,6 +3139,10 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + dashdash@1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -3273,6 +3320,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecc-jsbn@0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} @@ -3677,6 +3727,13 @@ packages: resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} engines: {node: '>= 0.10.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3770,6 +3827,9 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + forever-agent@0.6.1: + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + fork-ts-checker-webpack-plugin@6.5.3: resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} @@ -3784,6 +3844,10 @@ packages: vue-template-compiler: optional: true + form-data@2.3.3: + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} + form-data@4.0.1: resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} engines: {node: '>= 6'} @@ -3818,6 +3882,11 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3862,6 +3931,9 @@ packages: get-tsconfig@4.8.1: resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + getpass@0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + git-hooks-list@3.1.0: resolution: {integrity: sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==} @@ -3928,6 +4000,15 @@ packages: handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + har-schema@2.0.0: + resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} + engines: {node: '>=4'} + + har-validator@5.1.5: + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} + deprecated: this library is no longer supported + harmony-reflect@1.6.2: resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} @@ -4028,6 +4109,13 @@ packages: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} + http-signature@1.2.0: + resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} + engines: {node: '>=0.8', npm: '>=1.3.7'} + + human-readable-numbers@0.9.5: + resolution: {integrity: sha512-VC1uYLm7FR+4UkLaQdXPLodz7xTVLBte3X6iMCYK/uPJywoHyEZfR40+kCN4YqYd+FNCPJAJNYv2CSMvAprgsQ==} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -4160,6 +4248,9 @@ packages: engines: {node: '>=8'} hasBin: true + is-domain@0.0.1: + resolution: {integrity: sha512-hLm9uZUDm/sk0+xZgxyJluSf4B37sg3ivzv4ndTxNCAMnWFUUsHh1u4eh2maEcEvQl3mc65a9pJ/KURGItbLIg==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -4281,6 +4372,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isstream@0.1.2: + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -4488,6 +4582,9 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsbn@0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -4515,6 +4612,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + json2mq@0.2.0: resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} @@ -4544,6 +4644,10 @@ packages: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} + jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -4777,10 +4881,17 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} + minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -4792,6 +4903,9 @@ packages: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true + moniker@0.1.2: + resolution: {integrity: sha512-Uj9iV0QYr6281G+o0TvqhKwHHWB2Q/qUTT4LPQ3qDGc0r8cbMuqQjRXPZuVZ+gcL7APx+iQgE8lcfWPrj1LsLA==} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -4802,6 +4916,9 @@ packages: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -4823,6 +4940,9 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + netrc@0.1.4: + resolution: {integrity: sha512-ye8AIYWQcP9MvoM1i0Z2jV0qed31Z8EWXYnyGNkiUAd+Fo8J+7uy90xTV8g/oAbhtjkY7iZbNTizQaXdKUuwpQ==} + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -4861,6 +4981,9 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + oauth-sign@0.9.0: + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -5524,6 +5647,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + promise@8.3.0: resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} @@ -5541,6 +5668,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -5560,6 +5690,10 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} + qs@6.5.3: + resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -5849,6 +5983,10 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + read@1.0.5: + resolution: {integrity: sha512-hDLATrzYLoMu23c/69pMC6u3fO3Y0qLTIygJkEZHLOn+AO2gSapu6QgrgwX9ehyVtaRoZVZbF4IuiZPPRdGgdg==} + engines: {node: '>=0.8'} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -5928,6 +6066,11 @@ packages: renderkid@3.0.0: resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + request@2.88.2: + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -5995,6 +6138,11 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -6230,9 +6378,17 @@ packages: resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} engines: {node: '>=6.0.0'} + split@1.0.1: + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sshpk@1.18.0: + resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} + engines: {node: '>=0.10.0'} + hasBin: true + stable@0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' @@ -6377,6 +6533,25 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + surge-fstream-ignore@1.1.0: + resolution: {integrity: sha512-A3PZYu/d1/gCQtfI0k8uWQN5/WTKKsIAC1wGtCREbctXjE9cxgORZxJm6TCq/Wy6L1jl6PFQIpf4NidcWSkKMQ==} + + surge-ignore@0.2.0: + resolution: {integrity: sha512-ay4MPFjfiQzDsyTidljJLXQi22l2AwjcuamYnJWj/LdhaHdKmDJxRox52WXimdcLpMuLDtkQvv4+jEu+wu9eSw==} + + surge-ignore@0.3.0: + resolution: {integrity: sha512-tFq03FSTC2w6iVyJkzgFzwa2+lW4DZheIvStTO4Vn82Mk1x9aJyt/WUz5sKZE9woCn5dPB7nLARVFqJXDv5trA==} + + surge-sdk@0.6.4: + resolution: {integrity: sha512-v22+Kz8oyO/SL9EULWj2d+WuZFEL07UWfOaCBMDrcwSi1gf1GNmadhlJRSlvZ/IVUW1/PoyVlckKNkYXqiuxTw==} + + surge-stream@0.6.4: + resolution: {integrity: sha512-wf2Pl/R5vPMFNQkEajzNggRWhWHeeeW7XtTbfUWhjndId651yk3eLbns5B3t56L2Z5SzWEec7SIeKgoTph2A6g==} + + surge@0.24.6: + resolution: {integrity: sha512-tfJpTBlTyrAgmEVp21cyagcszFXNBZA6Ba9SkngRHHtKtfbJdJXQ7G1udW6HTLaUkzWDB7aheiyG+NSkZy/0PQ==} + hasBin: true + svg-parser@2.0.4: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} @@ -6412,6 +6587,9 @@ packages: resolution: {integrity: sha512-V3tQqlti/JJm577hhDdoCOw1xl4aPcDjTY6PesnFjWDcUYzB6fz39ETXFQuPr9piaoHPfc65sjAehTgbfXOvxQ==} hasBin: true + tarr@1.1.0: + resolution: {integrity: sha512-tENbQ43IQckay71stp1p1lljRhoEZpZk10FzEZKW2tJcMcnLwV3CfZdxBAERlH6nwnFvnHMS9eJOJl6IzSsG0g==} + temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -6479,6 +6657,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + tough-cookie@2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -6533,6 +6715,12 @@ packages: peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -6655,6 +6843,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-parse-as-address@1.0.0: + resolution: {integrity: sha512-1WJ8YX1Kcec9wgxy8d/ATzGP1ayO6BRnd3iB6NlM+7cOnn6U8p5PKppRTCPLobh3CSdJ4d0TdPjopzyU2KcVFw==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -6668,6 +6859,11 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -6683,6 +6879,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -6877,6 +7077,10 @@ packages: utf-8-validate: optional: true + xbytes@1.9.1: + resolution: {integrity: sha512-29E0ygMFWrM5JW2W1ypmezjyR2FOS5aCszdLe620ymSJBoZzL0/RCLJKCoUFu3DfQGhZ/FWykEBp5dfEy6+hjA==} + engines: {node: '>=1'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -9446,6 +9650,12 @@ snapshots: asap@2.0.6: {} + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assert-plus@1.0.0: {} + ast-types-flow@0.0.8: {} async@3.2.6: {} @@ -9468,8 +9678,20 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 + aws-sign2@0.7.0: {} + + aws4@1.13.2: {} + axe-core@4.10.1: {} + axios@1.7.3: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axios@1.7.7: dependencies: follow-redirects: 1.15.9 @@ -9480,6 +9702,10 @@ snapshots: axobject-query@4.1.0: {} + babar@0.2.3: + dependencies: + colors: 1.4.0 + babel-jest@27.5.1(@babel/core@7.25.9): dependencies: '@babel/core': 7.25.9 @@ -9656,6 +9882,10 @@ snapshots: batch@0.6.1: {} + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + bfj@7.1.0: dependencies: bluebird: 3.7.2 @@ -9668,6 +9898,10 @@ snapshots: binary-extensions@2.3.0: {} + block-stream@0.0.9: + dependencies: + inherits: 2.0.4 + bluebird@1.0.8: {} bluebird@3.7.2: {} @@ -9764,6 +9998,8 @@ snapshots: case-sensitive-paths-webpack-plugin@2.4.0: {} + caseless@0.12.0: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -9812,6 +10048,12 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 + cli-table3@0.6.1: + dependencies: + string-width: 4.2.3 + optionalDependencies: + colors: 1.4.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -9844,6 +10086,8 @@ snapshots: colorette@2.0.20: {} + colors@1.4.0: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -9908,6 +10152,8 @@ snapshots: core-js@3.39.0: {} + core-util-is@1.0.2: {} + core-util-is@1.0.3: {} cosmiconfig@6.0.0: @@ -10079,6 +10325,10 @@ snapshots: damerau-levenshtein@1.0.8: {} + dashdash@1.14.1: + dependencies: + assert-plus: 1.0.0 + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -10254,6 +10504,11 @@ snapshots: eastasianwidth@0.2.0: {} + ecc-jsbn@0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 @@ -10848,6 +11103,10 @@ snapshots: utils-merge: 1.0.1 vary: 1.1.2 + extend@3.0.2: {} + + extsprintf@1.3.0: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -10947,6 +11206,8 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + forever-agent@0.6.1: {} + fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.1)(typescript@5.6.3)(webpack@5.95.0): dependencies: '@babel/code-frame': 7.25.9 @@ -10967,6 +11228,12 @@ snapshots: optionalDependencies: eslint: 8.57.1 + form-data@2.3.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + form-data@4.0.1: dependencies: asynckit: 0.4.0 @@ -10999,6 +11266,13 @@ snapshots: fsevents@2.3.3: optional: true + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + function-bind@1.1.2: {} function.prototype.name@1.1.6: @@ -11040,6 +11314,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + getpass@0.1.7: + dependencies: + assert-plus: 1.0.0 + git-hooks-list@3.1.0: {} glob-parent@5.1.2: @@ -11122,6 +11400,13 @@ snapshots: handle-thing@2.0.1: {} + har-schema@2.0.0: {} + + har-validator@5.1.5: + dependencies: + ajv: 6.12.6 + har-schema: 2.0.0 + harmony-reflect@1.6.2: {} has-bigints@1.0.2: {} @@ -11229,6 +11514,14 @@ snapshots: transitivePeerDependencies: - debug + http-signature@1.2.0: + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.2 + sshpk: 1.18.0 + + human-readable-numbers@0.9.5: {} + human-signals@2.1.0: {} iconv-lite@0.4.24: @@ -11343,6 +11636,8 @@ snapshots: is-docker@2.2.1: {} + is-domain@0.0.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.0.2: @@ -11433,6 +11728,8 @@ snapshots: isexe@2.0.0: {} + isstream@0.1.2: {} + istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: @@ -11863,6 +12160,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsbn@0.1.1: {} + jsesc@0.5.0: {} jsesc@3.0.2: {} @@ -11879,6 +12178,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-safe@5.0.1: {} + json2mq@0.2.0: dependencies: string-convert: 0.2.1 @@ -11918,6 +12219,13 @@ snapshots: ms: 2.1.3 semver: 7.6.3 + jsprim@1.4.2: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -12115,10 +12423,16 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@7.4.6: + dependencies: + brace-expansion: 2.0.1 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 + minimist@1.2.6: {} + minimist@1.2.8: {} minipass@7.1.2: {} @@ -12127,6 +12441,8 @@ snapshots: dependencies: minimist: 1.2.8 + moniker@0.1.2: {} + ms@2.0.0: {} ms@2.1.3: {} @@ -12136,6 +12452,8 @@ snapshots: dns-packet: 5.6.1 thunky: 1.1.0 + mute-stream@0.0.8: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -12152,6 +12470,8 @@ snapshots: neo-async@2.6.2: {} + netrc@0.1.4: {} + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -12188,6 +12508,8 @@ snapshots: dependencies: boolbase: 1.0.0 + oauth-sign@0.9.0: {} + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -12846,6 +13168,8 @@ snapshots: process-nextick-args@2.0.1: {} + progress@2.0.3: {} + promise@8.3.0: dependencies: asap: 2.0.6 @@ -12868,6 +13192,8 @@ snapshots: proxy-from-env@1.1.0: {} + psl@1.9.0: {} + punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -12878,6 +13204,8 @@ snapshots: dependencies: side-channel: 1.0.6 + qs@6.5.3: {} + queue-microtask@1.2.3: {} raf@3.4.1: @@ -13294,6 +13622,10 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + read@1.0.5: + dependencies: + mute-stream: 0.0.8 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -13393,6 +13725,29 @@ snapshots: lodash: 4.17.21 strip-ansi: 6.0.1 + request@2.88.2: + dependencies: + aws-sign2: 0.7.0 + aws4: 1.13.2 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.5 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.3 + safe-buffer: 5.2.1 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 3.4.0 + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -13446,6 +13801,10 @@ snapshots: reusify@1.0.4: {} + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -13716,8 +14075,24 @@ snapshots: transitivePeerDependencies: - supports-color + split@1.0.1: + dependencies: + through: 2.3.8 + sprintf-js@1.0.3: {} + sshpk@1.18.0: + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + stable@0.1.8: {} stack-utils@2.0.6: @@ -13876,6 +14251,55 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + surge-fstream-ignore@1.1.0: + dependencies: + fstream: 1.0.12 + inherits: 2.0.4 + minimatch: 7.4.6 + + surge-ignore@0.2.0: {} + + surge-ignore@0.3.0: {} + + surge-sdk@0.6.4: + dependencies: + axios: 1.7.3 + transitivePeerDependencies: + - debug + + surge-stream@0.6.4: + dependencies: + axios: 1.7.3 + request: 2.88.2 + split: 1.0.1 + surge-fstream-ignore: 1.1.0 + surge-ignore: 0.3.0 + tarr: 1.1.0 + transitivePeerDependencies: + - debug + + surge@0.24.6: + dependencies: + babar: 0.2.3 + cli-table3: 0.6.1 + colors: 1.4.0 + human-readable-numbers: 0.9.5 + is-domain: 0.0.1 + minimist: 1.2.6 + moniker: 0.1.2 + netrc: 0.1.4 + progress: 2.0.3 + prompts: 2.4.2 + read: 1.0.5 + request: 2.88.2 + surge-ignore: 0.2.0 + surge-sdk: 0.6.4 + surge-stream: 0.6.4 + url-parse-as-address: 1.0.0 + xbytes: 1.9.1 + transitivePeerDependencies: + - debug + svg-parser@2.0.4: {} svgo@1.3.2: @@ -13949,6 +14373,12 @@ snapshots: resumer: 0.0.0 through: 2.3.8 + tarr@1.1.0: + dependencies: + block-stream: 0.0.9 + fstream: 1.0.12 + inherits: 2.0.4 + temp-dir@2.0.0: {} tempy@0.6.0: @@ -14006,6 +14436,11 @@ snapshots: toidentifier@1.0.1: {} + tough-cookie@2.5.0: + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + tr46@1.0.1: dependencies: punycode: 2.3.1 @@ -14053,6 +14488,12 @@ snapshots: tslib: 1.14.1 typescript: 5.6.3 + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + tweetnacl@0.14.5: {} + type-check@0.3.2: dependencies: prelude-ls: 1.1.2 @@ -14170,6 +14611,8 @@ snapshots: dependencies: punycode: 2.3.1 + url-parse-as-address@1.0.0: {} + util-deprecate@1.0.2: {} util.promisify@1.0.1: @@ -14183,6 +14626,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@3.4.0: {} + uuid@8.3.2: {} v8-to-istanbul@9.3.0: @@ -14198,6 +14643,12 @@ snapshots: vary@1.1.2: {} + verror@1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -14534,6 +14985,8 @@ snapshots: ws@8.18.0: {} + xbytes@1.9.1: {} + y18n@5.0.8: {} yallist@3.1.1: {} diff --git a/cspell.json b/cspell.json index 62d884db..73f564f1 100644 --- a/cspell.json +++ b/cspell.json @@ -4,7 +4,7 @@ "dictionaryDefinitions": [], "dictionaries": [], "caseSensitive": false, - "words": ["preinstall", "undici", "packagejson"], + "words": ["packagejson", "pkce", "PKCE", "preinstall", "undici"], "ignoreWords": [], "import": [] } diff --git a/examples/realtime-console/package.json b/examples/realtime-console/package.json index 867bc469..b06b236e 100644 --- a/examples/realtime-console/package.json +++ b/examples/realtime-console/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "build": "node scripts/build.js", + "deploy": "npm run build && surge --project ./build --domain $SURGE_DOMAIN --token $SURGE_TOKEN", "start": "node scripts/start.js", "test": "exit 0" }, @@ -135,6 +136,7 @@ "@types/node": "^20", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", + "surge": "^0.24.6", "typescript": "^5.5.3" } } diff --git a/examples/realtime-console/src/ConsoleFooter.tsx b/examples/realtime-console/src/ConsoleFooter.tsx index df8bc3d6..2d182743 100644 --- a/examples/realtime-console/src/ConsoleFooter.tsx +++ b/examples/realtime-console/src/ConsoleFooter.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; -import { Button, message, Typography } from 'antd'; +import { Button, message, Typography, Select } from 'antd'; import { RealtimeAPIError, type RealtimeClient, @@ -11,7 +11,7 @@ import { AudioOutlined, AudioMutedOutlined } from '@ant-design/icons'; import MessageForm from './MessageForm'; import './App.css'; -const { Link } = Typography; +const { Text, Link } = Typography; interface ConsoleFooterProps { onConnect: () => Promise; @@ -22,6 +22,10 @@ interface ConsoleFooterProps { clientRef: React.MutableRefObject; } +const STORAGE_KEY = 'noiseSuppression'; + +const DISCONNECT_TIME = 1800; // 30 minutes + const ConsoleFooter: React.FC = ({ onConnect, onDisconnect, @@ -36,6 +40,11 @@ const ConsoleFooter: React.FC = ({ ); const [isAudioPlaybackDeviceTest, setIsAudioPlaybackDeviceTest] = useState(false); + const [noiseSuppression, setNoiseSuppression] = useState(() => { + const savedValue = localStorage.getItem(STORAGE_KEY); + return savedValue ? JSON.parse(savedValue) : []; + }); + const [connectLeftTime, setConnectLeftTime] = useState(DISCONNECT_TIME); const checkMicrophonePermission = () => { RealtimeUtils.checkPermission().then(isDeviceEnable => { @@ -50,11 +59,30 @@ const ConsoleFooter: React.FC = ({ useEffect(() => { checkMicrophonePermission(); }, []); + useEffect(() => { + if (isConnected && connectLeftTime > 0) { + const timer = setInterval(() => { + setConnectLeftTime(prev => { + if (prev <= 1) { + handleDisconnect(); + return 0; + } + return prev - 1; + }); + }, 1000); + return () => clearInterval(timer); + } + }, [isConnected, connectLeftTime]); const handleRefreshMicrophone = () => { checkMicrophonePermission(); }; + const handleNoiseSuppressionChange = (value: string[]) => { + setNoiseSuppression(value); + localStorage.setItem(STORAGE_KEY, JSON.stringify(value)); + }; + const handleConnect = () => { setIsConnecting(true); onConnect().finally(() => { @@ -62,9 +90,14 @@ const ConsoleFooter: React.FC = ({ }); }; + const handleDisconnect = () => { + setConnectLeftTime(DISCONNECT_TIME); + onDisconnect(); + }; + const handleInterrupt = async () => { if (!clientRef.current) { - message.error('Please set accessToken and botId first'); + message.error('Please click Settings to set configuration first'); return; } @@ -85,13 +118,13 @@ const ConsoleFooter: React.FC = ({ onToggleMicrophone(!isMicrophoneOn); message.success(`Microphone ${!isMicrophoneOn ? 'unmuted' : 'muted'}`); } else { - message.error('Please set accessToken and botId first'); + message.error('Please click Settings to set configuration first'); } }; const handleSendMessage = (values: { eventData: string }) => { if (!clientRef.current) { - message.error('Please set accessToken and botId first'); + message.error('Please click Settings to set configuration first'); return; } try { @@ -104,7 +137,7 @@ const ConsoleFooter: React.FC = ({ const handleEnableAudioPropertiesReport = () => { if (!clientRef?.current) { - message.error('Please set accessToken and botId first'); + message.error('Please click Settings to set configuration first'); return; } try { @@ -118,7 +151,7 @@ const ConsoleFooter: React.FC = ({ const handleAudioPlaybackDeviceTest = () => { if (!clientRef?.current) { - message.error('Please set accessToken and botId first'); + message.error('Please click Settings to set configuration first'); return; } if (isAudioPlaybackDeviceTest) { @@ -175,10 +208,11 @@ const ConsoleFooter: React.FC = ({
@@ -201,6 +235,17 @@ const ConsoleFooter: React.FC = ({ } return ( <> + Audio Noise Suppression: + + {workspaces.map(workspace => ( + + {workspace.label} + + ))} + +); + +const BotSelect: React.FC<{ + bots: BotOption[]; + loading: boolean; + disabled: boolean; + value?: string; + onChange?: (value: string) => void; +}> = ({ bots, loading, disabled, value, onChange }) => ( + +); const Settings: React.FC = ({ onSaveSettings }) => { const [form] = Form.useForm(); const [isModalVisible, setIsModalVisible] = useState(false); - const [voiceOptions, setVoiceOptions] = useState([]); - const [forceUpdate, setForceUpdate] = useState(0); + const [accessToken, setAccessToken] = useState(''); + const [isAccessTokenLoaded, setIsAccessTokenLoaded] = useState(false); + const [selectedWorkspace, setSelectedWorkspace] = useState(''); + const [voices, setVoices] = useState([]); + const [workspaces, setWorkspaces] = useState([]); + const [bots, setBots] = useState([]); + const [loadingWorkspaces, setLoadingWorkspaces] = useState(false); + const [loadingVoices, setLoadingVoices] = useState(false); + const [loadingBots, setLoadingBots] = useState(false); + const baseURL = localStorage.getItem('baseURL') || 'https://api.coze.cn'; - useEffect(() => { - // Load values from localStorage on component mount - const storedAccessToken = localStorage.getItem('accessToken'); - const storedBotId = localStorage.getItem('botId'); - const storedVoiceId = localStorage.getItem('voiceId'); - const storedBaseURL = localStorage.getItem('baseURL'); - form.setFieldsValue({ - accessToken: storedAccessToken || '', - botId: storedBotId || '', - voiceId: storedVoiceId || '', - baseURL: storedBaseURL || 'https://api.coze.cn', + const { api, fetchAllVoices, fetchAllBots, fetchAllWorkspaces, cloneVoice } = + useCozeAPI({ + accessToken, + baseURL, }); - }, [form]); - useEffect(() => { - // Fetch voice options when accessToken or baseURL changes - const fetchVoiceOptions = async () => { - const accessToken = form.getFieldValue('accessToken'); - const baseURL = form.getFieldValue('baseURL'); + const setToken = (token: OAuthToken) => { + localStorage.setItem('accessToken', token.access_token); + localStorage.setItem('refreshToken', token.refresh_token); + localStorage.setItem( + 'tokenExpiresAt', + (token.expires_in * 1000).toString(), + ); + setAccessToken(token.access_token); + }; - if (!accessToken || !baseURL) { - return; + const getOrRefreshToken = async (): Promise => { + const storedAccessToken = localStorage.getItem('accessToken'); + const storedRefreshToken = localStorage.getItem('refreshToken'); + const tokenExpiresAt = localStorage.getItem('tokenExpiresAt'); + const urlParams = new URLSearchParams(window.location.search); + const code = urlParams.get('code'); + const codeVerifier = localStorage.getItem('pkce_code_verifier'); + + const isTokenExpired = (expiresAt: string | null): boolean => { + if (!expiresAt) { + return true; } + // Add 5-minute buffer before expiration + return Date.now() >= parseInt(expiresAt) - 5 * 60 * 1000; + }; + + const isNeedRefreshToken = (): boolean => { + // token expired, need refresh token + if ( + storedRefreshToken && + tokenExpiresAt && + isTokenExpired(tokenExpiresAt) + ) { + console.log('token expired, need refresh token'); + return true; + } + if (storedRefreshToken && !storedAccessToken) { + console.log('no access token, and has refresh token, need refresh'); + return true; + } + return false; + }; + if (code && codeVerifier) { + // pkce flow + const token = await exchangeCodeForToken(code, codeVerifier); + if (token) { + console.log('pkce flow, set token', token); + setToken(token); + return token.access_token; + } + console.log('pkce flow failed, no token'); + return ''; + } else if (storedRefreshToken && isNeedRefreshToken()) { try { - const api = new CozeAPI({ - token: accessToken, + const refreshedToken = await refreshOAuthToken({ baseURL, - allowPersonalAccessTokenInBrowser: true, + clientId: DEFAULT_OAUTH_CLIENT_ID, + refreshToken: storedRefreshToken, + clientSecret: '', }); - const result = await api.audio.voices.list(); - setVoiceOptions( - result.voice_list.map((voice: Voice) => ({ - id: voice.voice_id, - name: voice.name, - })), + + console.log('refresh token'); + setToken(refreshedToken); + return refreshedToken.access_token; + } catch (err) { + console.error(err); + message.error(`Failed to refresh token: ${err}`); + // if contains `BadRequestError`, remove token + if (`${err}`.includes('BadRequestError')) { + setToken({ + access_token: '', + refresh_token: '', + expires_in: 0, + }); + } + console.log('refresh token failed'); + return ''; + } + } else if (storedAccessToken) { + console.log('has access token, return access token'); + return storedAccessToken; + } else { + console.log('no access token, return empty'); + return ''; + } + }; + + /* eslint-disable max-params */ + const loadData = async ( + objectName: string, + dataLocalKey: string, + setLoading: (loading: boolean) => void, + /* eslint-disable @typescript-eslint/no-explicit-any */ + setData: (data: any[]) => void, + /* eslint-disable @typescript-eslint/no-explicit-any */ + fetchData: (id?: string) => Promise, + parentId?: string, + ): Promise => { + setLoading(true); + try { + const data = await fetchData(parentId); + + // if no data in localStorage, set the first data as default + const storedData = localStorage.getItem(dataLocalKey); + if (!storedData && data.length > 0) { + const firstData = data[0].value; + localStorage.setItem(dataLocalKey, firstData); + form.setFieldsValue({ [dataLocalKey]: firstData }); + } + setData(data); + } catch (err) { + console.error(err); + message.error(`Failed to load ${objectName}: ${err}`); + // code: 4100, remove token + if (`${err}`.includes('code: 4100')) { + console.log(`remove token when load ${objectName} failed: ${err}`); + localStorage.removeItem('accessToken'); + } + } finally { + setLoading(false); + } + }; + + const handleWorkspaceChange = () => (workspaceId: string) => { + setSelectedWorkspace(workspaceId); + localStorage.setItem('workspaceId', workspaceId); + form.setFieldsValue({ botId: undefined }); + + loadData( + 'Bots', + 'botId', + setLoadingBots, + setBots, + fetchAllBots, + workspaceId, + ); + }; + + useEffect(() => { + (async () => { + const accessTokenObtained = await getOrRefreshToken(); + setAccessToken(accessTokenObtained); + setIsAccessTokenLoaded(true); + + if (accessTokenObtained) { + await loadData( + 'Voices', + 'voiceId', + setLoadingVoices, + setVoices, + fetchAllVoices, + ); + + await loadData( + 'Workspaces', + 'workspaceId', + setLoadingWorkspaces, + setWorkspaces, + fetchAllWorkspaces, ); - } catch (error) { - console.error('Failed to fetch voice options:', error); - message.error('Failed to load voice options'); + + const workspaceId = localStorage.getItem('workspaceId'); + if (workspaceId) { + await loadData( + 'Bots', + 'botId', + setLoadingBots, + setBots, + fetchAllBots, + workspaceId, + ); + } } - }; - fetchVoiceOptions(); - }, [form.getFieldValue('accessToken'), form.getFieldValue('baseURL')]); - const handleFormChange = () => { - setForceUpdate(forceUpdate + 1); + form.setFieldsValue({ + workspaceId: localStorage.getItem('workspaceId') || '', + botId: localStorage.getItem('botId') || '', + voiceId: localStorage.getItem('voiceId') || '', + baseURL: localStorage.getItem('baseURL') || 'https://api.coze.cn', + }); + })(); + }, [form, api]); + + const getCurrentLocation = () => + `${window.location.protocol}//${window.location.host}${window.location.pathname}`; + + const exchangeCodeForToken = async ( + code: string, + codeVerifier: string, + ): Promise => { + try { + const token = await getPKCEOAuthToken({ + code, + baseURL: localStorage.getItem('baseURL') || 'https://api.coze.cn', + clientId: DEFAULT_OAUTH_CLIENT_ID, + redirectUrl: getCurrentLocation(), + codeVerifier, + }); + + setToken(token); + + window.history.replaceState({}, document.title, window.location.pathname); + localStorage.removeItem('pkce_code_verifier'); + + message.success('Successfully obtained access token'); + return token; + } catch (error) { + message.error('Failed to exchange code for token'); + console.error(error); + return null; + } + }; + + const initPKCEFlow = async () => { + try { + const pkceAuth = await getPKCEAuthenticationUrl({ + baseURL: form.getFieldValue('baseURL') || 'https://api.coze.cn', + clientId: DEFAULT_OAUTH_CLIENT_ID, + redirectUrl: getCurrentLocation(), + }); + localStorage.setItem('pkce_code_verifier', pkceAuth.codeVerifier); + window.location.href = pkceAuth.url; + } catch (error) { + message.error('Failed to initialize OAuth flow'); + } }; const showModal = () => { @@ -96,16 +369,21 @@ const Settings: React.FC = ({ onSaveSettings }) => { form .validateFields() .then(values => { - const { accessToken, botId, voiceId, baseURL } = values; - if (!accessToken || !botId || !baseURL) { - message.error('Access Token, Bot ID, and Base URL are required!'); + const { workspaceId, botId, voiceId, baseURL: baseURLForm } = values; + if (baseURLForm) { + localStorage.setItem('baseURL', baseURLForm); + } + if (!accessToken || !workspaceId || !botId) { + message.error( + 'Access Token, Workspace, Bot, and Base URL are required!', + ); return; } - localStorage.setItem('accessToken', accessToken); + localStorage.setItem('workspaceId', workspaceId); localStorage.setItem('botId', botId); localStorage.setItem('voiceId', voiceId); - localStorage.setItem('baseURL', baseURL); + localStorage.setItem('baseURL', baseURLForm); onSaveSettings(); setIsModalVisible(false); @@ -122,15 +400,20 @@ const Settings: React.FC = ({ onSaveSettings }) => { const menu = ( + + access_token: {accessToken || 'Not set'} + + + - access_token: {localStorage.getItem('accessToken') || 'Not set'} + workspace_id: {localStorage.getItem('workspaceId') || 'Not set'} - + = ({ onSaveSettings }) => { bot_id: {localStorage.getItem('botId') || 'Not set'} - + = ({ onSaveSettings }) => { voice_id: {localStorage.getItem('voiceId') || 'Not set'} - + = ({ onSaveSettings }) => { <>
-
+
Logo = ({ onSaveSettings }) => { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', + minWidth: 0, }} > Coze Realtime Console
-
- +
+ @@ -205,31 +508,89 @@ const Settings: React.FC = ({ onSaveSettings }) => { -
+ - Only available for Volcano Engine Professional users with - whitelist access. + 仅支持已开通白名单的火山引擎专业版用户, - You can enable whitelist through Bot conversation. + 填写问卷申请开通使用 } > - + + + + + + + + + + + + + + + form.setFieldsValue({ botId: value })} + /> + + + + + form.setFieldsValue({ voiceId: value })} + cloneVoice={cloneVoice} + fetchAllVoices={fetchAllVoices} + /> = ({ onSaveSettings }) => { > - - - - - + + + + + + + + + + } + > + false} + accept=".wav,.mp3,.ogg,.m4a,.aac,.pcm" + onChange={info => { + if (info.file) { + form.setFieldsValue({ + audio: { + file: info.file, + fileList: [info.file], + }, + }); + } + }} + > + + + + + +
+
+ ); +}; + +const VoiceSelect: React.FC<{ + voices: VoiceOption[]; + loading: boolean; + value?: string; + onChange?: (value: string) => void; + fetchAllVoices?: () => Promise; + cloneVoice: (params: CloneVoiceReq) => Promise; +}> = ({ voices, loading, value, onChange, fetchAllVoices, cloneVoice }) => { + const [audioPlayer] = useState(new Audio()); + const [isPlaying, setIsPlaying] = useState(false); + const [cloneModalVisible, setCloneModalVisible] = useState(false); + const [selectedVoice, setSelectedVoice] = useState(); + + const handleAudioError = (error: Event | string) => { + message.error('Failed to play audio preview'); + console.error('Audio playback error:', error); + }; + + const handlePreview = (previewUrl: string, e: React.MouseEvent) => { + e.stopPropagation(); + if (audioPlayer.src === previewUrl) { + if (audioPlayer.paused) { + audioPlayer.play(); + setIsPlaying(true); + } else { + audioPlayer.pause(); + setIsPlaying(false); + } + } else { + audioPlayer.src = previewUrl; + audioPlayer.onerror = handleAudioError; + audioPlayer.play(); + setIsPlaying(true); + } + }; + + const handleClone = (voice: VoiceOption, e: React.MouseEvent) => { + e.stopPropagation(); + // Check if cloning is allowed + if ( + voice.is_system_voice === false && + voice.available_training_times <= 0 + ) { + message.error('This voice does not support cloning'); + return; + } + setSelectedVoice(voice); + setCloneModalVisible(true); + }; + + useEffect( + () => () => { + audioPlayer.pause(); + audioPlayer.onerror = null; + audioPlayer.src = ''; + setIsPlaying(false); + }, + [audioPlayer], + ); + + return ( + <> + {!voices.some(v => !v.is_system_voice) && ( + + )} + + { + await fetchAllVoices?.(); + setCloneModalVisible(false); + }} + voice={selectedVoice} + cloneVoice={cloneVoice} + /> + + ); +}; + +export default VoiceSelect; diff --git a/examples/realtime-console/src/use-coze-api.tsx b/examples/realtime-console/src/use-coze-api.tsx new file mode 100644 index 00000000..b2682438 --- /dev/null +++ b/examples/realtime-console/src/use-coze-api.tsx @@ -0,0 +1,187 @@ +import { useEffect, useState } from 'react'; + +import { CozeAPI, type CloneVoiceReq } from '@coze/api'; + +export interface VoiceOption { + label: React.ReactNode; + value: string; + preview_url?: string; + name: string; + language_code: string; + language_name: string; + is_system_voice: boolean; + available_training_times: number; + preview_text: string; +} + +export interface BotOption { + value: string; + label: string; + avatar?: string; +} + +export interface WorkspaceOption { + value: string; + label: string; +} + +const useCozeAPI = ({ + accessToken, + baseURL, +}: { + accessToken: string; + baseURL: string; +}) => { + const [api, setApi] = useState(null); + + useEffect(() => { + if (accessToken && baseURL) { + setApi( + new CozeAPI({ + token: accessToken, + baseURL, + allowPersonalAccessTokenInBrowser: true, + }), + ); + } + }, [accessToken, baseURL]); + + const fetchAllBots = async (workspaceId?: string): Promise => { + let pageIndex = 1; + const pageSize = 20; + let allBots: BotOption[] = []; + let hasMore = true; + if (!workspaceId) { + throw new Error('workspaceId is required'); + } + + try { + while (hasMore) { + const response = await api?.bots.list({ + space_id: workspaceId, + page_size: pageSize, + page_index: pageIndex, + }); + + const bots: BotOption[] = + response?.space_bots.map(bot => ({ + value: bot.bot_id, + label: bot.bot_name, + avatar: bot.icon_url, + })) || []; + + allBots = [...allBots, ...bots]; + console.log(`load ${pageIndex} page bots`, bots); + + hasMore = response?.space_bots.length === pageSize; + pageIndex++; + } + + return allBots; + } catch (error) { + console.error('get bots error:', error); + throw error; + } + }; + + const fetchAllVoices = async (): Promise => { + try { + const response = await api?.audio.voices.list(); + + // Separate system voices and custom voices + const customVoices = + response?.voice_list.filter(voice => !voice.is_system_voice) || []; + const systemVoices = + response?.voice_list.filter(voice => voice.is_system_voice) || []; + + // Group system voices by language + const systemVoicesByLanguage = systemVoices.reduce< + Record + >((acc, voice) => { + const languageName = voice.language_name; + if (!acc[languageName]) { + acc[languageName] = []; + } + acc[languageName].push(voice); + return acc; + }, {}); + + // Sort languages alphabetically and flatten voices + const sortedSystemVoices = Object.entries(systemVoicesByLanguage) + .sort(([langA], [langB]) => langB.localeCompare(langA)) + .flatMap(([, voices]) => voices); + + // Merge custom voices with sorted system voices and format + const formattedVoices = [...customVoices, ...sortedSystemVoices].map( + voice => ({ + value: voice.voice_id, + preview_url: voice.preview_audio, + name: voice.name, + language_code: voice.language_code, + language_name: voice.language_name, + is_system_voice: voice.is_system_voice, + label: `${voice.name} (${voice.language_name})`, + preview_text: voice.preview_text, + available_training_times: voice.available_training_times, + }), + ); + + return formattedVoices; + } catch (error) { + console.error('get voices error:', error); + throw error; + } + }; + + const fetchAllWorkspaces = async (): Promise => { + let pageNum = 1; + const pageSize = 50; + let allWorkspaces: WorkspaceOption[] = []; + let hasMore = true; + + try { + while (hasMore) { + const response = await api?.workspaces.list({ + page_size: pageSize, + page_num: pageNum, + }); + + const workspaces = + response?.workspaces.map(workspace => ({ + value: workspace.id, + label: workspace.name, + })) || []; + + allWorkspaces = [...allWorkspaces, ...workspaces]; + + hasMore = response?.workspaces.length === pageSize; + pageNum++; + } + + return allWorkspaces; + } catch (error) { + console.error('get workspaces error:', error); + throw error; + } + }; + + const cloneVoice = async (params: CloneVoiceReq): Promise => { + try { + const response = await api?.audio.voices.clone(params); + return response?.voice_id || ''; + } catch (error) { + console.error('clone voice error:', error); + throw error; + } + }; + + return { + api, + fetchAllVoices, + fetchAllBots, + fetchAllWorkspaces, + cloneVoice, + }; +}; + +export default useCozeAPI; diff --git a/packages/coze-js/src/resources/workspaces/workspaces.ts b/packages/coze-js/src/resources/workspaces/workspaces.ts index 2b7872d6..68aa4a2a 100644 --- a/packages/coze-js/src/resources/workspaces/workspaces.ts +++ b/packages/coze-js/src/resources/workspaces/workspaces.ts @@ -12,7 +12,10 @@ export class WorkSpaces extends APIResource { * @param params.page_size - Optional The size of pagination. Default is 10. Maximum is 50. | 可选 分页大小。默认为 10,最大为 50。 * @returns OpenSpaceData | 工作空间列表 */ - async list(params?: ListWorkSpacesReq, options?: RequestOptions) { + async list( + params?: ListWorkSpacesReq, + options?: RequestOptions, + ): Promise { const apiUrl = '/v1/workspaces'; const response = await this._client.get( apiUrl, @@ -20,8 +23,7 @@ export class WorkSpaces extends APIResource { false, options, ); - // TODO The returned type is not in JSON format - return safeJsonParse(response).data as OpenSpaceData; + return safeJsonParse(response, response).data; } } diff --git a/packages/coze-js/test/resources/workspaces.spec.ts b/packages/coze-js/test/resources/workspaces.spec.ts index 162ea5c0..9f249707 100644 --- a/packages/coze-js/test/resources/workspaces.spec.ts +++ b/packages/coze-js/test/resources/workspaces.spec.ts @@ -61,7 +61,6 @@ describe('Workspaces', () => { false, undefined, ); - expect(utils.safeJsonParse).toHaveBeenCalledWith(mockResponse); expect(result).toEqual(mockOpenSpaceData); }); @@ -86,13 +85,5 @@ describe('Workspaces', () => { undefined, ); }); - - it('should handle errors from safeJsonParse', async () => { - const mockResponse = 'Invalid JSON'; - jest.spyOn(client, 'get').mockResolvedValue(mockResponse); - (utils.safeJsonParse as jest.Mock).mockReturnValue(null); - - await expect(workspaces.list({})).rejects.toThrow(); - }); }); }); diff --git a/packages/realtime-api/README.md b/packages/realtime-api/README.md index d2e730bd..152411b6 100644 --- a/packages/realtime-api/README.md +++ b/packages/realtime-api/README.md @@ -45,6 +45,8 @@ const client = new RealtimeClient({ debug: true, // Optional: Enable debug logging allowPersonalAccessTokenInBrowser: true, // Optional: Enable PAT token usage in browser audioMutedDefault: false, // Optional: Initial audio state (default: false) + suppressStationaryNoise: false, // Optional: Enable stationary noise suppression(default: false) + suppressNonStationaryNoise: false, // Optional: Enable non-stationary noise suppression(default: false) }); // Essential Setup diff --git a/packages/realtime-api/src/client.ts b/packages/realtime-api/src/client.ts index 2658b1c9..08ecb03b 100644 --- a/packages/realtime-api/src/client.ts +++ b/packages/realtime-api/src/client.ts @@ -1,3 +1,4 @@ +import RTCAIAnsExtension from '@volcengine/rtc/extension-ainr'; import VERTC, { type AudioPropertiesConfig, type IRTCEngine, @@ -13,6 +14,7 @@ import { RealtimeAPIError, RealtimeError } from './error'; export class EngineClient extends RealtimeEventHandler { private engine: IRTCEngine; private joinUserId = ''; + private _AIAnsExtension: RTCAIAnsExtension | null = null; constructor(appId: string, debug = false, isTestEnv = false) { super(debug); @@ -21,6 +23,7 @@ export class EngineClient extends RealtimeEventHandler { } this.engine = VERTC.createEngine(appId); + this.handleMessage = this.handleMessage.bind(this); this.handleUserJoin = this.handleUserJoin.bind(this); this.handleUserLeave = this.handleUserLeave.bind(this); @@ -218,6 +221,28 @@ export class EngineClient extends RealtimeEventHandler { } } + async enableAudioNoiseReduction() { + await this.engine?.setAudioCaptureConfig({ + noiseSuppression: true, + echoCancellation: true, + autoGainControl: true, + }); + } + + async initAIAnsExtension() { + const AIAnsExtension = new RTCAIAnsExtension(); + await this.engine.registerExtension(AIAnsExtension); + this._AIAnsExtension = AIAnsExtension; + } + + changeAIAnsExtension(enable: boolean) { + if (enable) { + this._AIAnsExtension?.enable(); + } else { + this._AIAnsExtension?.disable(); + } + } + async startAudioPlaybackDeviceTest() { try { await this.engine.startAudioPlaybackDeviceTest('audio-test.wav', 200); diff --git a/packages/realtime-api/src/event-handler.ts b/packages/realtime-api/src/event-handler.ts index 4a070b43..a3e18626 100644 --- a/packages/realtime-api/src/event-handler.ts +++ b/packages/realtime-api/src/event-handler.ts @@ -46,6 +46,16 @@ export enum EventNames { * zh: 客户端错误 */ ERROR = 'client.error', + /** + * en: Audio noise reduction enabled + * zh: 抑制平稳噪声 + */ + SUPPRESS_STATIONARY_NOISE = 'client.suppress.stationary.noise', + /** + * en: Suppress non-stationary noise + * zh: 抑制非平稳噪声 + */ + SUPPRESS_NON_STATIONARY_NOISE = 'client.suppress.non.stationary.noise', } type EventCallback = (eventName: string, event: unknown) => void; diff --git a/packages/realtime-api/src/index.ts b/packages/realtime-api/src/index.ts index d41c88f2..13b188cd 100644 --- a/packages/realtime-api/src/index.ts +++ b/packages/realtime-api/src/index.ts @@ -18,7 +18,9 @@ export interface RealtimeClientConfig { /** Whether to mute by default, defaults to false * If set to true, audio streams will not be automatically published and subscribed */ audioMutedDefault?: boolean; - connectorId?: string /** optional, Connector Id, defaults to '999' */; + connectorId: string /** required, Connector Id */; + suppressStationaryNoise?: boolean /** optional, Suppress stationary noise, defaults to false */; + suppressNonStationaryNoise?: boolean /** optional, Suppress non-stationary noise, defaults to false */; } class RealtimeClient extends RealtimeEventHandler { @@ -52,6 +54,12 @@ class RealtimeClient extends RealtimeEventHandler { * 可选,是否允许在浏览器环境中使用个人访问令牌。 * @param config.audioMutedDefault - Optional, whether audio is muted by default, defaults to false. | * 可选,默认是否静音,默认值为 false。 + * @param config.connectorId - Required, Connector Id. | + * 必填,渠道 Id。 + * @param config.suppressStationaryNoise - Optional, suppress stationary noise, defaults to false. | + * 可选,默认是否抑制静态噪声,默认值为 false。 + * @param config.suppressNonStationaryNoise - Optional, suppress non-stationary noise, defaults to false. | + * 可选,默认是否抑制非静态噪声,默认值为 false。 */ constructor(config: RealtimeClientConfig) { super(config.debug); @@ -84,7 +92,7 @@ class RealtimeClient extends RealtimeEventHandler { bot_id: botId, conversation_id: conversationId, voice_id: voiceId && voiceId.length > 0 ? voiceId : undefined, - connector_id: this._config.connectorId ?? '999', + connector_id: this._config.connectorId, }); } catch (error) { this.dispatch(EventNames.ERROR, error); @@ -109,6 +117,17 @@ class RealtimeClient extends RealtimeEventHandler { this.dispatch(eventName, data); }); + if (this._config.suppressStationaryNoise) { + await this._client.enableAudioNoiseReduction(); + this.dispatch(EventNames.SUPPRESS_STATIONARY_NOISE, {}); + } + + if (this._config.suppressNonStationaryNoise) { + await this._client.initAIAnsExtension(); + this._client.changeAIAnsExtension(true); + this.dispatch(EventNames.SUPPRESS_NON_STATIONARY_NOISE, {}); + } + // Step4 join room await this._client.joinRoom({ token: roomInfo.token, diff --git a/packages/realtime-api/test/client.spec.ts b/packages/realtime-api/test/client.spec.ts index 4083a6a0..cc03cd4b 100644 --- a/packages/realtime-api/test/client.spec.ts +++ b/packages/realtime-api/test/client.spec.ts @@ -4,6 +4,12 @@ import { RealtimeAPIError } from '../src/error'; import { EngineClient } from '../src/client'; jest.mock('@volcengine/rtc'); +jest.mock('@volcengine/rtc/extension-ainr', () => + jest.fn().mockImplementation(() => ({ + enable: jest.fn(), + disable: jest.fn(), + })), +); describe('EngineClient', () => { let client: EngineClient; @@ -23,6 +29,9 @@ describe('EngineClient', () => { enableAudioPropertiesReport: jest.fn(), startAudioPlaybackDeviceTest: jest.fn(), stopAudioPlaybackDeviceTest: jest.fn(), + setAudioCaptureConfig: jest.fn(), + registerExtension: jest.fn(), + sendUserMessage: jest.fn(), }; (VERTC.createEngine as jest.Mock).mockReturnValue(mockEngine); (VERTC.enumerateDevices as jest.Mock).mockResolvedValue([ @@ -124,9 +133,73 @@ describe('EngineClient', () => { }); describe('stop', () => { - it('should call engine stop', async () => { - // await client.stop('userId'); - // expect(mockEngine.stop).toHaveBeenCalledWith('userId', MediaType.AUDIO); + it('should send stop message to joined user', async () => { + mockEngine.sendUserMessage.mockResolvedValue('success'); + await client.stop(); + expect(mockEngine.sendUserMessage).toHaveBeenCalledWith( + '', // joinUserId is empty in test + JSON.stringify({ + id: 'event_1', + event_type: 'conversation.chat.cancel', + data: {}, + }), + ); + }); + + it('should handle errors when stopping', async () => { + mockEngine.sendUserMessage.mockRejectedValue(new Error('Failed to send')); + await expect(client.stop()).rejects.toThrow(Error); + }); + }); + + describe('sendMessage', () => { + it('should send message to joined user', async () => { + const message = { type: 'test', data: {} }; + mockEngine.sendUserMessage.mockResolvedValue('success'); + await client.sendMessage(message); + expect(mockEngine.sendUserMessage).toHaveBeenCalledWith( + '', // joinUserId is empty in test + JSON.stringify(message), + ); + }); + + it('should handle errors when sending message', async () => { + mockEngine.sendUserMessage.mockRejectedValue(new Error('Failed to send')); + await expect(client.sendMessage({})).rejects.toThrow(Error); + }); + }); + + describe('enableAudioNoiseReduction', () => { + it('should set audio capture config with noise reduction enabled', async () => { + await client.enableAudioNoiseReduction(); + expect(mockEngine.setAudioCaptureConfig).toHaveBeenCalledWith({ + noiseSuppression: true, + echoCancellation: true, + autoGainControl: true, + }); + }); + }); + + describe('initAIAnsExtension', () => { + it('should register AI extension', async () => { + await client.initAIAnsExtension(); + expect(mockEngine.registerExtension).toHaveBeenCalled(); + }); + }); + + describe('changeAIAnsExtension', () => { + it('should enable AI extension when true', async () => { + await client.initAIAnsExtension(); + client.changeAIAnsExtension(true); + // Note: We can't directly test the extension enable/disable calls + // as they are internal to the extension instance + }); + + it('should disable AI extension when false', async () => { + await client.initAIAnsExtension(); + client.changeAIAnsExtension(false); + // Note: We can't directly test the extension enable/disable calls + // as they are internal to the extension instance }); }); diff --git a/packages/realtime-api/test/index.spec.ts b/packages/realtime-api/test/index.spec.ts index b26eb123..02baad90 100644 --- a/packages/realtime-api/test/index.spec.ts +++ b/packages/realtime-api/test/index.spec.ts @@ -6,6 +6,12 @@ import { import { EngineClient } from '../src/client.js'; jest.mock('../src/client.js'); +jest.mock('@volcengine/rtc/extension-ainr', () => + jest.fn().mockImplementation(() => ({ + enable: jest.fn(), + disable: jest.fn(), + })), +); jest.mock('@coze/api', () => ({ CozeAPI: jest.fn().mockImplementation(() => ({ @@ -53,6 +59,9 @@ describe('RealtimeClient', () => { createLocalStream: jest.fn(), bindEngineEvents: jest.fn(), on: jest.fn(), + enableAudioNoiseReduction: jest.fn(), + initAIAnsExtension: jest.fn(), + changeAIAnsExtension: jest.fn(), } as any; (EngineClient as jest.Mock).mockImplementation(() => mockEngineClient); }); @@ -76,6 +85,31 @@ describe('RealtimeClient', () => { expect.any(Object), ); }); + + it('should connect successfully with noise suppression', async () => { + const configWithNoise = { + ...config, + suppressStationaryNoise: true, + suppressNonStationaryNoise: true, + }; + client = new RealtimeClient(configWithNoise); + const dispatchSpy = jest.spyOn(client, 'dispatch'); + + await client.connect(); + + expect(mockEngineClient.enableAudioNoiseReduction).toHaveBeenCalled(); + expect(mockEngineClient.initAIAnsExtension).toHaveBeenCalled(); + expect(mockEngineClient.changeAIAnsExtension).toHaveBeenCalledWith(true); + expect(dispatchSpy).toHaveBeenCalledWith( + EventNames.SUPPRESS_STATIONARY_NOISE, + {}, + ); + expect(dispatchSpy).toHaveBeenCalledWith( + EventNames.SUPPRESS_NON_STATIONARY_NOISE, + {}, + ); + // ... existing expectations ... + }); }); describe('interrupt', () => {