diff --git a/evi/evi-typescript-quickstart/.env.example b/evi/evi-typescript-quickstart/.env.example index 20909bab..9e638ac5 100644 --- a/evi/evi-typescript-quickstart/.env.example +++ b/evi/evi-typescript-quickstart/.env.example @@ -1,3 +1,2 @@ VITE_HUME_API_KEY="" -VITE_HUME_SECRET_KEY="" VITE_HUME_CONFIG_ID="" \ No newline at end of file diff --git a/evi/evi-typescript-quickstart/README.md b/evi/evi-typescript-quickstart/README.md index 2c2d4ced..8a5b1fd8 100644 --- a/evi/evi-typescript-quickstart/README.md +++ b/evi/evi-typescript-quickstart/README.md @@ -47,7 +47,6 @@ Note the `VITE` prefix to the environment variables. This prefix is required for ```sh VITE_HUME_API_KEY="" -VITE_HUME_SECRET_KEY="" VITE_HUME_CONFIG_ID="" // optional ``` diff --git a/evi/evi-typescript-quickstart/package.json b/evi/evi-typescript-quickstart/package.json index cc344f91..9935df96 100644 --- a/evi/evi-typescript-quickstart/package.json +++ b/evi/evi-typescript-quickstart/package.json @@ -9,7 +9,7 @@ "preview": "vite preview" }, "dependencies": { - "hume": "^0.9.6" + "hume": "^0.10.0" }, "devDependencies": { "typescript": "^5.2.2", diff --git a/evi/evi-typescript-quickstart/pnpm-lock.yaml b/evi/evi-typescript-quickstart/pnpm-lock.yaml index d09126be..b4224393 100644 --- a/evi/evi-typescript-quickstart/pnpm-lock.yaml +++ b/evi/evi-typescript-quickstart/pnpm-lock.yaml @@ -4,298 +4,332 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - hume: - specifier: ^0.9.6 - version: 0.9.6 +importers: .: dependencies: hume: - specifier: ^0.9.6 - version: 0.9.6 + specifier: ^0.10.0 + version: 0.10.0 devDependencies: typescript: specifier: ^5.2.2 - version: 5.3.3 + version: 5.8.3 vite: specifier: ^5.1.4 - version: 5.1.4 + version: 5.4.18 packages: - '@esbuild/aix-ppc64@0.19.12': - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.19.12': - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.19.12': - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.19.12': - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.19.12': - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.19.12': - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.19.12': - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.19.12': - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.19.12': - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.19.12': - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.19.12': - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.19.12': - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.19.12': - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.19.12': - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.19.12': - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.19.12': - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.19.12': - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.19.12': - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.19.12': - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.19.12': - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.19.12': - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.19.12': - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.19.12': - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - '@rollup/rollup-android-arm-eabi@4.12.0': - resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==} + '@rollup/rollup-android-arm-eabi@4.39.0': + resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.12.0': - resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==} + '@rollup/rollup-android-arm64@4.39.0': + resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.12.0': - resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==} + '@rollup/rollup-darwin-arm64@4.39.0': + resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.12.0': - resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==} + '@rollup/rollup-darwin-x64@4.39.0': + resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.12.0': - resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==} + '@rollup/rollup-freebsd-arm64@4.39.0': + resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.39.0': + resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.12.0': - resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==} + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.39.0': + resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.12.0': - resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==} + '@rollup/rollup-linux-arm64-musl@4.39.0': + resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.12.0': - resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==} + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.12.0': - resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==} + '@rollup/rollup-linux-riscv64-musl@4.39.0': + resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.39.0': + resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.39.0': + resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.12.0': - resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==} + '@rollup/rollup-linux-x64-musl@4.39.0': + resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.12.0': - resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==} + '@rollup/rollup-win32-arm64-msvc@4.39.0': + resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.12.0': - resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==} + '@rollup/rollup-win32-ia32-msvc@4.39.0': + resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.12.0': - resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==} + '@rollup/rollup-win32-x64-msvc@4.39.0': + resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} cpu: [x64] os: [win32] - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - /abort-controller@3.0.0: + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - dependencies: - event-target-shim: 5.0.1 - dev: false - /asynckit@0.4.0: + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - /base64-js@1.5.1: + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: false - /buffer@6.0.3: + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: false - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true - /event-target-shim@5.0.1: + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - dev: false - /events@3.3.0: + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - dev: false - /form-data-encoder@4.0.2: + form-data-encoder@4.0.2: resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==} engines: {node: '>= 18'} - form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} formdata-node@6.0.3: @@ -310,52 +344,41 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - /hume@0.9.6: - resolution: {integrity: sha512-7x24HgWnveMCYotzMdlU1aVmqq3OdMo7Ja/ATZk2wSrcpyCVqdfPIPDZfm5TJ0iG6/MqAUz4EB+WJfuhUfkZ6A==} - dependencies: - form-data: 4.0.0 - form-data-encoder: 4.0.2 - formdata-node: 6.0.3 - node-fetch: 2.7.0 - qs: 6.11.2 - readable-stream: 4.5.2 - url-join: 4.0.1 - uuid: 9.0.1 - ws: 8.17.0 - zod: 3.23.8 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - dev: false + hume@0.10.0: + resolution: {integrity: sha512-rjarPoQylEIJ1lqWginPgyj0yCxBhZevyFdAarkQkgedmIBdLwW/jyYMHvFzzipALzt1NL5P0D3qmqXgPPgEmg==} - /ieee754@1.2.1: + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: false - /mime-db@1.52.0: + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -363,8 +386,8 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -377,80 +400,77 @@ packages: encoding: optional: true - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - postcss@8.4.35: - resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} - /process@0.11.10: + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - dev: false - /qs@6.11.2: - resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - /readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - dev: false - /rollup@4.12.0: - resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==} + rollup@4.39.0: + resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - /safe-buffer@5.2.1: + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} engines: {node: '>= 0.4'} - source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - /string_decoder@1.3.0: + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - dev: false - /tr46@0.0.3: + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} - /uuid@9.0.1: + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - vite@5.1.4: - resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} + vite@5.4.18: + resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -458,6 +478,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -470,6 +491,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -483,8 +506,8 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -495,120 +518,141 @@ packages: utf-8-validate: optional: true - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.24.2: + resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} snapshots: - '@esbuild/aix-ppc64@0.19.12': + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.19.12': + '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.19.12': + '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.19.12': + '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.19.12': + '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.19.12': + '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.19.12': + '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.19.12': + '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.19.12': + '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.19.12': + '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.19.12': + '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.19.12': + '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.19.12': + '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.19.12': + '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.19.12': + '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.19.12': + '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.19.12': + '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.19.12': + '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.19.12': + '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.19.12': + '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.19.12': + '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.19.12': + '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.19.12': + '@rollup/rollup-android-arm-eabi@4.39.0': optional: true - '@rollup/rollup-android-arm-eabi@4.12.0': + '@rollup/rollup-android-arm64@4.39.0': optional: true - '@rollup/rollup-android-arm64@4.12.0': + '@rollup/rollup-darwin-arm64@4.39.0': optional: true - '@rollup/rollup-darwin-arm64@4.12.0': + '@rollup/rollup-darwin-x64@4.39.0': optional: true - '@rollup/rollup-darwin-x64@4.12.0': + '@rollup/rollup-freebsd-arm64@4.39.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.12.0': + '@rollup/rollup-freebsd-x64@4.39.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.12.0': + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.12.0': + '@rollup/rollup-linux-arm-musleabihf@4.39.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.12.0': + '@rollup/rollup-linux-arm64-gnu@4.39.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.12.0': + '@rollup/rollup-linux-arm64-musl@4.39.0': optional: true - '@rollup/rollup-linux-x64-musl@4.12.0': + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.12.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.12.0': + '@rollup/rollup-linux-riscv64-gnu@4.39.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.12.0': + '@rollup/rollup-linux-riscv64-musl@4.39.0': optional: true - '@types/estree@1.0.5': {} + '@rollup/rollup-linux-s390x-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.39.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.39.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.39.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.39.0': + optional: true + + '@types/estree@1.0.7': {} abort-controller@3.0.0: dependencies: @@ -623,57 +667,68 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - call-bind@1.0.7: + call-bind-apply-helpers@1.0.2: dependencies: - es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - define-data-property@1.1.4: + delayed-stream@1.0.0: {} + + dunder-proto@1.0.1: dependencies: - es-define-property: 1.0.0 + call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 - delayed-stream@1.0.0: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} - es-define-property@1.0.0: + es-object-atoms@1.1.1: dependencies: - get-intrinsic: 1.2.4 + es-errors: 1.3.0 - es-errors@1.3.0: {} + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 - esbuild@0.19.12: + esbuild@0.21.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 event-target-shim@5.0.1: {} @@ -681,10 +736,11 @@ snapshots: form-data-encoder@4.0.2: {} - form-data@4.0.0: + form-data@4.0.2: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 mime-types: 2.1.35 formdata-node@6.0.3: {} @@ -694,42 +750,48 @@ snapshots: function-bind@1.1.2: {} - get-intrinsic@1.2.4: + get-intrinsic@1.3.0: dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 es-errors: 1.3.0 + es-object-atoms: 1.1.1 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 + math-intrinsics: 1.1.0 - gopd@1.0.1: + get-proto@1.0.1: dependencies: - get-intrinsic: 1.2.4 + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 + gopd@1.2.0: {} - has-proto@1.0.3: {} + has-symbols@1.1.0: {} - has-symbols@1.0.3: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 hasown@2.0.2: dependencies: function-bind: 1.1.2 - hume@0.9.6: + hume@0.10.0: dependencies: - form-data: 4.0.0 + form-data: 4.0.2 form-data-encoder: 4.0.2 formdata-node: 6.0.3 node-fetch: 2.7.0 - qs: 6.11.2 - readable-stream: 4.5.2 + qs: 6.14.0 + readable-stream: 4.7.0 url-join: 4.0.1 uuid: 9.0.1 - ws: 8.17.0 - zod: 3.23.8 + ws: 8.18.1 + zod: 3.24.2 transitivePeerDependencies: - bufferutil - encoding @@ -737,35 +799,37 @@ snapshots: ieee754@1.2.1: {} + math-intrinsics@1.1.0: {} + mime-db@1.52.0: {} mime-types@2.1.35: dependencies: mime-db: 1.52.0 - nanoid@3.3.7: {} + nanoid@3.3.11: {} node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - object-inspect@1.13.1: {} + object-inspect@1.13.4: {} - picocolors@1.0.0: {} + picocolors@1.1.1: {} - postcss@8.4.35: + postcss@8.5.3: dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 process@0.11.10: {} - qs@6.11.2: + qs@6.14.0: dependencies: - side-channel: 1.0.6 + side-channel: 1.1.0 - readable-stream@4.5.2: + readable-stream@4.7.0: dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -773,44 +837,63 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - rollup@4.12.0: + rollup@4.39.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.12.0 - '@rollup/rollup-android-arm64': 4.12.0 - '@rollup/rollup-darwin-arm64': 4.12.0 - '@rollup/rollup-darwin-x64': 4.12.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.12.0 - '@rollup/rollup-linux-arm64-gnu': 4.12.0 - '@rollup/rollup-linux-arm64-musl': 4.12.0 - '@rollup/rollup-linux-riscv64-gnu': 4.12.0 - '@rollup/rollup-linux-x64-gnu': 4.12.0 - '@rollup/rollup-linux-x64-musl': 4.12.0 - '@rollup/rollup-win32-arm64-msvc': 4.12.0 - '@rollup/rollup-win32-ia32-msvc': 4.12.0 - '@rollup/rollup-win32-x64-msvc': 4.12.0 + '@rollup/rollup-android-arm-eabi': 4.39.0 + '@rollup/rollup-android-arm64': 4.39.0 + '@rollup/rollup-darwin-arm64': 4.39.0 + '@rollup/rollup-darwin-x64': 4.39.0 + '@rollup/rollup-freebsd-arm64': 4.39.0 + '@rollup/rollup-freebsd-x64': 4.39.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.39.0 + '@rollup/rollup-linux-arm-musleabihf': 4.39.0 + '@rollup/rollup-linux-arm64-gnu': 4.39.0 + '@rollup/rollup-linux-arm64-musl': 4.39.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.39.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-musl': 4.39.0 + '@rollup/rollup-linux-s390x-gnu': 4.39.0 + '@rollup/rollup-linux-x64-gnu': 4.39.0 + '@rollup/rollup-linux-x64-musl': 4.39.0 + '@rollup/rollup-win32-arm64-msvc': 4.39.0 + '@rollup/rollup-win32-ia32-msvc': 4.39.0 + '@rollup/rollup-win32-x64-msvc': 4.39.0 fsevents: 2.3.3 safe-buffer@5.2.1: {} - set-function-length@1.2.2: + side-channel-list@1.0.0: dependencies: - define-data-property: 1.1.4 es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 - side-channel@1.0.6: + side-channel@1.1.0: dependencies: - call-bind: 1.0.7 es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 - source-map-js@1.0.2: {} + source-map-js@1.2.1: {} string_decoder@1.3.0: dependencies: @@ -818,17 +901,17 @@ snapshots: tr46@0.0.3: {} - typescript@5.3.3: {} + typescript@5.8.3: {} url-join@4.0.1: {} uuid@9.0.1: {} - vite@5.1.4: + vite@5.4.18: dependencies: - esbuild: 0.19.12 - postcss: 8.4.35 - rollup: 4.12.0 + esbuild: 0.21.5 + postcss: 8.5.3 + rollup: 4.39.0 optionalDependencies: fsevents: 2.3.3 @@ -839,6 +922,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - ws@8.17.0: {} + ws@8.18.1: {} - zod@3.23.8: {} + zod@3.24.2: {} diff --git a/evi/evi-typescript-quickstart/src/main.ts b/evi/evi-typescript-quickstart/src/main.ts index cea61087..2b30b745 100644 --- a/evi/evi-typescript-quickstart/src/main.ts +++ b/evi/evi-typescript-quickstart/src/main.ts @@ -8,8 +8,13 @@ import { getBrowserSupportedMimeType, MimeType, } from 'hume'; +import { CloseEvent as HumeCloseEvent } from 'hume/core/websocket/events'; import './styles.css'; +const RECONNECT_DELAY_MS = 3000; // Delay before attempting reconnection after unexpected closure +const TIME_SLICE_MS = 50; // Audio buffer duration for streamed audio +const ABNORMAL_CLOSE_CODES = new Set([1006, 1011, 1012, 1013, 1014]); // WebSocket close codes for abnormal closures + (async () => { const startBtn = document.querySelector('button#start-btn'); const stopBtn = document.querySelector('button#stop-btn'); @@ -18,351 +23,315 @@ import './styles.css'; startBtn?.addEventListener('click', connect); stopBtn?.addEventListener('click', disconnect); - /** - * the Hume Client, includes methods for connecting to EVI and managing the Web Socket connection - */ - let client: HumeClient | null = null; + toggleBtnStates(false); - /** - * the WebSocket instance - */ + /**--- Hume SDK and State ---*/ + let client: HumeClient | null = null; let socket: Hume.empathicVoice.chat.ChatSocket | null = null; + /** ID for resuming chats across connections (if shouldResumeChats is true). */ + let chatGroupId: string | undefined; + /** Set to true to reuse the same chat context after reconnecting. */ + const shouldResumeChats = true; + /** Flag indicating if the connection *should* be active (user initiated). Used for reconnection logic. */ + let shouldBeConnected = false; - /** - * flag which denotes the intended state of the WebSocket - */ - let connected = false; - - /** - * the recorder responsible for recording the audio stream to be prepared as the audio input - */ + /**--- Audio Recording State ---*/ let recorder: MediaRecorder | null = null; - - /** - * the stream of audio captured from the user's microphone - */ let audioStream: MediaStream | null = null; + const mimeTypeResult = getBrowserSupportedMimeType(); + const mimeType: MimeType = mimeTypeResult.success ? mimeTypeResult.mimeType : MimeType.WEBM; - /** - * the current audio element to be played - */ + /**--- Audio Playback State ---*/ + const audioQueue: Blob[] = []; let currentAudio: HTMLAudioElement | null = null; - - /** - * flag which denotes whether audio is currently playing or not - */ let isPlaying = false; - /** - * flag which denotes whether to utilize chat resumability (preserve context from one chat to the next) - */ - let resumeChats = true; - - /** - * The ChatGroup ID used to resume the chat if disconnected unexpectedly - */ - let chatGroupId: string | undefined; - - /** - * audio playback queue - */ - const audioQueue: Blob[] = []; + /**--- Reconnection State ---*/ + let reconnectTimeoutId: ReturnType | null = null; - /** - * mime type supported by the browser the application is running in - */ - const mimeType: MimeType = (() => { - const result = getBrowserSupportedMimeType(); - return result.success ? result.mimeType : MimeType.WEBM; - })(); - - /** - * instantiates interface config and client, sets up Web Socket handlers, and establishes secure Web Socket connection - */ - async function connect(): Promise { - // instantiate the HumeClient with credentials to make authenticated requests - if (!client) { - client = new HumeClient({ - apiKey: import.meta.env.VITE_HUME_API_KEY || '', - secretKey: import.meta.env.VITE_HUME_SECRET_KEY || '', - }); + /** Establishes a connection to the Hume EVI WebSocket API. */ + function connect() { + if (isConnectingOrConnected()) { + console.log("Already connecting or connected."); + return; } - // instantiates WebSocket and establishes an authenticated connection - socket = client.empathicVoice.chat.connect({ - configId: import.meta.env.VITE_HUME_CONFIG_ID || null, - resumedChatGroupId: chatGroupId, - }); - - socket.on('open', handleWebSocketOpenEvent); - socket.on('message', handleWebSocketMessageEvent); - socket.on('error', handleWebSocketErrorEvent); - socket.on('close', handleWebSocketCloseEvent); - - // update ui state - toggleBtnStates(); + clearReconnectTimer(); // Clear any pending reconnection attempts + shouldBeConnected = true; + toggleBtnStates(true); // Update UI immediately to reflect connection attempt + + try { + // Initialize HumeClient if not yet initialized + if (!client) { + /** + * SECURITY NOTICE: This example uses direct API key authentication for simplicity. + * For production browser environments, implement the "Token Auth" strategy instead + * to prevent exposing your API key in client-side code. + * See: https://dev.hume.ai/docs/introduction/api-key#authentication-strategies + */ + const apiKey = import.meta.env.VITE_HUME_API_KEY; + if (!apiKey) throw new Error("VITE_HUME_API_KEY is not set in environment variables."); + client = new HumeClient({ apiKey }); + } + const configId = import.meta.env.VITE_HUME_CONFIG_ID; + if (!configId) console.warn("No Config ID specified, using default EVI configuration settings."); + + // Connect to EVI + socket = client.empathicVoice.chat.connect({ configId, resumedChatGroupId: chatGroupId }); + + // Attach WebSocket event listeners + socket.on('open', handleWebSocketOpen); + socket.on('message', handleWebSocketMessage); + socket.on('error', handleWebSocketError); + socket.on('close', handleWebSocketClose); + + } catch (error) { + console.error("Connection failed:", error); + // Reset state fully on initial connection failure + shouldBeConnected = false; + socket = null; + toggleBtnStates(false); + } } - /** - * stops audio capture and playback, and closes the Web Socket connection - */ + /** Gracefully disconnects from the Hume EVI WebSocket API and cleans up resources. */ function disconnect(): void { - // update ui state - toggleBtnStates(); - - // stop audio playback - stopAudio(); - - // stop audio capture - recorder?.stop(); - recorder = null; - audioStream = null; - - // set connected state to false to prevent automatic reconnect - connected = false; - - // IF resumeChats flag is false, reset chatGroupId so a new conversation is started when reconnecting - if (!resumeChats) { + shouldBeConnected = false; // Mark that the user intended to disconnect + clearReconnectTimer(); + // Stop audio playback and clear queue + stopAudioPlayback(); + // Stop recording and release microphone + stopRecording(); + releaseMicrophoneStream(); + // Reset chat group ID if chats shouldn't be resumed + if (!shouldResumeChats) { chatGroupId = undefined; + console.log("Chat resume disabled. Resetting chat group ID."); } - - // closed the Web Socket connection - socket?.close(); - } - - /** - * captures and records audio stream, and sends audio stream through the socket - * - * API Reference: - * - `audio_input`: https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#send.Audio%20Input.type - */ - async function captureAudio(): Promise { - audioStream = await getAudioStream(); - // ensure there is only one audio track in the stream - ensureSingleValidAudioTrack(audioStream); - - // instantiate the media recorder - recorder = new MediaRecorder(audioStream, { mimeType }); - console.log(recorder) - - // callback for when recorded chunk is available to be processed - recorder.ondataavailable = async ({ data }) => { - // IF size of data is smaller than 1 byte then do nothing - if (data.size < 1) return; - - // base64 encode audio data - const encodedAudioData = await convertBlobToBase64(data); - console.log(encodedAudioData.slice(0, 20)) - - // define the audio_input message JSON - const audioInput: Omit = { - data: encodedAudioData, - }; - - // send audio_input message - socket?.sendAudioInput(audioInput); - }; - - // capture audio input at a rate of 100ms (recommended) - const timeSlice = 100; - recorder.start(timeSlice); - } - - /** - * play the audio within the playback queue, converting each Blob into playable HTMLAudioElements - */ - function playAudio(): void { - // IF there is nothing in the audioQueue OR audio is currently playing then do nothing - if (!audioQueue.length || isPlaying) return; - - // update isPlaying state - isPlaying = true; - - // pull next audio output from the queue - const audioBlob = audioQueue.shift(); - - // IF audioBlob is unexpectedly undefined then do nothing - if (!audioBlob) return; - - // converts Blob to AudioElement for playback - const audioUrl = URL.createObjectURL(audioBlob); - currentAudio = new Audio(audioUrl); - - // play audio - currentAudio.play(); - - // callback for when audio finishes playing - currentAudio.onended = () => { - // update isPlaying state - isPlaying = false; - - // attempt to pull next audio output from queue - if (audioQueue.length) playAudio(); - }; - } - - /** - * stops audio playback, clears audio playback queue, and updates audio playback state - */ - function stopAudio(): void { - // stop the audio playback - currentAudio?.pause(); - currentAudio = null; - - // update audio playback state - isPlaying = false; - - // clear the audioQueue - audioQueue.length = 0; + // Close the WebSocket connection if it's open or connecting + if (socket && socket.readyState !== WebSocket.CLOSING && socket.readyState !== WebSocket.CLOSED) { + socket.close(); + } + socket = null; + toggleBtnStates(false); } - /** - * callback function to handle a WebSocket opened event - */ - async function handleWebSocketOpenEvent(): Promise { - /* place logic here which you would like invoked when the socket opens */ - console.log('Web socket connection opened'); - - // ensures socket will reconnect if disconnected unintentionally - connected = true; - - await captureAudio(); + /**--- WebSocket Event Handlers ---*/ + /** Handles the WebSocket 'open' event. Starts audio capture. */ + async function handleWebSocketOpen(): Promise { + console.log('WebSocket connection opened.'); + try { + await startAudioCapture(); + } catch (error) { + console.error("Failed to start audio capture after WebSocket open:", error); + alert("Failed to access microphone. Disconnecting."); + disconnect(); // Disconnect if we can't capture audio + } } - /** - * callback function to handle a WebSocket message event - * - * API Reference: - * - `user_message`: https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive.User%20Message.type - * - `assistant_message`: https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive.Assistant%20Message.type - * - `audio_output`: https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive.Audio%20Output.type - * - `user_interruption`: https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive.User%20Interruption.type - */ - async function handleWebSocketMessageEvent( - message: Hume.empathicVoice.SubscribeEvent - ): Promise { - /* place logic here which you would like to invoke when receiving a message through the socket */ - - // handle messages received through the WebSocket (messages are distinguished by their "type" field.) + /** Handles incoming WebSocket messages. */ + function handleWebSocketMessage(message: Hume.empathicVoice.SubscribeEvent) { switch (message.type) { - // save chat_group_id to resume chat if disconnected case 'chat_metadata': + // Store chat group ID for potential resumption chatGroupId = message.chatGroupId; break; - - // append user and assistant messages to UI for chat visibility case 'user_message': case 'assistant_message': - if (message.type === 'user_message') stopAudio(); + // Stop playback if user starts speaking + if (message.type === 'user_message') stopAudioPlayback(); + // Display the message in the chat UI const { role, content } = message.message; - const topThreeEmotions = extractTopThreeEmotions(message); - appendMessage(role, content ?? '', topThreeEmotions); + const topEmotions = extractTopThreeEmotions(message); + appendMessageToChat(role, content ?? '', topEmotions); break; - - // add received audio to the playback queue, and play next audio output case 'audio_output': - // convert base64 encoded audio to a Blob - const audioOutput = message.data; - const blob = convertBase64ToBlob(audioOutput, mimeType); - - // add audio Blob to audioQueue - audioQueue.push(blob); - - // play the next audio output - if (audioQueue.length >= 1) playAudio(); + // Decode and queue audio for playback + const audioBlob = convertBase64ToBlob(message.data, mimeType); + audioQueue.push(audioBlob); + playNextAudioChunk(); // Attempt to play immediately if not already playing break; - - // stop audio playback, clear audio playback queue, and update audio playback state on interrupt case 'user_interruption': - stopAudio(); + // Stop playback immediately when the user interrupts + console.log("User interruption detected."); + stopAudioPlayback(); + break; + case 'error': + // Log errors received from the EVI service + console.error(`EVI Error: Code=${message.code}, Slug=${message.slug}, Message=${message.message}`); break; } } - /** - * callback function to handle a WebSocket error event - */ - function handleWebSocketErrorEvent(error: Error): void { - /* place logic here which you would like invoked when receiving an error through the socket */ - console.error(error); + /** Handles WebSocket transport errors. */ + function handleWebSocketError(error: Event | Error): void { + console.error("WebSocket transport error:", error); } - /** - * callback function to handle a WebSocket closed event - */ - async function handleWebSocketCloseEvent(): Promise { - /* place logic here which you would like invoked when the socket closes */ + /** Handles the WebSocket 'close' event. Cleans up and attempts reconnection if needed. */ + function handleWebSocketClose(event: HumeCloseEvent): void { + console.log('WebSocket connection closed.'); + // Clean up resources regardless of why it closed + stopRecording(); + releaseMicrophoneStream(); + stopAudioPlayback(); + socket = null; + // Decide whether to reconnect based on shouldBeConnected flag and close code + const isAbnormalClosure = ABNORMAL_CLOSE_CODES.has(event.code); + if (shouldBeConnected && isAbnormalClosure) { + console.warn(`Unexpected closure (Code: ${event.code}). Attempting to reconnect in ${RECONNECT_DELAY_MS / 1000}s...`); + scheduleReconnect(); + } else { + // If it was a normal closure or user intended to disconnect, reset UI to disconnected state + shouldBeConnected = false; + if (!shouldResumeChats) chatGroupId = undefined; + toggleBtnStates(false); + } + } + + /**--- Audio Capture Functions ---*/ + /** Initializes microphone access and starts recording. */ + async function startAudioCapture(): Promise { + try { + audioStream = await getAudioStream(); + ensureSingleValidAudioTrack(audioStream); // Validate the stream + + recorder = new MediaRecorder(audioStream, { mimeType }); + recorder.ondataavailable = handleAudioDataAvailable; + recorder.onerror = (event) => console.error("MediaRecorder error:", event); + recorder.start(TIME_SLICE_MS); + } catch (error) { + console.error("Failed to initialize or start audio capture:", error); + throw error; + } + } + + /** Handles available audio data chunks from the MediaRecorder. */ + async function handleAudioDataAvailable(event: BlobEvent): Promise { + if (event.data.size > 0 && socket && socket.readyState === WebSocket.OPEN) { + const encodedAudio = await convertBlobToBase64(event.data); + socket.sendAudioInput({ data: encodedAudio }); + } + } - // reconnect to the socket if disconnect was unintentional - if (connected) await connect(); + /** Stops the MediaRecorder if it's active. */ + function stopRecording(): void { + if (recorder && recorder.state !== 'inactive') recorder.stop(); + recorder = null; + } - console.log('Web socket connection closed'); + /** Stops the tracks of the microphone audio stream to release the device. */ + function releaseMicrophoneStream(): void { + if (audioStream) audioStream.getTracks().forEach(track => track.stop()); + audioStream = null; } - /** - * adds message to Chat in the webpage's UI - * - * @param role the speaker associated with the audio transcription - * @param content transcript of the audio - * @param topThreeEmotions the top three emotion prediction scores for the message - */ - function appendMessage( + /**--- Audio Playback Functions ---*/ + /** Plays the next audio chunk from the queue if available and not already playing. */ + function playNextAudioChunk(): void { + // Don't play if already playing or queue is empty + if (isPlaying || audioQueue.length === 0) return; + + isPlaying = true; + const audioBlob = audioQueue.shift(); + + if (!audioBlob) { + isPlaying = false; + return; + } + const audioUrl = URL.createObjectURL(audioBlob); + currentAudio = new Audio(audioUrl); + currentAudio.play(); + currentAudio.onended = () => { + URL.revokeObjectURL(audioUrl); + currentAudio = null; + isPlaying = false; + playNextAudioChunk(); // Recursively play the next chunk if queue is not empty + }; + } + + /** Stops the currently playing audio and clears the playback queue. */ + function stopAudioPlayback(): void { + if (currentAudio) { + currentAudio.pause(); + console.log("Audio playback paused."); + if (currentAudio.src && currentAudio.src.startsWith('blob:')) { + URL.revokeObjectURL(currentAudio.src); // Revoke URL if paused mid-play + } + currentAudio = null; + } + audioQueue.length = 0; // Clear the queue + isPlaying = false; // Reset playback state + } + + /**--- UI and Helper Functions ---*/ + /** Appends a message (user or assistant) to the chat UI. */ + function appendMessageToChat( role: Hume.empathicVoice.Role, content: string, - topThreeEmotions: { emotion: string; score: any }[] + topEmotions: { emotion: string; score: string }[] ): void { - // generate chat card component with message content and emotion scores + if (!chat) return; const chatCard = new ChatCard({ role, timestamp: new Date().toLocaleTimeString(), content, - scores: topThreeEmotions, + scores: topEmotions, }); - - // append chat card to the UI - chat?.appendChild(chatCard.render()); - - // scroll to the bottom to view most recently added message - if (chat) chat.scrollTop = chat.scrollHeight; + chat.appendChild(chatCard.render()); + chat.scrollTop = chat.scrollHeight; // Auto-scroll to the bottom } - /** - * toggles `start` and `stop` buttons' disabled states - */ - function toggleBtnStates(): void { - if (startBtn) startBtn.disabled = !startBtn.disabled; - if (stopBtn) stopBtn.disabled = !stopBtn.disabled; - } - - /** - * takes a received `user_message` or `assistant_message` and extracts the top 3 emotions from the - * predicted expression measurement scores. - */ + /** Extracts and formats the top 3 emotion scores from a message. */ function extractTopThreeEmotions( message: Hume.empathicVoice.UserMessage | Hume.empathicVoice.AssistantMessage ): { emotion: string; score: string }[] { - // extract emotion scores from the message + // Extract emotion scores from the message const scores = message.models.prosody?.scores; - - // convert the emotions object into an array of key-value pairs + // Convert the emotions object into an array of key-value pairs const scoresArray = Object.entries(scores || {}); - - // sort the array by the values in descending order + // Sort the array by the values in descending order scoresArray.sort((a, b) => b[1] - a[1]); - - // extract the top three emotions and convert them back to an object + // Extract the top three emotions and convert them back to an object const topThreeEmotions = scoresArray.slice(0, 3).map(([emotion, score]) => ({ emotion, - score: (Math.round(Number(score) * 100) / 100).toFixed(2), + score: Number(score).toFixed(2), })); - return topThreeEmotions; } + + /** Updates the enabled/disabled state of the start/stop buttons. */ + function toggleBtnStates(isConnectedOrConnecting: boolean): void { + if (startBtn) startBtn.disabled = isConnectedOrConnecting; + if (stopBtn) stopBtn.disabled = !isConnectedOrConnecting; + } + + /** Checks if the socket is currently connecting or already open. */ + function isConnectingOrConnected(): boolean { + return socket?.readyState === WebSocket.CONNECTING || socket?.readyState === WebSocket.OPEN; + } + + /**--- Reconnection Logic ---*/ + /** Schedules a reconnection attempt after a delay. */ + function scheduleReconnect(): void { + clearReconnectTimer(); // Ensure no duplicate timers + reconnectTimeoutId = setTimeout(() => { + if (shouldBeConnected) { // Double-check if still intended before reconnecting + console.log("Attempting reconnect now..."); + connect(); // No 'await' needed, runs async + } + }, RECONNECT_DELAY_MS); + } + + /** Clears any pending reconnection timer. */ + function clearReconnectTimer(): void { + if (!reconnectTimeoutId) return; + clearTimeout(reconnectTimeoutId); + reconnectTimeoutId = null; + } })(); -/** - * The code below does not pertain to the EVI implementation, and only serves to style the UI. - */ +/** The code below does not pertain to the EVI implementation, and only serves to style the UI. */ interface Score { emotion: string; @@ -396,8 +365,7 @@ class ChatCard { const role = document.createElement('div'); role.className = 'role'; - role.textContent = - this.message.role.charAt(0).toUpperCase() + this.message.role.slice(1); + role.textContent = this.message.role.charAt(0).toUpperCase() + this.message.role.slice(1); const timestamp = document.createElement('div'); timestamp.className = 'timestamp'; @@ -409,9 +377,7 @@ class ChatCard { const scores = document.createElement('div'); scores.className = 'scores'; - this.message.scores.forEach((score) => { - scores.appendChild(this.createScoreItem(score)); - }); + this.message.scores.forEach((score) => scores.appendChild(this.createScoreItem(score))); card.appendChild(role); card.appendChild(timestamp);