diff --git a/.fern/metadata.json b/.fern/metadata.json new file mode 100644 index 00000000..1fbd137e --- /dev/null +++ b/.fern/metadata.json @@ -0,0 +1,21 @@ +{ + "cliVersion": "0.114.0", + "generatorName": "fernapi/fern-typescript-node-sdk", + "generatorVersion": "3.35.3", + "generatorConfig": { + "extraDependencies": { + "uuid": "9.0.1", + "zod": "^3.23.8" + }, + "extraDevDependencies": { + "@types/uuid": "9.0.7", + "@types/ws": "^8.5.9" + }, + "formDataSupport": "Node18", + "noSerdeLayer": false, + "enableInlineTypes": false, + "allowCustomFetcher": true, + "shouldGenerateWebsocketClients": true, + "namespaceExport": "Hume" + } +} diff --git a/.mock/fern.config.json b/.mock/fern.config.json index 2a7ce060..8ee40941 100644 --- a/.mock/fern.config.json +++ b/.mock/fern.config.json @@ -1,4 +1,4 @@ { - "organization": "hume", - "version": "0.114.0" -} + "organization" : "hume", + "version" : "0.114.0" +} \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index c0c40ac1..00000000 --- a/.npmignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules -src -tests -.gitignore -.github -.fernignore -.prettierrc.yml -biome.json -tsconfig.json -yarn.lock -pnpm-lock.yaml \ No newline at end of file diff --git a/package.json b/package.json index 093da573..5fead140 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hume", - "version": "0.15.9", + "version": "0.15.10", "private": false, "repository": "github:humeai/hume-typescript-sdk", "type": "commonjs", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4b03291..5fdd23bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,7 +35,7 @@ importers: version: 2.11.2(@types/node@18.19.130)(typescript@5.7.3) ts-loader: specifier: ^9.5.1 - version: 9.5.4(typescript@5.7.3)(webpack@5.102.1) + version: 9.5.4(typescript@5.7.3)(webpack@5.103.0) typescript: specifier: ~5.7.2 version: 5.7.3 @@ -44,7 +44,7 @@ importers: version: 3.2.4(@types/node@18.19.130)(msw@2.11.2(@types/node@18.19.130)(typescript@5.7.3))(terser@5.44.1) webpack: specifier: ^5.97.1 - version: 5.102.1 + version: 5.103.0 packages: @@ -263,12 +263,12 @@ packages: cpu: [x64] os: [win32] - '@inquirer/ansi@1.0.1': - resolution: {integrity: sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==} + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} - '@inquirer/confirm@5.1.19': - resolution: {integrity: sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==} + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -276,8 +276,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.3.0': - resolution: {integrity: sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==} + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -285,12 +285,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.14': - resolution: {integrity: sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==} + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} - '@inquirer/type@3.0.9': - resolution: {integrity: sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==} + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -327,113 +327,113 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@rollup/rollup-android-arm-eabi@4.52.5': - resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.5': - resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.5': - resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.5': - resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.5': - resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.5': - resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': - resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.5': - resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.5': - resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.5': - resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.5': - resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': - resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.5': - resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.5': - resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.5': - resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.5': - resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.5': - resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.5': - resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': - resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': - resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': - resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} cpu: [x64] os: [win32] @@ -589,16 +589,16 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - baseline-browser-mapping@2.8.25: - resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} + baseline-browser-mapping@2.8.32: + resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==} hasBin: true braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.27.0: - resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -609,8 +609,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - caniuse-lite@1.0.30001753: - resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} @@ -663,8 +663,8 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} - electron-to-chromium@1.5.245: - resolution: {integrity: sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==} + electron-to-chromium@1.5.262: + resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -876,8 +876,8 @@ packages: rettime@0.7.0: resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==} - rollup@4.52.5: - resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -997,11 +997,11 @@ packages: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} - tldts-core@7.0.17: - resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} - tldts@7.0.17: - resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} hasBin: true to-regex-range@5.0.1: @@ -1046,8 +1046,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.2.0: - resolution: {integrity: sha512-C/Naxf8H0pBx1PA4BdpT+c/5wdqI9ILMdwjSMILw7tVIh3JsxzZqdeTLmmdaoh5MYUEOyBnM9K3o0DzoZ/fe+w==} + vite@7.2.6: + resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -1122,8 +1122,8 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.102.1: - resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} + webpack@5.103.0: + resolution: {integrity: sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -1299,20 +1299,20 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@inquirer/ansi@1.0.1': {} + '@inquirer/ansi@1.0.2': {} - '@inquirer/confirm@5.1.19(@types/node@18.19.130)': + '@inquirer/confirm@5.1.21(@types/node@18.19.130)': dependencies: - '@inquirer/core': 10.3.0(@types/node@18.19.130) - '@inquirer/type': 3.0.9(@types/node@18.19.130) + '@inquirer/core': 10.3.2(@types/node@18.19.130) + '@inquirer/type': 3.0.10(@types/node@18.19.130) optionalDependencies: '@types/node': 18.19.130 - '@inquirer/core@10.3.0(@types/node@18.19.130)': + '@inquirer/core@10.3.2(@types/node@18.19.130)': dependencies: - '@inquirer/ansi': 1.0.1 - '@inquirer/figures': 1.0.14 - '@inquirer/type': 3.0.9(@types/node@18.19.130) + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@18.19.130) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 @@ -1321,9 +1321,9 @@ snapshots: optionalDependencies: '@types/node': 18.19.130 - '@inquirer/figures@1.0.14': {} + '@inquirer/figures@1.0.15': {} - '@inquirer/type@3.0.9(@types/node@18.19.130)': + '@inquirer/type@3.0.10(@types/node@18.19.130)': optionalDependencies: '@types/node': 18.19.130 @@ -1364,70 +1364,70 @@ snapshots: '@open-draft/until@2.1.0': {} - '@rollup/rollup-android-arm-eabi@4.52.5': + '@rollup/rollup-android-arm-eabi@4.53.3': optional: true - '@rollup/rollup-android-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.53.3': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@rollup/rollup-darwin-arm64@4.53.3': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@rollup/rollup-darwin-x64@4.53.3': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@rollup/rollup-freebsd-arm64@4.53.3': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@rollup/rollup-freebsd-x64@4.53.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@rollup/rollup-linux-arm-musleabihf@4.53.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@rollup/rollup-linux-arm64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.53.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.5': + '@rollup/rollup-linux-riscv64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': + '@rollup/rollup-linux-riscv64-musl@4.53.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': + '@rollup/rollup-linux-s390x-gnu@4.53.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': + '@rollup/rollup-linux-x64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': + '@rollup/rollup-linux-x64-musl@4.53.3': optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': + '@rollup/rollup-openharmony-arm64@4.53.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': + '@rollup/rollup-win32-arm64-msvc@4.53.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': + '@rollup/rollup-win32-ia32-msvc@4.53.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': + '@rollup/rollup-win32-x64-gnu@4.53.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': + '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true '@types/chai@5.2.3': @@ -1473,14 +1473,14 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(msw@2.11.2(@types/node@18.19.130)(typescript@5.7.3))(vite@7.2.0(@types/node@18.19.130)(terser@5.44.1))': + '@vitest/mocker@3.2.4(msw@2.11.2(@types/node@18.19.130)(typescript@5.7.3))(vite@7.2.6(@types/node@18.19.130)(terser@5.44.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.11.2(@types/node@18.19.130)(typescript@5.7.3) - vite: 7.2.0(@types/node@18.19.130)(terser@5.44.1) + vite: 7.2.6(@types/node@18.19.130)(terser@5.44.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -1618,25 +1618,25 @@ snapshots: assertion-error@2.0.1: {} - baseline-browser-mapping@2.8.25: {} + baseline-browser-mapping@2.8.32: {} braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.27.0: + browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.8.25 - caniuse-lite: 1.0.30001753 - electron-to-chromium: 1.5.245 + baseline-browser-mapping: 2.8.32 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.262 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.27.0) + update-browserslist-db: 1.1.4(browserslist@4.28.0) buffer-from@1.1.2: {} cac@6.7.14: {} - caniuse-lite@1.0.30001753: {} + caniuse-lite@1.0.30001757: {} chai@5.3.3: dependencies: @@ -1679,7 +1679,7 @@ snapshots: deep-eql@5.0.2: {} - electron-to-chromium@1.5.245: {} + electron-to-chromium@1.5.262: {} emoji-regex@8.0.0: {} @@ -1814,7 +1814,7 @@ snapshots: dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 - '@inquirer/confirm': 5.1.19(@types/node@18.19.130) + '@inquirer/confirm': 5.1.21(@types/node@18.19.130) '@mswjs/interceptors': 0.39.8 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 @@ -1874,32 +1874,32 @@ snapshots: rettime@0.7.0: {} - rollup@4.52.5: + rollup@4.53.3: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.5 - '@rollup/rollup-android-arm64': 4.52.5 - '@rollup/rollup-darwin-arm64': 4.52.5 - '@rollup/rollup-darwin-x64': 4.52.5 - '@rollup/rollup-freebsd-arm64': 4.52.5 - '@rollup/rollup-freebsd-x64': 4.52.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 - '@rollup/rollup-linux-arm-musleabihf': 4.52.5 - '@rollup/rollup-linux-arm64-gnu': 4.52.5 - '@rollup/rollup-linux-arm64-musl': 4.52.5 - '@rollup/rollup-linux-loong64-gnu': 4.52.5 - '@rollup/rollup-linux-ppc64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-musl': 4.52.5 - '@rollup/rollup-linux-s390x-gnu': 4.52.5 - '@rollup/rollup-linux-x64-gnu': 4.52.5 - '@rollup/rollup-linux-x64-musl': 4.52.5 - '@rollup/rollup-openharmony-arm64': 4.52.5 - '@rollup/rollup-win32-arm64-msvc': 4.52.5 - '@rollup/rollup-win32-ia32-msvc': 4.52.5 - '@rollup/rollup-win32-x64-gnu': 4.52.5 - '@rollup/rollup-win32-x64-msvc': 4.52.5 + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 safe-buffer@5.2.1: {} @@ -1964,14 +1964,14 @@ snapshots: tapable@2.3.0: {} - terser-webpack-plugin@5.3.14(webpack@5.102.1): + terser-webpack-plugin@5.3.14(webpack@5.103.0): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.1 - webpack: 5.102.1 + webpack: 5.103.0 terser@5.44.1: dependencies: @@ -1995,11 +1995,11 @@ snapshots: tinyspy@4.0.4: {} - tldts-core@7.0.17: {} + tldts-core@7.0.19: {} - tldts@7.0.17: + tldts@7.0.19: dependencies: - tldts-core: 7.0.17 + tldts-core: 7.0.19 to-regex-range@5.0.1: dependencies: @@ -2007,9 +2007,9 @@ snapshots: tough-cookie@6.0.0: dependencies: - tldts: 7.0.17 + tldts: 7.0.19 - ts-loader@9.5.4(typescript@5.7.3)(webpack@5.102.1): + ts-loader@9.5.4(typescript@5.7.3)(webpack@5.103.0): dependencies: chalk: 4.1.2 enhanced-resolve: 5.18.3 @@ -2017,7 +2017,7 @@ snapshots: semver: 7.7.3 source-map: 0.7.6 typescript: 5.7.3 - webpack: 5.102.1 + webpack: 5.103.0 type-fest@4.41.0: {} @@ -2025,9 +2025,9 @@ snapshots: undici-types@5.26.5: {} - update-browserslist-db@1.1.4(browserslist@4.27.0): + update-browserslist-db@1.1.4(browserslist@4.28.0): dependencies: - browserslist: 4.27.0 + browserslist: 4.28.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -2039,7 +2039,7 @@ snapshots: debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.2.0(@types/node@18.19.130)(terser@5.44.1) + vite: 7.2.6(@types/node@18.19.130)(terser@5.44.1) transitivePeerDependencies: - '@types/node' - jiti @@ -2054,13 +2054,13 @@ snapshots: - tsx - yaml - vite@7.2.0(@types/node@18.19.130)(terser@5.44.1): + vite@7.2.6(@types/node@18.19.130)(terser@5.44.1): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.5 + rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 18.19.130 @@ -2071,7 +2071,7 @@ snapshots: dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.11.2(@types/node@18.19.130)(typescript@5.7.3))(vite@7.2.0(@types/node@18.19.130)(terser@5.44.1)) + '@vitest/mocker': 3.2.4(msw@2.11.2(@types/node@18.19.130)(typescript@5.7.3))(vite@7.2.6(@types/node@18.19.130)(terser@5.44.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -2089,7 +2089,7 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.2.0(@types/node@18.19.130)(terser@5.44.1) + vite: 7.2.6(@types/node@18.19.130)(terser@5.44.1) vite-node: 3.2.4(@types/node@18.19.130)(terser@5.44.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -2115,7 +2115,7 @@ snapshots: webpack-sources@3.3.3: {} - webpack@5.102.1: + webpack@5.103.0: dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -2125,7 +2125,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.27.0 + browserslist: 4.28.0 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 @@ -2139,7 +2139,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(webpack@5.102.1) + terser-webpack-plugin: 5.3.14(webpack@5.103.0) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: diff --git a/reference.md b/reference.md index 270e6ac4..0a984b16 100644 --- a/reference.md +++ b/reference.md @@ -1,6 +1,6 @@ # Reference -## EmpathicVoice ControlPlane -
client.empathicVoice.controlPlane.send(chatId, { ...params }) -> void +## Tts +
client.tts.synthesizeJson({ ...params }) -> Hume.ReturnTts
@@ -12,7 +12,9 @@
-Send a message to a specific chat. +Synthesizes one or more input texts into speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. + +The response includes the base64-encoded audio and metadata in JSON format.
@@ -27,8 +29,21 @@ Send a message to a specific chat.
```typescript -await client.empathicVoice.controlPlane.send("chat_id", { - type: "session_settings" +await client.tts.synthesizeJson({ + context: { + utterances: [{ + text: "How can people see beauty so differently?", + description: "A curious student with a clear and respectful tone, seeking clarification on Hume's ideas with a straightforward question." + }] + }, + format: { + type: "mp3" + }, + numGenerations: 1, + utterances: [{ + text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", + description: "Middle-aged masculine voice with a clear, rhythmic Scots lilt, rounded vowels, and a warm, steady tone with an articulate, academic quality." + }] }); ``` @@ -45,15 +60,7 @@ await client.empathicVoice.controlPlane.send("chat_id", {
-**chatId:** `string` - -
-
- -
-
- -**request:** `Hume.ControlPlanePublishEvent` +**request:** `Hume.PostedTts`
@@ -61,7 +68,7 @@ await client.empathicVoice.controlPlane.send("chat_id", {
-**requestOptions:** `ControlPlane.RequestOptions` +**requestOptions:** `TtsClient.RequestOptions`
@@ -73,8 +80,7 @@ await client.empathicVoice.controlPlane.send("chat_id", {
-## EmpathicVoice ChatGroups -
client.empathicVoice.chatGroups.listChatGroups({ ...params }) -> core.Page +
client.tts.synthesizeFile({ ...params }) -> core.BinaryResponse
@@ -86,7 +92,9 @@ await client.empathicVoice.controlPlane.send("chat_id", {
-Fetches a paginated list of **Chat Groups**. +Synthesizes one or more input texts into speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. + +The response contains the generated audio file in the requested format.
@@ -101,29 +109,19 @@ Fetches a paginated list of **Chat Groups**.
```typescript -const pageableResponse = await client.empathicVoice.chatGroups.listChatGroups({ - pageNumber: 0, - pageSize: 1, - ascendingOrder: true, - configId: "1b60e1a0-cc59-424a-8d2c-189d354db3f3" -}); -for await (const item of pageableResponse) { - console.log(item); -} - -// Or you can manually iterate page-by-page -let page = await client.empathicVoice.chatGroups.listChatGroups({ - pageNumber: 0, - pageSize: 1, - ascendingOrder: true, - configId: "1b60e1a0-cc59-424a-8d2c-189d354db3f3" +await client.tts.synthesizeFile({ + context: { + generationId: "09ad914d-8e7f-40f8-a279-e34f07f7dab2" + }, + format: { + type: "mp3" + }, + numGenerations: 1, + utterances: [{ + text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", + description: "Middle-aged masculine voice with a clear, rhythmic Scots lilt, rounded vowels, and a warm, steady tone with an articulate, academic quality." + }] }); -while (page.hasNextPage()) { - page = page.getNextPage(); -} - -// You can also access the underlying response -const response = page.response; ```
@@ -139,7 +137,7 @@ const response = page.response;
-**request:** `Hume.empathicVoice.ChatGroupsListChatGroupsRequest` +**request:** `Hume.PostedTts`
@@ -147,7 +145,7 @@ const response = page.response;
-**requestOptions:** `ChatGroups.RequestOptions` +**requestOptions:** `TtsClient.RequestOptions`
@@ -159,7 +157,7 @@ const response = page.response;
-
client.empathicVoice.chatGroups.getChatGroup(id, { ...params }) -> Hume.ReturnChatGroupPagedChats +
client.tts.synthesizeFileStreaming({ ...params }) -> core.BinaryResponse
@@ -171,7 +169,7 @@ const response = page.response;
-Fetches a **ChatGroup** by ID, including a paginated list of **Chats** associated with the **ChatGroup**. +Streams synthesized speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody.
@@ -186,10 +184,14 @@ Fetches a **ChatGroup** by ID, including a paginated list of **Chats** associate
```typescript -await client.empathicVoice.chatGroups.getChatGroup("697056f0-6c7e-487d-9bd8-9c19df79f05f", { - pageNumber: 0, - pageSize: 1, - ascendingOrder: true +await client.tts.synthesizeFileStreaming({ + utterances: [{ + text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", + voice: { + name: "Male English Actor", + provider: "HUME_AI" + } + }] }); ``` @@ -206,15 +208,7 @@ await client.empathicVoice.chatGroups.getChatGroup("697056f0-6c7e-487d-9bd8-9c19
-**id:** `string` — Identifier for a Chat Group. Formatted as a UUID. - -
-
- -
-
- -**request:** `Hume.empathicVoice.ChatGroupsGetChatGroupRequest` +**request:** `Hume.PostedTts`
@@ -222,7 +216,7 @@ await client.empathicVoice.chatGroups.getChatGroup("697056f0-6c7e-487d-9bd8-9c19
-**requestOptions:** `ChatGroups.RequestOptions` +**requestOptions:** `TtsClient.RequestOptions`
@@ -234,7 +228,7 @@ await client.empathicVoice.chatGroups.getChatGroup("697056f0-6c7e-487d-9bd8-9c19
-
client.empathicVoice.chatGroups.getAudio(id, { ...params }) -> Hume.ReturnChatGroupPagedAudioReconstructions +
client.tts.synthesizeJsonStreaming({ ...params }) -> core.Stream
@@ -246,7 +240,9 @@ await client.empathicVoice.chatGroups.getChatGroup("697056f0-6c7e-487d-9bd8-9c19
-Fetches a paginated list of audio for each **Chat** within the specified **Chat Group**. For more details, see our guide on audio reconstruction [here](/docs/speech-to-speech-evi/faq#can-i-access-the-audio-of-previous-conversations-with-evi). +Streams synthesized speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. + +The response is a stream of JSON objects including audio encoded in base64.
@@ -261,11 +257,18 @@ Fetches a paginated list of audio for each **Chat** within the specified **Chat
```typescript -await client.empathicVoice.chatGroups.getAudio("369846cf-6ad5-404d-905e-a8acb5cdfc78", { - pageNumber: 0, - pageSize: 10, - ascendingOrder: true +const response = await client.tts.synthesizeJsonStreaming({ + utterances: [{ + text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", + voice: { + name: "Male English Actor", + provider: "HUME_AI" + } + }] }); +for await (const item of response) { + console.log(item); +} ```
@@ -281,15 +284,7 @@ await client.empathicVoice.chatGroups.getAudio("369846cf-6ad5-404d-905e-a8acb5cd
-**id:** `string` — Identifier for a Chat Group. Formatted as a UUID. - -
-
- -
-
- -**request:** `Hume.empathicVoice.ChatGroupsGetAudioRequest` +**request:** `Hume.PostedTts`
@@ -297,7 +292,7 @@ await client.empathicVoice.chatGroups.getAudio("369846cf-6ad5-404d-905e-a8acb5cd
-**requestOptions:** `ChatGroups.RequestOptions` +**requestOptions:** `TtsClient.RequestOptions`
@@ -309,24 +304,10 @@ await client.empathicVoice.chatGroups.getAudio("369846cf-6ad5-404d-905e-a8acb5cd
-
client.empathicVoice.chatGroups.listChatGroupEvents(id, { ...params }) -> core.Page -
-
- -#### 📝 Description - -
-
- +
client.tts.convertVoiceJson({ ...params }) -> core.Stream
-Fetches a paginated list of **Chat** events associated with a **Chat Group**. -
-
-
-
- #### 🔌 Usage
@@ -336,28 +317,11 @@ Fetches a paginated list of **Chat** events associated with a **Chat Group**.
```typescript -const pageableResponse = await client.empathicVoice.chatGroups.listChatGroupEvents("697056f0-6c7e-487d-9bd8-9c19df79f05f", { - pageNumber: 0, - pageSize: 3, - ascendingOrder: true -}); -for await (const item of pageableResponse) { +const response = await client.tts.convertVoiceJson({}); +for await (const item of response) { console.log(item); } -// Or you can manually iterate page-by-page -let page = await client.empathicVoice.chatGroups.listChatGroupEvents("697056f0-6c7e-487d-9bd8-9c19df79f05f", { - pageNumber: 0, - pageSize: 3, - ascendingOrder: true -}); -while (page.hasNextPage()) { - page = page.getNextPage(); -} - -// You can also access the underlying response -const response = page.response; - ```
@@ -372,15 +336,7 @@ const response = page.response;
-**id:** `string` — Identifier for a Chat Group. Formatted as a UUID. - -
-
- -
-
- -**request:** `Hume.empathicVoice.ChatGroupsListChatGroupEventsRequest` +**request:** `Hume.tts.ConvertVoiceJsonRequest`
@@ -388,7 +344,7 @@ const response = page.response;
-**requestOptions:** `ChatGroups.RequestOptions` +**requestOptions:** `TtsClient.RequestOptions`
@@ -400,8 +356,8 @@ const response = page.response;
-## EmpathicVoice Chats -
client.empathicVoice.chats.listChats({ ...params }) -> core.Page +## Tts Voices +
client.tts.voices.list({ ...params }) -> core.Page
@@ -413,7 +369,7 @@ const response = page.response;
-Fetches a paginated list of **Chats**. +Lists voices you have saved in your account, or voices from the [Voice Library](https://platform.hume.ai/tts/voice-library).
@@ -428,20 +384,16 @@ Fetches a paginated list of **Chats**.
```typescript -const pageableResponse = await client.empathicVoice.chats.listChats({ - pageNumber: 0, - pageSize: 1, - ascendingOrder: true +const pageableResponse = await client.tts.voices.list({ + provider: "CUSTOM_VOICE" }); for await (const item of pageableResponse) { console.log(item); } // Or you can manually iterate page-by-page -let page = await client.empathicVoice.chats.listChats({ - pageNumber: 0, - pageSize: 1, - ascendingOrder: true +let page = await client.tts.voices.list({ + provider: "CUSTOM_VOICE" }); while (page.hasNextPage()) { page = page.getNextPage(); @@ -464,7 +416,7 @@ const response = page.response;
-**request:** `Hume.empathicVoice.ChatsListChatsRequest` +**request:** `Hume.tts.VoicesListRequest`
@@ -472,7 +424,7 @@ const response = page.response;
-**requestOptions:** `Chats.RequestOptions` +**requestOptions:** `VoicesClient.RequestOptions`
@@ -484,7 +436,7 @@ const response = page.response;
-
client.empathicVoice.chats.listChatEvents(id, { ...params }) -> core.Page +
client.tts.voices.create({ ...params }) -> Hume.ReturnVoice
@@ -496,7 +448,9 @@ const response = page.response;
-Fetches a paginated list of **Chat** events. +Saves a new custom voice to your account using the specified TTS generation ID. + +Once saved, this voice can be reused in subsequent TTS requests, ensuring consistent speech style and prosody. For more details on voice creation, see the [Voices Guide](/docs/text-to-speech-tts/voices).
@@ -511,27 +465,10 @@ Fetches a paginated list of **Chat** events.
```typescript -const pageableResponse = await client.empathicVoice.chats.listChatEvents("470a49f6-1dec-4afe-8b61-035d3b2d63b0", { - pageNumber: 0, - pageSize: 3, - ascendingOrder: true -}); -for await (const item of pageableResponse) { - console.log(item); -} - -// Or you can manually iterate page-by-page -let page = await client.empathicVoice.chats.listChatEvents("470a49f6-1dec-4afe-8b61-035d3b2d63b0", { - pageNumber: 0, - pageSize: 3, - ascendingOrder: true +await client.tts.voices.create({ + generationId: "795c949a-1510-4a80-9646-7d0863b023ab", + name: "David Hume" }); -while (page.hasNextPage()) { - page = page.getNextPage(); -} - -// You can also access the underlying response -const response = page.response; ```
@@ -547,15 +484,7 @@ const response = page.response;
-**id:** `string` — Identifier for a Chat. Formatted as a UUID. - -
-
- -
-
- -**request:** `Hume.empathicVoice.ChatsListChatEventsRequest` +**request:** `Hume.tts.PostedVoice`
@@ -563,7 +492,7 @@ const response = page.response;
-**requestOptions:** `Chats.RequestOptions` +**requestOptions:** `VoicesClient.RequestOptions`
@@ -575,7 +504,7 @@ const response = page.response;
-
client.empathicVoice.chats.getAudio(id) -> Hume.ReturnChatAudioReconstruction +
client.tts.voices.delete({ ...params }) -> void
@@ -587,7 +516,7 @@ const response = page.response;
-Fetches the audio of a previous **Chat**. For more details, see our guide on audio reconstruction [here](/docs/speech-to-speech-evi/faq#can-i-access-the-audio-of-previous-conversations-with-evi). +Deletes a previously generated custom voice.
@@ -602,7 +531,9 @@ Fetches the audio of a previous **Chat**. For more details, see our guide on aud
```typescript -await client.empathicVoice.chats.getAudio("470a49f6-1dec-4afe-8b61-035d3b2d63b0"); +await client.tts.voices.delete({ + name: "David Hume" +}); ```
@@ -618,7 +549,7 @@ await client.empathicVoice.chats.getAudio("470a49f6-1dec-4afe-8b61-035d3b2d63b0"
-**id:** `string` — Identifier for a chat. Formatted as a UUID. +**request:** `Hume.tts.VoicesDeleteRequest`
@@ -626,7 +557,7 @@ await client.empathicVoice.chats.getAudio("470a49f6-1dec-4afe-8b61-035d3b2d63b0"
-**requestOptions:** `Chats.RequestOptions` +**requestOptions:** `VoicesClient.RequestOptions`
@@ -638,8 +569,8 @@ await client.empathicVoice.chats.getAudio("470a49f6-1dec-4afe-8b61-035d3b2d63b0"
-## EmpathicVoice Configs -
client.empathicVoice.configs.listConfigs({ ...params }) -> core.Page +## EmpathicVoice ControlPlane +
client.empathicVoice.controlPlane.send(chat_id, { ...params }) -> void
@@ -651,9 +582,7 @@ await client.empathicVoice.chats.getAudio("470a49f6-1dec-4afe-8b61-035d3b2d63b0"
-Fetches a paginated list of **Configs**. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Send a message to a specific chat.
@@ -668,25 +597,9 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -const pageableResponse = await client.empathicVoice.configs.listConfigs({ - pageNumber: 0, - pageSize: 1 -}); -for await (const item of pageableResponse) { - console.log(item); -} - -// Or you can manually iterate page-by-page -let page = await client.empathicVoice.configs.listConfigs({ - pageNumber: 0, - pageSize: 1 +await client.empathicVoice.controlPlane.send("chat_id", { + type: "session_settings" }); -while (page.hasNextPage()) { - page = page.getNextPage(); -} - -// You can also access the underlying response -const response = page.response; ```
@@ -702,102 +615,15 @@ const response = page.response;
-**request:** `Hume.empathicVoice.ConfigsListConfigsRequest` - -
-
- -
-
- -**requestOptions:** `Configs.RequestOptions` +**chat_id:** `string`
- -
- - - - -
- -
client.empathicVoice.configs.createConfig({ ...params }) -> Hume.ReturnConfig -
-
- -#### 📝 Description - -
-
- -
-
- -Creates a **Config** which can be applied to EVI. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.empathicVoice.configs.createConfig({ - name: "Weather Assistant Config", - prompt: { - id: "af699d45-2985-42cc-91b9-af9e5da3bac5", - version: 0 - }, - eviVersion: "3", - voice: { - provider: "HUME_AI", - name: "Ava Song" - }, - languageModel: { - modelProvider: "ANTHROPIC", - modelResource: "claude-3-7-sonnet-latest", - temperature: 1 - }, - eventMessages: { - onNewChat: { - enabled: false, - text: "" - }, - onInactivityTimeout: { - enabled: false, - text: "" - }, - onMaxDurationTimeout: { - enabled: false, - text: "" - } - } -}); - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
-**request:** `Hume.empathicVoice.PostedConfig` +**request:** `Hume.ControlPlanePublishEvent`
@@ -805,7 +631,7 @@ await client.empathicVoice.configs.createConfig({
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ControlPlaneClient.RequestOptions`
@@ -817,7 +643,8 @@ await client.empathicVoice.configs.createConfig({
-
client.empathicVoice.configs.listConfigVersions(id, { ...params }) -> core.Page +## EmpathicVoice ChatGroups +
client.empathicVoice.chatGroups.listChatGroups({ ...params }) -> core.Page
@@ -829,9 +656,7 @@ await client.empathicVoice.configs.createConfig({
-Fetches a list of a **Config's** versions. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches a paginated list of **Chat Groups**.
@@ -846,13 +671,23 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -const pageableResponse = await client.empathicVoice.configs.listConfigVersions("1b60e1a0-cc59-424a-8d2c-189d354db3f3"); +const pageableResponse = await client.empathicVoice.chatGroups.listChatGroups({ + pageNumber: 0, + pageSize: 1, + ascendingOrder: true, + configId: "1b60e1a0-cc59-424a-8d2c-189d354db3f3" +}); for await (const item of pageableResponse) { console.log(item); } // Or you can manually iterate page-by-page -let page = await client.empathicVoice.configs.listConfigVersions("1b60e1a0-cc59-424a-8d2c-189d354db3f3"); +let page = await client.empathicVoice.chatGroups.listChatGroups({ + pageNumber: 0, + pageSize: 1, + ascendingOrder: true, + configId: "1b60e1a0-cc59-424a-8d2c-189d354db3f3" +}); while (page.hasNextPage()) { page = page.getNextPage(); } @@ -874,15 +709,7 @@ const response = page.response;
-**id:** `string` — Identifier for a Config. Formatted as a UUID. - -
-
- -
-
- -**request:** `Hume.empathicVoice.ConfigsListConfigVersionsRequest` +**request:** `Hume.empathicVoice.ChatGroupsListChatGroupsRequest`
@@ -890,7 +717,7 @@ const response = page.response;
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ChatGroupsClient.RequestOptions`
@@ -902,7 +729,7 @@ const response = page.response;
-
client.empathicVoice.configs.createConfigVersion(id, { ...params }) -> Hume.ReturnConfig +
client.empathicVoice.chatGroups.getChatGroup(id, { ...params }) -> Hume.ReturnChatGroupPagedChats
@@ -914,9 +741,7 @@ const response = page.response;
-Updates a **Config** by creating a new version of the **Config**. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches a **ChatGroup** by ID, including a paginated list of **Chats** associated with the **ChatGroup**.
@@ -931,39 +756,10 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-189d354db3f3", { - versionDescription: "This is an updated version of the Weather Assistant Config.", - eviVersion: "3", - prompt: { - id: "af699d45-2985-42cc-91b9-af9e5da3bac5", - version: 0 - }, - voice: { - provider: "HUME_AI", - name: "Ava Song" - }, - languageModel: { - modelProvider: "ANTHROPIC", - modelResource: "claude-3-7-sonnet-latest", - temperature: 1 - }, - ellmModel: { - allowShortResponses: true - }, - eventMessages: { - onNewChat: { - enabled: false, - text: "" - }, - onInactivityTimeout: { - enabled: false, - text: "" - }, - onMaxDurationTimeout: { - enabled: false, - text: "" - } - } +await client.empathicVoice.chatGroups.getChatGroup("697056f0-6c7e-487d-9bd8-9c19df79f05f", { + pageNumber: 0, + pageSize: 1, + ascendingOrder: true }); ``` @@ -980,7 +776,7 @@ await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-
-**id:** `string` — Identifier for a Config. Formatted as a UUID. +**id:** `string` — Identifier for a Chat Group. Formatted as a UUID.
@@ -988,7 +784,7 @@ await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-
-**request:** `Hume.empathicVoice.PostedConfigVersion` +**request:** `Hume.empathicVoice.ChatGroupsGetChatGroupRequest`
@@ -996,7 +792,7 @@ await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ChatGroupsClient.RequestOptions`
@@ -1008,7 +804,7 @@ await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-
-
client.empathicVoice.configs.deleteConfig(id) -> void +
client.empathicVoice.chatGroups.getAudio(id, { ...params }) -> Hume.ReturnChatGroupPagedAudioReconstructions
@@ -1020,9 +816,7 @@ await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-
-Deletes a **Config** and its versions. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches a paginated list of audio for each **Chat** within the specified **Chat Group**. For more details, see our guide on audio reconstruction [here](/docs/speech-to-speech-evi/faq#can-i-access-the-audio-of-previous-conversations-with-evi).
@@ -1037,7 +831,11 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354db3f3"); +await client.empathicVoice.chatGroups.getAudio("369846cf-6ad5-404d-905e-a8acb5cdfc78", { + pageNumber: 0, + pageSize: 10, + ascendingOrder: true +}); ```
@@ -1053,7 +851,7 @@ await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354
-**id:** `string` — Identifier for a Config. Formatted as a UUID. +**id:** `string` — Identifier for a Chat Group. Formatted as a UUID.
@@ -1061,7 +859,15 @@ await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354
-**requestOptions:** `Configs.RequestOptions` +**request:** `Hume.empathicVoice.ChatGroupsGetAudioRequest` + +
+
+ +
+
+ +**requestOptions:** `ChatGroupsClient.RequestOptions`
@@ -1073,7 +879,7 @@ await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354
-
client.empathicVoice.configs.updateConfigName(id, { ...params }) -> string +
client.empathicVoice.chatGroups.listChatGroupEvents(id, { ...params }) -> core.Page
@@ -1085,9 +891,7 @@ await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354
-Updates the name of a **Config**. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches a paginated list of **Chat** events associated with a **Chat Group**.
@@ -1102,9 +906,27 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189d354db3f3", { - name: "Updated Weather Assistant Config Name" +const pageableResponse = await client.empathicVoice.chatGroups.listChatGroupEvents("697056f0-6c7e-487d-9bd8-9c19df79f05f", { + pageNumber: 0, + pageSize: 3, + ascendingOrder: true +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.empathicVoice.chatGroups.listChatGroupEvents("697056f0-6c7e-487d-9bd8-9c19df79f05f", { + pageNumber: 0, + pageSize: 3, + ascendingOrder: true }); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; ```
@@ -1120,7 +942,7 @@ await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189
-**id:** `string` — Identifier for a Config. Formatted as a UUID. +**id:** `string` — Identifier for a Chat Group. Formatted as a UUID.
@@ -1128,7 +950,7 @@ await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189
-**request:** `Hume.empathicVoice.PostedConfigName` +**request:** `Hume.empathicVoice.ChatGroupsListChatGroupEventsRequest`
@@ -1136,7 +958,7 @@ await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ChatGroupsClient.RequestOptions`
@@ -1148,7 +970,8 @@ await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189
-
client.empathicVoice.configs.getConfigVersion(id, version) -> Hume.ReturnConfig +## EmpathicVoice Chats +
client.empathicVoice.chats.listChats({ ...params }) -> core.Page
@@ -1160,9 +983,7 @@ await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189
-Fetches a specified version of a **Config**. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches a paginated list of **Chats**.
@@ -1177,7 +998,27 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -await client.empathicVoice.configs.getConfigVersion("1b60e1a0-cc59-424a-8d2c-189d354db3f3", 1); +const pageableResponse = await client.empathicVoice.chats.listChats({ + pageNumber: 0, + pageSize: 1, + ascendingOrder: true +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.empathicVoice.chats.listChats({ + pageNumber: 0, + pageSize: 1, + ascendingOrder: true +}); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; ```
@@ -1193,21 +1034,7 @@ await client.empathicVoice.configs.getConfigVersion("1b60e1a0-cc59-424a-8d2c-189
-**id:** `string` — Identifier for a Config. Formatted as a UUID. - -
-
- -
-
- -**version:** `number` - -Version number for a Config. - -Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. - -Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number. +**request:** `Hume.empathicVoice.ChatsListChatsRequest`
@@ -1215,7 +1042,7 @@ Version numbers are integer values representing different iterations of the Conf
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ChatsClient.RequestOptions`
@@ -1227,7 +1054,7 @@ Version numbers are integer values representing different iterations of the Conf
-
client.empathicVoice.configs.deleteConfigVersion(id, version) -> void +
client.empathicVoice.chats.listChatEvents(id, { ...params }) -> core.Page
@@ -1239,9 +1066,7 @@ Version numbers are integer values representing different iterations of the Conf
-Deletes a specified version of a **Config**. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches a paginated list of **Chat** events.
@@ -1256,7 +1081,27 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -await client.empathicVoice.configs.deleteConfigVersion("1b60e1a0-cc59-424a-8d2c-189d354db3f3", 1); +const pageableResponse = await client.empathicVoice.chats.listChatEvents("470a49f6-1dec-4afe-8b61-035d3b2d63b0", { + pageNumber: 0, + pageSize: 3, + ascendingOrder: true +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.empathicVoice.chats.listChatEvents("470a49f6-1dec-4afe-8b61-035d3b2d63b0", { + pageNumber: 0, + pageSize: 3, + ascendingOrder: true +}); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; ```
@@ -1272,7 +1117,7 @@ await client.empathicVoice.configs.deleteConfigVersion("1b60e1a0-cc59-424a-8d2c-
-**id:** `string` — Identifier for a Config. Formatted as a UUID. +**id:** `string` — Identifier for a Chat. Formatted as a UUID.
@@ -1280,13 +1125,7 @@ await client.empathicVoice.configs.deleteConfigVersion("1b60e1a0-cc59-424a-8d2c-
-**version:** `number` - -Version number for a Config. - -Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. - -Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number. +**request:** `Hume.empathicVoice.ChatsListChatEventsRequest`
@@ -1294,7 +1133,7 @@ Version numbers are integer values representing different iterations of the Conf
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ChatsClient.RequestOptions`
@@ -1306,7 +1145,7 @@ Version numbers are integer values representing different iterations of the Conf
-
client.empathicVoice.configs.updateConfigDescription(id, version, { ...params }) -> Hume.ReturnConfig +
client.empathicVoice.chats.getAudio(id) -> Hume.ReturnChatAudioReconstruction
@@ -1318,9 +1157,7 @@ Version numbers are integer values representing different iterations of the Conf
-Updates the description of a **Config**. - -For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). +Fetches the audio of a previous **Chat**. For more details, see our guide on audio reconstruction [here](/docs/speech-to-speech-evi/faq#can-i-access-the-audio-of-previous-conversations-with-evi).
@@ -1335,9 +1172,7 @@ For more details on configuration options and how to configure EVI, see our [con
```typescript -await client.empathicVoice.configs.updateConfigDescription("1b60e1a0-cc59-424a-8d2c-189d354db3f3", 1, { - versionDescription: "This is an updated version_description." -}); +await client.empathicVoice.chats.getAudio("470a49f6-1dec-4afe-8b61-035d3b2d63b0"); ```
@@ -1353,29 +1188,7 @@ await client.empathicVoice.configs.updateConfigDescription("1b60e1a0-cc59-424a-8
-**id:** `string` — Identifier for a Config. Formatted as a UUID. - -
-
- -
-
- -**version:** `number` - -Version number for a Config. - -Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. - -Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number. - -
-
- -
-
- -**request:** `Hume.empathicVoice.PostedConfigVersionDescription` +**id:** `string` — Identifier for a chat. Formatted as a UUID.
@@ -1383,7 +1196,7 @@ Version numbers are integer values representing different iterations of the Conf
-**requestOptions:** `Configs.RequestOptions` +**requestOptions:** `ChatsClient.RequestOptions`
@@ -1395,8 +1208,8 @@ Version numbers are integer values representing different iterations of the Conf
-## EmpathicVoice Prompts -
client.empathicVoice.prompts.listPrompts({ ...params }) -> core.Page<(Hume.ReturnPrompt | undefined), Hume.ReturnPagedPrompts> +## EmpathicVoice Configs +
client.empathicVoice.configs.listConfigs({ ...params }) -> core.Page
@@ -1408,9 +1221,9 @@ Version numbers are integer values representing different iterations of the Conf
-Fetches a paginated list of **Prompts**. +Fetches a paginated list of **Configs**. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1425,18 +1238,18 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -const pageableResponse = await client.empathicVoice.prompts.listPrompts({ +const pageableResponse = await client.empathicVoice.configs.listConfigs({ pageNumber: 0, - pageSize: 2 + pageSize: 1 }); for await (const item of pageableResponse) { console.log(item); } // Or you can manually iterate page-by-page -let page = await client.empathicVoice.prompts.listPrompts({ +let page = await client.empathicVoice.configs.listConfigs({ pageNumber: 0, - pageSize: 2 + pageSize: 1 }); while (page.hasNextPage()) { page = page.getNextPage(); @@ -1459,7 +1272,7 @@ const response = page.response;
-**request:** `Hume.empathicVoice.PromptsListPromptsRequest` +**request:** `Hume.empathicVoice.ConfigsListConfigsRequest`
@@ -1467,7 +1280,7 @@ const response = page.response;
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1479,7 +1292,7 @@ const response = page.response;
-
client.empathicVoice.prompts.createPrompt({ ...params }) -> Hume.ReturnPrompt | undefined +
client.empathicVoice.configs.createConfig({ ...params }) -> Hume.ReturnConfig
@@ -1491,9 +1304,9 @@ const response = page.response;
-Creates a **Prompt** that can be added to an [EVI configuration](/reference/speech-to-speech-evi/configs/create-config). +Creates a **Config** which can be applied to EVI. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1508,9 +1321,36 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.createPrompt({ - name: "Weather Assistant Prompt", - text: "You are an AI weather assistant providing users with accurate and up-to-date weather information. Respond to user queries concisely and clearly. Use simple language and avoid technical jargon. Provide temperature, precipitation, wind conditions, and any weather alerts. Include helpful tips if severe weather is expected." +await client.empathicVoice.configs.createConfig({ + name: "Weather Assistant Config", + prompt: { + id: "af699d45-2985-42cc-91b9-af9e5da3bac5", + version: 0 + }, + eviVersion: "3", + voice: { + provider: "HUME_AI", + name: "Ava Song" + }, + languageModel: { + modelProvider: "ANTHROPIC", + modelResource: "claude-3-7-sonnet-latest", + temperature: 1 + }, + eventMessages: { + onNewChat: { + enabled: false, + text: "" + }, + onInactivityTimeout: { + enabled: false, + text: "" + }, + onMaxDurationTimeout: { + enabled: false, + text: "" + } + } }); ``` @@ -1527,7 +1367,7 @@ await client.empathicVoice.prompts.createPrompt({
-**request:** `Hume.empathicVoice.PostedPrompt` +**request:** `Hume.empathicVoice.PostedConfig`
@@ -1535,7 +1375,7 @@ await client.empathicVoice.prompts.createPrompt({
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1547,7 +1387,7 @@ await client.empathicVoice.prompts.createPrompt({
-
client.empathicVoice.prompts.listPromptVersions(id, { ...params }) -> Hume.ReturnPagedPrompts +
client.empathicVoice.configs.listConfigVersions(id, { ...params }) -> core.Page
@@ -1559,9 +1399,9 @@ await client.empathicVoice.prompts.createPrompt({
-Fetches a list of a **Prompt's** versions. +Fetches a list of a **Config's** versions. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1576,7 +1416,19 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-af9e5da3bac5"); +const pageableResponse = await client.empathicVoice.configs.listConfigVersions("1b60e1a0-cc59-424a-8d2c-189d354db3f3"); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.empathicVoice.configs.listConfigVersions("1b60e1a0-cc59-424a-8d2c-189d354db3f3"); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; ```
@@ -1592,7 +1444,7 @@ await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-a
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -1600,7 +1452,7 @@ await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-a
-**request:** `Hume.empathicVoice.PromptsListPromptVersionsRequest` +**request:** `Hume.empathicVoice.ConfigsListConfigVersionsRequest`
@@ -1608,7 +1460,7 @@ await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-a
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1620,7 +1472,7 @@ await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-a
-
client.empathicVoice.prompts.createPromptVersion(id, { ...params }) -> Hume.ReturnPrompt | undefined +
client.empathicVoice.configs.createConfigVersion(id, { ...params }) -> Hume.ReturnConfig
@@ -1632,9 +1484,9 @@ await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-a
-Updates a **Prompt** by creating a new version of the **Prompt**. +Updates a **Config** by creating a new version of the **Config**. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1649,9 +1501,39 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-af9e5da3bac5", { - text: "You are an updated version of an AI weather assistant providing users with accurate and up-to-date weather information. Respond to user queries concisely and clearly. Use simple language and avoid technical jargon. Provide temperature, precipitation, wind conditions, and any weather alerts. Include helpful tips if severe weather is expected.", - versionDescription: "This is an updated version of the Weather Assistant Prompt." +await client.empathicVoice.configs.createConfigVersion("1b60e1a0-cc59-424a-8d2c-189d354db3f3", { + versionDescription: "This is an updated version of the Weather Assistant Config.", + eviVersion: "3", + prompt: { + id: "af699d45-2985-42cc-91b9-af9e5da3bac5", + version: 0 + }, + voice: { + provider: "HUME_AI", + name: "Ava Song" + }, + languageModel: { + modelProvider: "ANTHROPIC", + modelResource: "claude-3-7-sonnet-latest", + temperature: 1 + }, + ellmModel: { + allowShortResponses: true + }, + eventMessages: { + onNewChat: { + enabled: false, + text: "" + }, + onInactivityTimeout: { + enabled: false, + text: "" + }, + onMaxDurationTimeout: { + enabled: false, + text: "" + } + } }); ``` @@ -1668,7 +1550,7 @@ await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -1676,7 +1558,7 @@ await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-
-**request:** `Hume.empathicVoice.PostedPromptVersion` +**request:** `Hume.empathicVoice.PostedConfigVersion`
@@ -1684,7 +1566,7 @@ await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1696,7 +1578,7 @@ await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-
-
client.empathicVoice.prompts.deletePrompt(id) -> void +
client.empathicVoice.configs.deleteConfig(id) -> void
@@ -1708,9 +1590,9 @@ await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-
-Deletes a **Prompt** and its versions. +Deletes a **Config** and its versions. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1725,7 +1607,7 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da3bac5"); +await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354db3f3"); ```
@@ -1741,7 +1623,7 @@ await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -1749,7 +1631,7 @@ await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1761,7 +1643,7 @@ await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da
-
client.empathicVoice.prompts.updatePromptName(id, { ...params }) -> string +
client.empathicVoice.configs.updateConfigName(id, { ...params }) -> string
@@ -1773,9 +1655,9 @@ await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da
-Updates the name of a **Prompt**. +Updates the name of a **Config**. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1790,8 +1672,8 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9e5da3bac5", { - name: "Updated Weather Assistant Prompt Name" +await client.empathicVoice.configs.updateConfigName("1b60e1a0-cc59-424a-8d2c-189d354db3f3", { + name: "Updated Weather Assistant Config Name" }); ``` @@ -1808,7 +1690,7 @@ await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -1816,7 +1698,7 @@ await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9
-**request:** `Hume.empathicVoice.PostedPromptName` +**request:** `Hume.empathicVoice.PostedConfigName`
@@ -1824,7 +1706,7 @@ await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1836,7 +1718,7 @@ await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9
-
client.empathicVoice.prompts.getPromptVersion(id, version) -> Hume.ReturnPrompt | undefined +
client.empathicVoice.configs.getConfigVersion(id, version) -> Hume.ReturnConfig
@@ -1848,9 +1730,9 @@ await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9
-Fetches a specified version of a **Prompt**. +Fetches a specified version of a **Config**. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1865,7 +1747,7 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.getPromptVersion("af699d45-2985-42cc-91b9-af9e5da3bac5", 0); +await client.empathicVoice.configs.getConfigVersion("1b60e1a0-cc59-424a-8d2c-189d354db3f3", 1); ```
@@ -1881,7 +1763,7 @@ await client.empathicVoice.prompts.getPromptVersion("af699d45-2985-42cc-91b9-af9
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -1891,11 +1773,11 @@ await client.empathicVoice.prompts.getPromptVersion("af699d45-2985-42cc-91b9-af9 **version:** `number` -Version number for a Prompt. +Version number for a Config. -Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. +Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. -Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number. +Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number.
@@ -1903,7 +1785,7 @@ Version numbers are integer values representing different iterations of the Prom
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1915,7 +1797,7 @@ Version numbers are integer values representing different iterations of the Prom
-
client.empathicVoice.prompts.deletePromptVersion(id, version) -> void +
client.empathicVoice.configs.deleteConfigVersion(id, version) -> void
@@ -1927,9 +1809,9 @@ Version numbers are integer values representing different iterations of the Prom
-Deletes a specified version of a **Prompt**. +Deletes a specified version of a **Config**. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -1944,7 +1826,7 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.deletePromptVersion("af699d45-2985-42cc-91b9-af9e5da3bac5", 1); +await client.empathicVoice.configs.deleteConfigVersion("1b60e1a0-cc59-424a-8d2c-189d354db3f3", 1); ```
@@ -1960,7 +1842,7 @@ await client.empathicVoice.prompts.deletePromptVersion("af699d45-2985-42cc-91b9-
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -1970,11 +1852,11 @@ await client.empathicVoice.prompts.deletePromptVersion("af699d45-2985-42cc-91b9- **version:** `number` -Version number for a Prompt. +Version number for a Config. -Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. +Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. -Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number. +Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number.
@@ -1982,7 +1864,7 @@ Version numbers are integer values representing different iterations of the Prom
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -1994,7 +1876,7 @@ Version numbers are integer values representing different iterations of the Prom
-
client.empathicVoice.prompts.updatePromptDescription(id, version, { ...params }) -> Hume.ReturnPrompt | undefined +
client.empathicVoice.configs.updateConfigDescription(id, version, { ...params }) -> Hume.ReturnConfig
@@ -2006,9 +1888,9 @@ Version numbers are integer values representing different iterations of the Prom
-Updates the description of a **Prompt**. +Updates the description of a **Config**. -See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. +For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration).
@@ -2023,7 +1905,7 @@ See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for t
```typescript -await client.empathicVoice.prompts.updatePromptDescription("af699d45-2985-42cc-91b9-af9e5da3bac5", 1, { +await client.empathicVoice.configs.updateConfigDescription("1b60e1a0-cc59-424a-8d2c-189d354db3f3", 1, { versionDescription: "This is an updated version_description." }); @@ -2041,7 +1923,7 @@ await client.empathicVoice.prompts.updatePromptDescription("af699d45-2985-42cc-9
-**id:** `string` — Identifier for a Prompt. Formatted as a UUID. +**id:** `string` — Identifier for a Config. Formatted as a UUID.
@@ -2051,11 +1933,11 @@ await client.empathicVoice.prompts.updatePromptDescription("af699d45-2985-42cc-9 **version:** `number` -Version number for a Prompt. +Version number for a Config. -Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. +Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. -Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number. +Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number.
@@ -2063,7 +1945,7 @@ Version numbers are integer values representing different iterations of the Prom
-**request:** `Hume.empathicVoice.PostedPromptVersionDescription` +**request:** `Hume.empathicVoice.PostedConfigVersionDescription`
@@ -2071,7 +1953,7 @@ Version numbers are integer values representing different iterations of the Prom
-**requestOptions:** `Prompts.RequestOptions` +**requestOptions:** `ConfigsClient.RequestOptions`
@@ -2083,8 +1965,8 @@ Version numbers are integer values representing different iterations of the Prom
-## EmpathicVoice Tools -
client.empathicVoice.tools.listTools({ ...params }) -> core.Page<(Hume.ReturnUserDefinedTool | undefined), Hume.ReturnPagedUserDefinedTools> +## EmpathicVoice Prompts +
client.empathicVoice.prompts.listPrompts({ ...params }) -> core.Page<(Hume.ReturnPrompt | undefined), Hume.ReturnPagedPrompts>
@@ -2096,9 +1978,9 @@ Version numbers are integer values representing different iterations of the Prom
-Fetches a paginated list of **Tools**. +Fetches a paginated list of **Prompts**. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2113,7 +1995,7 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -const pageableResponse = await client.empathicVoice.tools.listTools({ +const pageableResponse = await client.empathicVoice.prompts.listPrompts({ pageNumber: 0, pageSize: 2 }); @@ -2122,7 +2004,7 @@ for await (const item of pageableResponse) { } // Or you can manually iterate page-by-page -let page = await client.empathicVoice.tools.listTools({ +let page = await client.empathicVoice.prompts.listPrompts({ pageNumber: 0, pageSize: 2 }); @@ -2147,7 +2029,7 @@ const response = page.response;
-**request:** `Hume.empathicVoice.ToolsListToolsRequest` +**request:** `Hume.empathicVoice.PromptsListPromptsRequest`
@@ -2155,7 +2037,7 @@ const response = page.response;
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2167,7 +2049,7 @@ const response = page.response;
-
client.empathicVoice.tools.createTool({ ...params }) -> Hume.ReturnUserDefinedTool | undefined +
client.empathicVoice.prompts.createPrompt({ ...params }) -> Hume.ReturnPrompt | undefined
@@ -2179,9 +2061,9 @@ const response = page.response;
-Creates a **Tool** that can be added to an [EVI configuration](/reference/speech-to-speech-evi/configs/create-config). +Creates a **Prompt** that can be added to an [EVI configuration](/reference/speech-to-speech-evi/configs/create-config). -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2196,12 +2078,9 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.createTool({ - name: "get_current_weather", - parameters: "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }", - versionDescription: "Fetches current weather and uses celsius or fahrenheit based on location of user.", - description: "This tool is for getting the current weather.", - fallbackContent: "Unable to fetch current weather." +await client.empathicVoice.prompts.createPrompt({ + name: "Weather Assistant Prompt", + text: "You are an AI weather assistant providing users with accurate and up-to-date weather information. Respond to user queries concisely and clearly. Use simple language and avoid technical jargon. Provide temperature, precipitation, wind conditions, and any weather alerts. Include helpful tips if severe weather is expected." }); ``` @@ -2218,7 +2097,7 @@ await client.empathicVoice.tools.createTool({
-**request:** `Hume.empathicVoice.PostedUserDefinedTool` +**request:** `Hume.empathicVoice.PostedPrompt`
@@ -2226,7 +2105,7 @@ await client.empathicVoice.tools.createTool({
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2238,7 +2117,7 @@ await client.empathicVoice.tools.createTool({
-
client.empathicVoice.tools.listToolVersions(id, { ...params }) -> core.Page<(Hume.ReturnUserDefinedTool | undefined), Hume.ReturnPagedUserDefinedTools> +
client.empathicVoice.prompts.listPromptVersions(id, { ...params }) -> Hume.ReturnPagedPrompts
@@ -2250,9 +2129,9 @@ await client.empathicVoice.tools.createTool({
-Fetches a list of a **Tool's** versions. +Fetches a list of a **Prompt's** versions. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2267,19 +2146,7 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -const pageableResponse = await client.empathicVoice.tools.listToolVersions("00183a3f-79ba-413d-9f3b-609864268bea"); -for await (const item of pageableResponse) { - console.log(item); -} - -// Or you can manually iterate page-by-page -let page = await client.empathicVoice.tools.listToolVersions("00183a3f-79ba-413d-9f3b-609864268bea"); -while (page.hasNextPage()) { - page = page.getNextPage(); -} - -// You can also access the underlying response -const response = page.response; +await client.empathicVoice.prompts.listPromptVersions("af699d45-2985-42cc-91b9-af9e5da3bac5"); ```
@@ -2295,7 +2162,7 @@ const response = page.response;
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2303,7 +2170,7 @@ const response = page.response;
-**request:** `Hume.empathicVoice.ToolsListToolVersionsRequest` +**request:** `Hume.empathicVoice.PromptsListPromptVersionsRequest`
@@ -2311,7 +2178,7 @@ const response = page.response;
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2323,7 +2190,7 @@ const response = page.response;
-
client.empathicVoice.tools.createToolVersion(id, { ...params }) -> Hume.ReturnUserDefinedTool | undefined +
client.empathicVoice.prompts.createPromptVersion(id, { ...params }) -> Hume.ReturnPrompt | undefined
@@ -2335,9 +2202,9 @@ const response = page.response;
-Updates a **Tool** by creating a new version of the **Tool**. +Updates a **Prompt** by creating a new version of the **Prompt**. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2352,11 +2219,9 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-609864268bea", { - parameters: "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\", \"kelvin\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }", - versionDescription: "Fetches current weather and uses celsius, fahrenheit, or kelvin based on location of user.", - fallbackContent: "Unable to fetch current weather.", - description: "This tool is for getting the current weather." +await client.empathicVoice.prompts.createPromptVersion("af699d45-2985-42cc-91b9-af9e5da3bac5", { + text: "You are an updated version of an AI weather assistant providing users with accurate and up-to-date weather information. Respond to user queries concisely and clearly. Use simple language and avoid technical jargon. Provide temperature, precipitation, wind conditions, and any weather alerts. Include helpful tips if severe weather is expected.", + versionDescription: "This is an updated version of the Weather Assistant Prompt." }); ``` @@ -2373,7 +2238,7 @@ await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-6098
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2381,7 +2246,7 @@ await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-6098
-**request:** `Hume.empathicVoice.PostedUserDefinedToolVersion` +**request:** `Hume.empathicVoice.PostedPromptVersion`
@@ -2389,7 +2254,7 @@ await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-6098
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2401,7 +2266,7 @@ await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-6098
-
client.empathicVoice.tools.deleteTool(id) -> void +
client.empathicVoice.prompts.deletePrompt(id) -> void
@@ -2413,9 +2278,9 @@ await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-6098
-Deletes a **Tool** and its versions. +Deletes a **Prompt** and its versions. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2430,7 +2295,7 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268bea"); +await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da3bac5"); ```
@@ -2446,7 +2311,7 @@ await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268be
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2454,7 +2319,7 @@ await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268be
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2466,7 +2331,7 @@ await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268be
-
client.empathicVoice.tools.updateToolName(id, { ...params }) -> string +
client.empathicVoice.prompts.updatePromptName(id, { ...params }) -> string
@@ -2478,9 +2343,9 @@ await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268be
-Updates the name of a **Tool**. +Updates the name of a **Prompt**. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2495,8 +2360,8 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-609864268bea", { - name: "get_current_temperature" +await client.empathicVoice.prompts.updatePromptName("af699d45-2985-42cc-91b9-af9e5da3bac5", { + name: "Updated Weather Assistant Prompt Name" }); ``` @@ -2513,7 +2378,7 @@ await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-6098642
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2521,7 +2386,7 @@ await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-6098642
-**request:** `Hume.empathicVoice.PostedUserDefinedToolName` +**request:** `Hume.empathicVoice.PostedPromptName`
@@ -2529,7 +2394,7 @@ await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-6098642
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2541,7 +2406,7 @@ await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-6098642
-
client.empathicVoice.tools.getToolVersion(id, version) -> Hume.ReturnUserDefinedTool | undefined +
client.empathicVoice.prompts.getPromptVersion(id, version) -> Hume.ReturnPrompt | undefined
@@ -2553,9 +2418,9 @@ await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-6098642
-Fetches a specified version of a **Tool**. +Fetches a specified version of a **Prompt**. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2570,7 +2435,7 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.getToolVersion("00183a3f-79ba-413d-9f3b-609864268bea", 1); +await client.empathicVoice.prompts.getPromptVersion("af699d45-2985-42cc-91b9-af9e5da3bac5", 0); ```
@@ -2586,7 +2451,7 @@ await client.empathicVoice.tools.getToolVersion("00183a3f-79ba-413d-9f3b-6098642
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2596,11 +2461,11 @@ await client.empathicVoice.tools.getToolVersion("00183a3f-79ba-413d-9f3b-6098642 **version:** `number` -Version number for a Tool. +Version number for a Prompt. -Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. +Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. -Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. +Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number.
@@ -2608,7 +2473,7 @@ Version numbers are integer values representing different iterations of the Tool
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2620,7 +2485,7 @@ Version numbers are integer values representing different iterations of the Tool
-
client.empathicVoice.tools.deleteToolVersion(id, version) -> void +
client.empathicVoice.prompts.deletePromptVersion(id, version) -> void
@@ -2632,9 +2497,9 @@ Version numbers are integer values representing different iterations of the Tool
-Deletes a specified version of a **Tool**. +Deletes a specified version of a **Prompt**. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2649,7 +2514,7 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.deleteToolVersion("00183a3f-79ba-413d-9f3b-609864268bea", 1); +await client.empathicVoice.prompts.deletePromptVersion("af699d45-2985-42cc-91b9-af9e5da3bac5", 1); ```
@@ -2665,7 +2530,7 @@ await client.empathicVoice.tools.deleteToolVersion("00183a3f-79ba-413d-9f3b-6098
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2675,11 +2540,11 @@ await client.empathicVoice.tools.deleteToolVersion("00183a3f-79ba-413d-9f3b-6098 **version:** `number` -Version number for a Tool. +Version number for a Prompt. -Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. +Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. -Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. +Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number.
@@ -2687,7 +2552,7 @@ Version numbers are integer values representing different iterations of the Tool
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2699,7 +2564,7 @@ Version numbers are integer values representing different iterations of the Tool
-
client.empathicVoice.tools.updateToolDescription(id, version, { ...params }) -> Hume.ReturnUserDefinedTool | undefined +
client.empathicVoice.prompts.updatePromptDescription(id, version, { ...params }) -> Hume.ReturnPrompt | undefined
@@ -2711,9 +2576,9 @@ Version numbers are integer values representing different iterations of the Tool
-Updates the description of a specified **Tool** version. +Updates the description of a **Prompt**. -Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt.
@@ -2728,8 +2593,8 @@ Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-ca
```typescript -await client.empathicVoice.tools.updateToolDescription("00183a3f-79ba-413d-9f3b-609864268bea", 1, { - versionDescription: "Fetches current temperature, precipitation, wind speed, AQI, and other weather conditions. Uses Celsius, Fahrenheit, or kelvin depending on user's region." +await client.empathicVoice.prompts.updatePromptDescription("af699d45-2985-42cc-91b9-af9e5da3bac5", 1, { + versionDescription: "This is an updated version_description." }); ``` @@ -2746,7 +2611,7 @@ await client.empathicVoice.tools.updateToolDescription("00183a3f-79ba-413d-9f3b-
-**id:** `string` — Identifier for a Tool. Formatted as a UUID. +**id:** `string` — Identifier for a Prompt. Formatted as a UUID.
@@ -2756,11 +2621,11 @@ await client.empathicVoice.tools.updateToolDescription("00183a3f-79ba-413d-9f3b- **version:** `number` -Version number for a Tool. +Version number for a Prompt. -Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. +Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. -Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. +Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number.
@@ -2768,7 +2633,7 @@ Version numbers are integer values representing different iterations of the Tool
-**request:** `Hume.empathicVoice.PostedUserDefinedToolVersionDescription` +**request:** `Hume.empathicVoice.PostedPromptVersionDescription`
@@ -2776,7 +2641,7 @@ Version numbers are integer values representing different iterations of the Tool
-**requestOptions:** `Tools.RequestOptions` +**requestOptions:** `PromptsClient.RequestOptions`
@@ -2788,8 +2653,91 @@ Version numbers are integer values representing different iterations of the Tool
-## Tts -
client.tts.synthesizeJson({ ...params }) -> Hume.ReturnTts +## EmpathicVoice Tools +
client.empathicVoice.tools.listTools({ ...params }) -> core.Page<(Hume.ReturnUserDefinedTool | undefined), Hume.ReturnPagedUserDefinedTools> +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetches a paginated list of **Tools**. + +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +const pageableResponse = await client.empathicVoice.tools.listTools({ + pageNumber: 0, + pageSize: 2 +}); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.empathicVoice.tools.listTools({ + pageNumber: 0, + pageSize: 2 +}); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Hume.empathicVoice.ToolsListToolsRequest` + +
+
+ +
+
+ +**requestOptions:** `ToolsClient.RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.empathicVoice.tools.createTool({ ...params }) -> Hume.ReturnUserDefinedTool | undefined
@@ -2801,9 +2749,9 @@ Version numbers are integer values representing different iterations of the Tool
-Synthesizes one or more input texts into speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. +Creates a **Tool** that can be added to an [EVI configuration](/reference/speech-to-speech-evi/configs/create-config). -The response includes the base64-encoded audio and metadata in JSON format. +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -2818,21 +2766,12 @@ The response includes the base64-encoded audio and metadata in JSON format.
```typescript -await client.tts.synthesizeJson({ - context: { - utterances: [{ - text: "How can people see beauty so differently?", - description: "A curious student with a clear and respectful tone, seeking clarification on Hume's ideas with a straightforward question." - }] - }, - format: { - type: "mp3" - }, - numGenerations: 1, - utterances: [{ - text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", - description: "Middle-aged masculine voice with a clear, rhythmic Scots lilt, rounded vowels, and a warm, steady tone with an articulate, academic quality." - }] +await client.empathicVoice.tools.createTool({ + name: "get_current_weather", + parameters: "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }", + versionDescription: "Fetches current weather and uses celsius or fahrenheit based on location of user.", + description: "This tool is for getting the current weather.", + fallbackContent: "Unable to fetch current weather." }); ``` @@ -2849,7 +2788,7 @@ await client.tts.synthesizeJson({
-**request:** `Hume.PostedTts` +**request:** `Hume.empathicVoice.PostedUserDefinedTool`
@@ -2857,7 +2796,7 @@ await client.tts.synthesizeJson({
-**requestOptions:** `Tts.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -2869,7 +2808,7 @@ await client.tts.synthesizeJson({
-
client.tts.synthesizeFile({ ...params }) -> core.BinaryResponse +
client.empathicVoice.tools.listToolVersions(id, { ...params }) -> core.Page<(Hume.ReturnUserDefinedTool | undefined), Hume.ReturnPagedUserDefinedTools>
@@ -2881,9 +2820,9 @@ await client.tts.synthesizeJson({
-Synthesizes one or more input texts into speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. +Fetches a list of a **Tool's** versions. -The response contains the generated audio file in the requested format. +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -2898,19 +2837,19 @@ The response contains the generated audio file in the requested format.
```typescript -await client.tts.synthesizeFile({ - context: { - generationId: "09ad914d-8e7f-40f8-a279-e34f07f7dab2" - }, - format: { - type: "mp3" - }, - numGenerations: 1, - utterances: [{ - text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", - description: "Middle-aged masculine voice with a clear, rhythmic Scots lilt, rounded vowels, and a warm, steady tone with an articulate, academic quality." - }] -}); +const pageableResponse = await client.empathicVoice.tools.listToolVersions("00183a3f-79ba-413d-9f3b-609864268bea"); +for await (const item of pageableResponse) { + console.log(item); +} + +// Or you can manually iterate page-by-page +let page = await client.empathicVoice.tools.listToolVersions("00183a3f-79ba-413d-9f3b-609864268bea"); +while (page.hasNextPage()) { + page = page.getNextPage(); +} + +// You can also access the underlying response +const response = page.response; ```
@@ -2926,7 +2865,15 @@ await client.tts.synthesizeFile({
-**request:** `Hume.PostedTts` +**id:** `string` — Identifier for a Tool. Formatted as a UUID. + +
+
+ +
+
+ +**request:** `Hume.empathicVoice.ToolsListToolVersionsRequest`
@@ -2934,7 +2881,7 @@ await client.tts.synthesizeFile({
-**requestOptions:** `Tts.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -2946,7 +2893,7 @@ await client.tts.synthesizeFile({
-
client.tts.synthesizeFileStreaming({ ...params }) -> core.BinaryResponse +
client.empathicVoice.tools.createToolVersion(id, { ...params }) -> Hume.ReturnUserDefinedTool | undefined
@@ -2958,7 +2905,9 @@ await client.tts.synthesizeFile({
-Streams synthesized speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. +Updates a **Tool** by creating a new version of the **Tool**. + +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -2973,14 +2922,11 @@ Streams synthesized speech using the specified voice. If no voice is provided, a
```typescript -await client.tts.synthesizeFileStreaming({ - utterances: [{ - text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", - voice: { - name: "Male English Actor", - provider: "HUME_AI" - } - }] +await client.empathicVoice.tools.createToolVersion("00183a3f-79ba-413d-9f3b-609864268bea", { + parameters: "{ \"type\": \"object\", \"properties\": { \"location\": { \"type\": \"string\", \"description\": \"The city and state, e.g. San Francisco, CA\" }, \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\", \"kelvin\"], \"description\": \"The temperature unit to use. Infer this from the users location.\" } }, \"required\": [\"location\", \"format\"] }", + versionDescription: "Fetches current weather and uses celsius, fahrenheit, or kelvin based on location of user.", + fallbackContent: "Unable to fetch current weather.", + description: "This tool is for getting the current weather." }); ``` @@ -2997,7 +2943,15 @@ await client.tts.synthesizeFileStreaming({
-**request:** `Hume.PostedTts` +**id:** `string` — Identifier for a Tool. Formatted as a UUID. + +
+
+ +
+
+ +**request:** `Hume.empathicVoice.PostedUserDefinedToolVersion`
@@ -3005,7 +2959,7 @@ await client.tts.synthesizeFileStreaming({
-**requestOptions:** `Tts.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -3017,7 +2971,7 @@ await client.tts.synthesizeFileStreaming({
-
client.tts.synthesizeJsonStreaming({ ...params }) -> core.Stream +
client.empathicVoice.tools.deleteTool(id) -> void
@@ -3029,9 +2983,9 @@ await client.tts.synthesizeFileStreaming({
-Streams synthesized speech using the specified voice. If no voice is provided, a novel voice will be generated dynamically. Optionally, additional context can be included to influence the speech's style and prosody. +Deletes a **Tool** and its versions. -The response is a stream of JSON objects including audio encoded in base64. +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -3046,18 +3000,7 @@ The response is a stream of JSON objects including audio encoded in base64.
```typescript -const response = await client.tts.synthesizeJsonStreaming({ - utterances: [{ - text: "Beauty is no quality in things themselves: It exists merely in the mind which contemplates them.", - voice: { - name: "Male English Actor", - provider: "HUME_AI" - } - }] -}); -for await (const item of response) { - console.log(item); -} +await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268bea"); ```
@@ -3073,7 +3016,7 @@ for await (const item of response) {
-**request:** `Hume.PostedTts` +**id:** `string` — Identifier for a Tool. Formatted as a UUID.
@@ -3081,7 +3024,7 @@ for await (const item of response) {
-**requestOptions:** `Tts.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -3093,10 +3036,26 @@ for await (const item of response) {
-
client.tts.convertVoiceJson({ ...params }) -> core.Stream +
client.empathicVoice.tools.updateToolName(id, { ...params }) -> string +
+
+ +#### 📝 Description + +
+
+
+Updates the name of a **Tool**. + +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. +
+
+
+
+ #### 🔌 Usage
@@ -3106,10 +3065,9 @@ for await (const item of response) {
```typescript -const response = await client.tts.convertVoiceJson({}); -for await (const item of response) { - console.log(item); -} +await client.empathicVoice.tools.updateToolName("00183a3f-79ba-413d-9f3b-609864268bea", { + name: "get_current_temperature" +}); ```
@@ -3125,7 +3083,15 @@ for await (const item of response) {
-**request:** `Hume.tts.ConvertVoiceJsonRequest` +**id:** `string` — Identifier for a Tool. Formatted as a UUID. + +
+
+ +
+
+ +**request:** `Hume.empathicVoice.PostedUserDefinedToolName`
@@ -3133,7 +3099,7 @@ for await (const item of response) {
-**requestOptions:** `Tts.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -3145,8 +3111,7 @@ for await (const item of response) {
-## Tts Voices -
client.tts.voices.list({ ...params }) -> core.Page +
client.empathicVoice.tools.getToolVersion(id, version) -> Hume.ReturnUserDefinedTool | undefined
@@ -3158,7 +3123,9 @@ for await (const item of response) {
-Lists voices you have saved in your account, or voices from the [Voice Library](https://platform.hume.ai/tts/voice-library). +Fetches a specified version of a **Tool**. + +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -3173,23 +3140,7 @@ Lists voices you have saved in your account, or voices from the [Voice Library](
```typescript -const pageableResponse = await client.tts.voices.list({ - provider: "CUSTOM_VOICE" -}); -for await (const item of pageableResponse) { - console.log(item); -} - -// Or you can manually iterate page-by-page -let page = await client.tts.voices.list({ - provider: "CUSTOM_VOICE" -}); -while (page.hasNextPage()) { - page = page.getNextPage(); -} - -// You can also access the underlying response -const response = page.response; +await client.empathicVoice.tools.getToolVersion("00183a3f-79ba-413d-9f3b-609864268bea", 1); ```
@@ -3205,7 +3156,21 @@ const response = page.response;
-**request:** `Hume.tts.VoicesListRequest` +**id:** `string` — Identifier for a Tool. Formatted as a UUID. + +
+
+ +
+
+ +**version:** `number` + +Version number for a Tool. + +Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. + +Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number.
@@ -3213,7 +3178,7 @@ const response = page.response;
-**requestOptions:** `Voices.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -3225,7 +3190,7 @@ const response = page.response;
-
client.tts.voices.create({ ...params }) -> Hume.ReturnVoice +
client.empathicVoice.tools.deleteToolVersion(id, version) -> void
@@ -3237,9 +3202,9 @@ const response = page.response;
-Saves a new custom voice to your account using the specified TTS generation ID. +Deletes a specified version of a **Tool**. -Once saved, this voice can be reused in subsequent TTS requests, ensuring consistent speech style and prosody. For more details on voice creation, see the [Voices Guide](/docs/text-to-speech-tts/voices). +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -3254,10 +3219,7 @@ Once saved, this voice can be reused in subsequent TTS requests, ensuring consis
```typescript -await client.tts.voices.create({ - generationId: "795c949a-1510-4a80-9646-7d0863b023ab", - name: "David Hume" -}); +await client.empathicVoice.tools.deleteToolVersion("00183a3f-79ba-413d-9f3b-609864268bea", 1); ```
@@ -3273,7 +3235,7 @@ await client.tts.voices.create({
-**request:** `Hume.tts.PostedVoice` +**id:** `string` — Identifier for a Tool. Formatted as a UUID.
@@ -3281,7 +3243,21 @@ await client.tts.voices.create({
-**requestOptions:** `Voices.RequestOptions` +**version:** `number` + +Version number for a Tool. + +Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. + +Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. + +
+
+ +
+
+ +**requestOptions:** `ToolsClient.RequestOptions`
@@ -3293,7 +3269,7 @@ await client.tts.voices.create({
-
client.tts.voices.delete({ ...params }) -> void +
client.empathicVoice.tools.updateToolDescription(id, version, { ...params }) -> Hume.ReturnUserDefinedTool | undefined
@@ -3305,7 +3281,9 @@ await client.tts.voices.create({
-Deletes a previously generated custom voice. +Updates the description of a specified **Tool** version. + +Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI.
@@ -3320,8 +3298,8 @@ Deletes a previously generated custom voice.
```typescript -await client.tts.voices.delete({ - name: "David Hume" +await client.empathicVoice.tools.updateToolDescription("00183a3f-79ba-413d-9f3b-609864268bea", 1, { + versionDescription: "Fetches current temperature, precipitation, wind speed, AQI, and other weather conditions. Uses Celsius, Fahrenheit, or kelvin depending on user's region." }); ``` @@ -3338,7 +3316,29 @@ await client.tts.voices.delete({
-**request:** `Hume.tts.VoicesDeleteRequest` +**id:** `string` — Identifier for a Tool. Formatted as a UUID. + +
+
+ +
+
+ +**version:** `number` + +Version number for a Tool. + +Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. + +Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. + +
+
+ +
+
+ +**request:** `Hume.empathicVoice.PostedUserDefinedToolVersionDescription`
@@ -3346,7 +3346,7 @@ await client.tts.voices.delete({
-**requestOptions:** `Voices.RequestOptions` +**requestOptions:** `ToolsClient.RequestOptions`
@@ -3410,7 +3410,7 @@ await client.expressionMeasurement.batch.listJobs();
-**requestOptions:** `Batch.RequestOptions` +**requestOptions:** `BatchClient.RequestOptions`
@@ -3476,7 +3476,7 @@ await client.expressionMeasurement.batch.startInferenceJob({
-**requestOptions:** `Batch.RequestOptions` +**requestOptions:** `BatchClient.RequestOptions`
@@ -3539,7 +3539,7 @@ await client.expressionMeasurement.batch.getJobDetails("job_id");
-**requestOptions:** `Batch.RequestOptions` +**requestOptions:** `BatchClient.RequestOptions`
@@ -3602,7 +3602,7 @@ await client.expressionMeasurement.batch.getJobPredictions("job_id");
-**requestOptions:** `Batch.RequestOptions` +**requestOptions:** `BatchClient.RequestOptions`
@@ -3667,7 +3667,7 @@ await client.expressionMeasurement.batch.startInferenceJobFromLocalFile({
-**requestOptions:** `Batch.RequestOptions` +**requestOptions:** `BatchClient.RequestOptions`
diff --git a/src/BaseClient.ts b/src/BaseClient.ts index d2142b11..9d459f6b 100644 --- a/src/BaseClient.ts +++ b/src/BaseClient.ts @@ -1,6 +1,8 @@ // This file was auto-generated by Fern from our API Definition. -import type * as core from "./core/index.js"; +import { HeaderAuthProvider } from "./auth/HeaderAuthProvider.js"; +import { mergeHeaders } from "./core/headers.js"; +import * as core from "./core/index.js"; import type * as environments from "./environments.js"; export interface BaseClientOptions { @@ -33,3 +35,50 @@ export interface BaseRequestOptions { /** Additional headers to include in the request. */ headers?: Record | null | undefined>; } + +export type NormalizedClientOptions = T & { + logging: core.logging.Logger; + authProvider?: core.AuthProvider; +}; + +export type NormalizedClientOptionsWithAuth = NormalizedClientOptions & { + authProvider: core.AuthProvider; +}; + +export function normalizeClientOptions(options: T): NormalizedClientOptions { + const headers = mergeHeaders( + { + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "hume", + "X-Fern-SDK-Version": "0.15.10", + "User-Agent": "hume/0.15.10", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + options?.headers, + ); + + return { + ...options, + logging: core.logging.createLogger(options?.logging), + headers, + } as NormalizedClientOptions; +} + +export function normalizeClientOptionsWithAuth( + options: T, +): NormalizedClientOptionsWithAuth { + const normalized = normalizeClientOptions(options) as NormalizedClientOptionsWithAuth; + const normalizedWithNoOpAuthProvider = withNoOpAuthProvider(normalized); + normalized.authProvider ??= new HeaderAuthProvider(normalizedWithNoOpAuthProvider); + return normalized; +} + +function withNoOpAuthProvider( + options: NormalizedClientOptions, +): NormalizedClientOptionsWithAuth { + return { + ...options, + authProvider: new core.NoOpAuthProvider(), + }; +} diff --git a/src/Client.ts b/src/Client.ts index 4aa3714a..77efdc71 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -1,11 +1,10 @@ // This file was auto-generated by Fern from our API Definition. -import { EmpathicVoice } from "./api/resources/empathicVoice/client/Client.js"; -import { ExpressionMeasurement } from "./api/resources/expressionMeasurement/client/Client.js"; -import { Tts } from "./api/resources/tts/client/Client.js"; +import { EmpathicVoiceClient } from "./api/resources/empathicVoice/client/Client.js"; +import { ExpressionMeasurementClient } from "./api/resources/expressionMeasurement/client/Client.js"; +import { TtsClient } from "./api/resources/tts/client/Client.js"; import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; -import { mergeHeaders } from "./core/headers.js"; -import * as core from "./core/index.js"; +import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "./BaseClient.js"; export declare namespace HumeClient { export interface Options extends BaseClientOptions {} @@ -14,38 +13,24 @@ export declare namespace HumeClient { } export class HumeClient { - protected readonly _options: HumeClient.Options; - protected _empathicVoice: EmpathicVoice | undefined; - protected _tts: Tts | undefined; - protected _expressionMeasurement: ExpressionMeasurement | undefined; - - constructor(_options: HumeClient.Options = {}) { - this._options = { - ..._options, - logging: core.logging.createLogger(_options?.logging), - headers: mergeHeaders( - { - "X-Fern-Language": "JavaScript", - "X-Fern-SDK-Name": "hume", - "X-Fern-SDK-Version": "0.15.9", - "User-Agent": "hume/0.15.9", - "X-Fern-Runtime": core.RUNTIME.type, - "X-Fern-Runtime-Version": core.RUNTIME.version, - }, - _options?.headers, - ), - }; + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _tts: TtsClient | undefined; + protected _empathicVoice: EmpathicVoiceClient | undefined; + protected _expressionMeasurement: ExpressionMeasurementClient | undefined; + + constructor(options: HumeClient.Options = {}) { + this._options = normalizeClientOptionsWithAuth(options); } - public get empathicVoice(): EmpathicVoice { - return (this._empathicVoice ??= new EmpathicVoice(this._options)); + public get tts(): TtsClient { + return (this._tts ??= new TtsClient(this._options)); } - public get tts(): Tts { - return (this._tts ??= new Tts(this._options)); + public get empathicVoice(): EmpathicVoiceClient { + return (this._empathicVoice ??= new EmpathicVoiceClient(this._options)); } - public get expressionMeasurement(): ExpressionMeasurement { - return (this._expressionMeasurement ??= new ExpressionMeasurement(this._options)); + public get expressionMeasurement(): ExpressionMeasurementClient { + return (this._expressionMeasurement ??= new ExpressionMeasurementClient(this._options)); } } diff --git a/src/api/resources/empathicVoice/client/Client.ts b/src/api/resources/empathicVoice/client/Client.ts index c0618b2c..82e29b69 100644 --- a/src/api/resources/empathicVoice/client/Client.ts +++ b/src/api/resources/empathicVoice/client/Client.ts @@ -1,57 +1,58 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions } from "../../../../BaseClient.js"; -import { Chat } from "../resources/chat/client/Client.js"; -import { ChatGroups } from "../resources/chatGroups/client/Client.js"; -import { Chats } from "../resources/chats/client/Client.js"; -import { Configs } from "../resources/configs/client/Client.js"; -import { ControlPlane } from "../resources/controlPlane/client/Client.js"; -import { Prompts } from "../resources/prompts/client/Client.js"; -import { Tools } from "../resources/tools/client/Client.js"; - -export declare namespace EmpathicVoice { +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../BaseClient.js"; +import { ChatClient } from "../resources/chat/client/Client.js"; +import { ChatGroupsClient } from "../resources/chatGroups/client/Client.js"; +import { ChatsClient } from "../resources/chats/client/Client.js"; +import { ConfigsClient } from "../resources/configs/client/Client.js"; +import { ControlPlaneClient } from "../resources/controlPlane/client/Client.js"; +import { PromptsClient } from "../resources/prompts/client/Client.js"; +import { ToolsClient } from "../resources/tools/client/Client.js"; + +export declare namespace EmpathicVoiceClient { export interface Options extends BaseClientOptions {} } -export class EmpathicVoice { - protected readonly _options: EmpathicVoice.Options; - protected _controlPlane: ControlPlane | undefined; - protected _chatGroups: ChatGroups | undefined; - protected _chats: Chats | undefined; - protected _configs: Configs | undefined; - protected _prompts: Prompts | undefined; - protected _tools: Tools | undefined; - protected _chat: Chat | undefined; +export class EmpathicVoiceClient { + protected readonly _options: NormalizedClientOptions; + protected _controlPlane: ControlPlaneClient | undefined; + protected _chatGroups: ChatGroupsClient | undefined; + protected _chats: ChatsClient | undefined; + protected _configs: ConfigsClient | undefined; + protected _prompts: PromptsClient | undefined; + protected _tools: ToolsClient | undefined; + protected _chat: ChatClient | undefined; - constructor(_options: EmpathicVoice.Options = {}) { - this._options = _options; + constructor(options: EmpathicVoiceClient.Options = {}) { + this._options = normalizeClientOptions(options); } - public get controlPlane(): ControlPlane { - return (this._controlPlane ??= new ControlPlane(this._options)); + public get controlPlane(): ControlPlaneClient { + return (this._controlPlane ??= new ControlPlaneClient(this._options)); } - public get chatGroups(): ChatGroups { - return (this._chatGroups ??= new ChatGroups(this._options)); + public get chatGroups(): ChatGroupsClient { + return (this._chatGroups ??= new ChatGroupsClient(this._options)); } - public get chats(): Chats { - return (this._chats ??= new Chats(this._options)); + public get chats(): ChatsClient { + return (this._chats ??= new ChatsClient(this._options)); } - public get configs(): Configs { - return (this._configs ??= new Configs(this._options)); + public get configs(): ConfigsClient { + return (this._configs ??= new ConfigsClient(this._options)); } - public get prompts(): Prompts { - return (this._prompts ??= new Prompts(this._options)); + public get prompts(): PromptsClient { + return (this._prompts ??= new PromptsClient(this._options)); } - public get tools(): Tools { - return (this._tools ??= new Tools(this._options)); + public get tools(): ToolsClient { + return (this._tools ??= new ToolsClient(this._options)); } - public get chat(): Chat { - return (this._chat ??= new Chat(this._options)); + public get chat(): ChatClient { + return (this._chat ??= new ChatClient(this._options)); } } diff --git a/src/api/resources/empathicVoice/resources/chat/client/Client.ts.diff b/src/api/resources/empathicVoice/resources/chat/client/Client.ts.diff deleted file mode 100644 index 8c42162a..00000000 --- a/src/api/resources/empathicVoice/resources/chat/client/Client.ts.diff +++ /dev/null @@ -1,182 +0,0 @@ -diff --git a/src/api/resources/empathicVoice/resources/chat/client/Client.ts b/src/api/resources/empathicVoice/resources/chat/client/Client.ts -index 210954d..37d17b0 100644 ---- a/src/api/resources/empathicVoice/resources/chat/client/Client.ts -+++ b/src/api/resources/empathicVoice/resources/chat/client/Client.ts -@@ -1,36 +1,26 @@ --/** THIS FILE IS MANUALLY MAINTAINED: see .fernignore */ -+// This file was auto-generated by Fern from our API Definition. - --import * as environments from "../../../../../../environments.js"; -+import type { BaseClientOptions } from "../../../../../../BaseClient.js"; -+import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; - import * as core from "../../../../../../core/index.js"; --import * as Hume from "../../../../../index.js"; --import { mergeOnlyDefinedHeaders, mergeHeaders } from "../../../../../../core/headers.js"; -+import * as environments from "../../../../../../environments.js"; - import * as serializers from "../../../../../../serialization/index.js"; -+import type * as Hume from "../../../../../index.js"; - import { ChatSocket } from "./Socket.js"; - - export declare namespace Chat { -- export interface Options { -- environment?: core.Supplier; -- /** Specify a custom URL to connect the client to. */ -- baseUrl?: core.Supplier; -- apiKey?: core.Supplier; -- /** Additional headers to include in requests. */ -- headers?: Record | null | undefined>; -- } -+ export interface Options extends BaseClientOptions {} - - export interface ConnectArgs { - accessToken?: string | undefined; -+ allowConnection?: boolean | undefined; - configId?: string | undefined; -- configVersion?: string | number | undefined; -+ configVersion?: number | undefined; - eventLimit?: number | undefined; - resumedChatGroupId?: string | undefined; - verboseTranscription?: boolean | undefined; -- allowConnection?: boolean | undefined; -- /** @deprecated Use sessionSettings.voiceId instead */ -- voiceId?: string | undefined; - apiKey?: string | undefined; -- sessionSettings?: Hume.empathicVoice.ConnectSessionSettings; -- /** Extra query parameters sent at WebSocket connection */ -- queryParams?: Record; -+ sessionSettings: Hume.empathicVoice.ConnectSessionSettings; - /** Arbitrary headers to send with the websocket connect request. */ - headers?: Record; - /** Enable debug mode on the websocket. Defaults to false. */ -@@ -47,91 +37,69 @@ export class Chat { - this._options = _options; - } - -- public connect(args: Chat.ConnectArgs = {}): ChatSocket { -+ public async connect(args: Chat.ConnectArgs): Promise { - const { - accessToken, -+ allowConnection, - configId, - configVersion, - eventLimit, - resumedChatGroupId, - verboseTranscription, -- voiceId, - apiKey, - sessionSettings, -- queryParams, - headers, - debug, - reconnectAttempts, -- allowConnection, - } = args; - const _queryParams: Record = {}; -- - if (accessToken != null) { -- _queryParams["access_token"] = accessToken; -+ _queryParams.access_token = accessToken; -+ } -+ -+ if (allowConnection != null) { -+ _queryParams.allow_connection = allowConnection.toString(); - } - - if (configId != null) { -- _queryParams["config_id"] = configId; -+ _queryParams.config_id = configId; - } - - if (configVersion != null) { -- _queryParams["config_version"] = -- typeof configVersion === "number" ? configVersion.toString() : configVersion; -+ _queryParams.config_version = configVersion.toString(); - } - - if (eventLimit != null) { -- _queryParams["event_limit"] = eventLimit.toString(); -+ _queryParams.event_limit = eventLimit.toString(); - } - - if (resumedChatGroupId != null) { -- _queryParams["resumed_chat_group_id"] = resumedChatGroupId; -+ _queryParams.resumed_chat_group_id = resumedChatGroupId; - } - - if (verboseTranscription != null) { -- _queryParams["verbose_transcription"] = verboseTranscription.toString(); -- } -- -- if (voiceId != null) { -- _queryParams["voice_id"] = voiceId; -+ _queryParams.verbose_transcription = verboseTranscription.toString(); - } - - if (apiKey != null) { -- _queryParams["api_key"] = apiKey; -- } -- -- if (allowConnection != null) { -- _queryParams["allow_connection"] = allowConnection === true ? "true" : "false"; -+ _queryParams.api_key = apiKey; - } - -- if (sessionSettings != null) { -- _queryParams["session_settings"] = serializers.empathicVoice.ConnectSessionSettings.jsonOrThrow( -- sessionSettings, -- { -- unrecognizedObjectKeys: "passthrough", -- allowUnrecognizedUnionMembers: true, -- allowUnrecognizedEnumValues: true, -- omitUndefined: true, -- breadcrumbsPrefix: ["request", "sessionSettings"], -- }, -- ); -- } -- -- // Merge in any additional query parameters -- if (queryParams != null) { -- for (const [name, value] of Object.entries(queryParams)) { -- _queryParams[name] = value; -- } -- } -- -- let _headers: Record = mergeHeaders( -- mergeOnlyDefinedHeaders({ ...this._getCustomAuthorizationHeaders() }), -+ _queryParams.session_settings = serializers.empathicVoice.ConnectSessionSettings.jsonOrThrow(sessionSettings, { -+ unrecognizedObjectKeys: "passthrough", -+ allowUnrecognizedUnionMembers: true, -+ allowUnrecognizedEnumValues: true, -+ omitUndefined: true, -+ breadcrumbsPrefix: ["request", "sessionSettings"], -+ }); -+ const _headers: Record = mergeHeaders( -+ mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - headers, - ); -- - const socket = new core.ReconnectingWebSocket({ - url: core.url.join( -- core.Supplier.get(this._options["baseUrl"]) ?? -- (core.Supplier.get(this._options["environment"]) ?? environments.HumeEnvironment.Prod).evi, -+ (await core.Supplier.get(this._options.baseUrl)) ?? -+ ((await core.Supplier.get(this._options.environment)) ?? environments.HumeEnvironment.Prod).evi, - "/chat", - ), - protocols: [], -@@ -142,12 +110,8 @@ export class Chat { - return new ChatSocket({ socket }); - } - -- protected _getCustomAuthorizationHeaders(): Record { -- const apiKeyValue = core.Supplier.get(this._options.apiKey); -- // This `authHeaderValue` is manually added as if you don't provide it it will -- // be omitted from the headers which means it won't reach the logic in ws.ts that -- // extracts values from the headers and adds them to query parameters. -- const authHeaderValue = core.Supplier.get(this._options.headers?.authorization); -- return { "X-Hume-Api-Key": apiKeyValue, Authorization: authHeaderValue }; -+ protected async _getCustomAuthorizationHeaders(): Promise> { -+ const apiKeyValue = await core.Supplier.get(this._options.apiKey); -+ return { "X-Hume-Api-Key": apiKeyValue }; - } - } diff --git a/src/api/resources/empathicVoice/resources/chat/client/Socket.ts.diff b/src/api/resources/empathicVoice/resources/chat/client/Socket.ts.diff deleted file mode 100644 index 7c0bd3a4..00000000 --- a/src/api/resources/empathicVoice/resources/chat/client/Socket.ts.diff +++ /dev/null @@ -1,139 +0,0 @@ -diff --git a/src/api/resources/empathicVoice/resources/chat/client/Socket.ts b/src/api/resources/empathicVoice/resources/chat/client/Socket.ts -index a55c7b5..0f2f2a5 100644 ---- a/src/api/resources/empathicVoice/resources/chat/client/Socket.ts -+++ b/src/api/resources/empathicVoice/resources/chat/client/Socket.ts -@@ -1,17 +1,17 @@ --/** THIS FILE IS MANUALLY MAINTAINED: see .fernignore */ -+// This file was auto-generated by Fern from our API Definition. - - import * as core from "../../../../../../core/index.js"; --import * as Hume from "../../../../../index.js"; --import { PublishEvent } from "../../../../../../serialization/resources/empathicVoice/resources/chat/types/PublishEvent.js"; - import { fromJson } from "../../../../../../core/json.js"; - import * as serializers from "../../../../../../serialization/index.js"; -+import { PublishEvent } from "../../../../../../serialization/resources/empathicVoice/resources/chat/types/PublishEvent.js"; -+import type * as Hume from "../../../../../index.js"; - - export declare namespace ChatSocket { - export interface Args { - socket: core.ReconnectingWebSocket; - } - -- export type Response = Hume.empathicVoice.SubscribeEvent & { receivedAt: Date }; -+ export type Response = Hume.empathicVoice.SubscribeEvent; - type EventHandlers = { - open?: () => void; - message?: (message: Response) => void; -@@ -37,10 +37,7 @@ export class ChatSocket { - omitUndefined: true, - }); - if (parsedResponse.ok) { -- this.eventHandlers.message?.({ -- ...parsedResponse.value, -- receivedAt: new Date(), -- }); -+ this.eventHandlers.message?.(parsedResponse.value); - } else { - this.eventHandlers.error?.(new Error("Received unknown message type")); - } -@@ -92,86 +89,6 @@ export class ChatSocket { - this.socket.send(JSON.stringify(jsonPayload)); - } - -- /** -- * Send audio input -- */ -- public sendAudioInput(message: Omit): void { -- this.sendPublish({ -- type: "audio_input", -- ...message, -- }); -- } -- -- /** -- * Send session settings -- */ -- public sendSessionSettings(message: Omit = {}): void { -- this.sendPublish({ -- type: "session_settings", -- ...message, -- }); -- } -- -- /** -- * Send assistant input -- */ -- public sendAssistantInput(message: Omit): void { -- this.sendPublish({ -- type: "assistant_input", -- ...message, -- }); -- } -- -- /** -- * Send pause assistant message -- */ -- public pauseAssistant(message: Omit = {}): void { -- this.sendPublish({ -- type: "pause_assistant_message", -- ...message, -- }); -- } -- -- /** -- * Send resume assistant message -- */ -- public resumeAssistant(message: Omit = {}): void { -- this.sendPublish({ -- type: "resume_assistant_message", -- ...message, -- }); -- } -- -- /** -- * Send tool response message -- */ -- public sendToolResponseMessage(message: Omit): void { -- this.sendPublish({ -- type: "tool_response", -- ...message, -- }); -- } -- -- /** -- * Send tool error message -- */ -- public sendToolErrorMessage(message: Omit): void { -- this.sendPublish({ -- type: "tool_error", -- ...message, -- }); -- } -- -- /** -- * Send text input -- */ -- public sendUserInput(text: string): void { -- this.sendPublish({ -- type: "user_input", -- text, -- }); -- } -- - /** Connect to the websocket and register event handlers. */ - public connect(): ChatSocket { - this.socket.reconnect(); -@@ -213,13 +130,6 @@ export class ChatSocket { - }); - } - -- /** -- * @deprecated Use waitForOpen() instead -- */ -- public async tillSocketOpen(): Promise { -- return this.waitForOpen(); -- } -- - /** Asserts that the websocket is open. */ - private assertSocketIsOpen(): void { - if (!this.socket) { diff --git a/src/api/resources/empathicVoice/resources/chat/client/index.ts.diff b/src/api/resources/empathicVoice/resources/chat/client/index.ts.diff deleted file mode 100644 index 4976271e..00000000 --- a/src/api/resources/empathicVoice/resources/chat/client/index.ts.diff +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/src/api/resources/empathicVoice/resources/chat/client/index.ts b/src/api/resources/empathicVoice/resources/chat/client/index.ts -index 38d5d5f..cb0ff5c 100644 ---- a/src/api/resources/empathicVoice/resources/chat/client/index.ts -+++ b/src/api/resources/empathicVoice/resources/chat/client/index.ts -@@ -1,3 +1 @@ --/** THIS FILE IS MANUALLY MAINTAINED: see .fernignore */ --export { ChatSocket } from "./Socket.js"; --export { Chat } from "./Client.js"; -+export {}; diff --git a/src/api/resources/empathicVoice/resources/chat/index.ts.diff b/src/api/resources/empathicVoice/resources/chat/index.ts.diff deleted file mode 100644 index e1f02d67..00000000 --- a/src/api/resources/empathicVoice/resources/chat/index.ts.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/api/resources/empathicVoice/resources/chat/index.ts b/src/api/resources/empathicVoice/resources/chat/index.ts -index a2f33dc..d9adb1a 100644 ---- a/src/api/resources/empathicVoice/resources/chat/index.ts -+++ b/src/api/resources/empathicVoice/resources/chat/index.ts -@@ -1,7 +1,2 @@ --export * from "./types/index.js"; - export * from "./client/index.js"; --/** -- * @deprecated Use `Hume.empathicVoice.SubscribeEvent` instead. -- * This type alias will be removed in a future version. -- */ --export type { SubscribeEvent } from "./types/SubscribeEvent.js"; -+export * from "./types/index.js"; diff --git a/src/api/resources/empathicVoice/resources/chatGroups/client/Client.ts b/src/api/resources/empathicVoice/resources/chatGroups/client/Client.ts index 0345fbab..b1ebeb5c 100644 --- a/src/api/resources/empathicVoice/resources/chatGroups/client/Client.ts +++ b/src/api/resources/empathicVoice/resources/chatGroups/client/Client.ts @@ -1,31 +1,32 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import * as environments from "../../../../../../environments.js"; import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import * as Hume from "../../../../../index.js"; -export declare namespace ChatGroups { +export declare namespace ChatGroupsClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class ChatGroups { - protected readonly _options: ChatGroups.Options; +export class ChatGroupsClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: ChatGroups.Options = {}) { - this._options = _options; + constructor(options: ChatGroupsClient.Options = {}) { + this._options = normalizeClientOptions(options); } /** * Fetches a paginated list of **Chat Groups**. * * @param {Hume.empathicVoice.ChatGroupsListChatGroupsRequest} request - * @param {ChatGroups.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatGroupsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -39,7 +40,7 @@ export class ChatGroups { */ public async listChatGroups( request: Hume.empathicVoice.ChatGroupsListChatGroupsRequest = {}, - requestOptions?: ChatGroups.RequestOptions, + requestOptions?: ChatGroupsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -61,7 +62,6 @@ export class ChatGroups { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -120,6 +120,11 @@ export class ChatGroups { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/chat_groups."); case "unknown": @@ -149,7 +154,7 @@ export class ChatGroups { * * @param {string} id - Identifier for a Chat Group. Formatted as a UUID. * @param {Hume.empathicVoice.ChatGroupsGetChatGroupRequest} request - * @param {ChatGroups.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatGroupsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -163,7 +168,7 @@ export class ChatGroups { public getChatGroup( id: string, request: Hume.empathicVoice.ChatGroupsGetChatGroupRequest = {}, - requestOptions?: ChatGroups.RequestOptions, + requestOptions?: ChatGroupsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getChatGroup(id, request, requestOptions)); } @@ -171,7 +176,7 @@ export class ChatGroups { private async __getChatGroup( id: string, request: Hume.empathicVoice.ChatGroupsGetChatGroupRequest = {}, - requestOptions?: ChatGroups.RequestOptions, + requestOptions?: ChatGroupsClient.RequestOptions, ): Promise> { const { status, pageSize, pageNumber, ascendingOrder } = request; const _queryParams: Record = {}; @@ -191,11 +196,7 @@ export class ChatGroups { _queryParams.ascending_order = ascendingOrder.toString(); } - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -253,6 +254,11 @@ export class ChatGroups { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/chat_groups/{id}."); case "unknown": @@ -268,7 +274,7 @@ export class ChatGroups { * * @param {string} id - Identifier for a Chat Group. Formatted as a UUID. * @param {Hume.empathicVoice.ChatGroupsGetAudioRequest} request - * @param {ChatGroups.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatGroupsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -282,7 +288,7 @@ export class ChatGroups { public getAudio( id: string, request: Hume.empathicVoice.ChatGroupsGetAudioRequest = {}, - requestOptions?: ChatGroups.RequestOptions, + requestOptions?: ChatGroupsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getAudio(id, request, requestOptions)); } @@ -290,7 +296,7 @@ export class ChatGroups { private async __getAudio( id: string, request: Hume.empathicVoice.ChatGroupsGetAudioRequest = {}, - requestOptions?: ChatGroups.RequestOptions, + requestOptions?: ChatGroupsClient.RequestOptions, ): Promise> { const { pageNumber, pageSize, ascendingOrder } = request; const _queryParams: Record = {}; @@ -306,11 +312,7 @@ export class ChatGroups { _queryParams.ascending_order = ascendingOrder.toString(); } - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -368,6 +370,11 @@ export class ChatGroups { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/chat_groups/{id}/audio."); case "unknown": @@ -383,7 +390,7 @@ export class ChatGroups { * * @param {string} id - Identifier for a Chat Group. Formatted as a UUID. * @param {Hume.empathicVoice.ChatGroupsListChatGroupEventsRequest} request - * @param {ChatGroups.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatGroupsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -397,7 +404,7 @@ export class ChatGroups { public async listChatGroupEvents( id: string, request: Hume.empathicVoice.ChatGroupsListChatGroupEventsRequest = {}, - requestOptions?: ChatGroups.RequestOptions, + requestOptions?: ChatGroupsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -416,7 +423,6 @@ export class ChatGroups { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -475,6 +481,11 @@ export class ChatGroups { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling GET /v0/evi/chat_groups/{id}/events.", @@ -500,9 +511,4 @@ export class ChatGroups { }, }); } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/empathicVoice/resources/chats/client/Client.ts b/src/api/resources/empathicVoice/resources/chats/client/Client.ts index 1939868f..73931342 100644 --- a/src/api/resources/empathicVoice/resources/chats/client/Client.ts +++ b/src/api/resources/empathicVoice/resources/chats/client/Client.ts @@ -1,31 +1,32 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import * as environments from "../../../../../../environments.js"; import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import * as Hume from "../../../../../index.js"; -export declare namespace Chats { +export declare namespace ChatsClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Chats { - protected readonly _options: Chats.Options; +export class ChatsClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: Chats.Options = {}) { - this._options = _options; + constructor(options: ChatsClient.Options = {}) { + this._options = normalizeClientOptions(options); } /** * Fetches a paginated list of **Chats**. * * @param {Hume.empathicVoice.ChatsListChatsRequest} request - * @param {Chats.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -38,7 +39,7 @@ export class Chats { */ public async listChats( request: Hume.empathicVoice.ChatsListChatsRequest = {}, - requestOptions?: Chats.RequestOptions, + requestOptions?: ChatsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -63,7 +64,6 @@ export class Chats { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -122,6 +122,11 @@ export class Chats { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/chats."); case "unknown": @@ -151,7 +156,7 @@ export class Chats { * * @param {string} id - Identifier for a Chat. Formatted as a UUID. * @param {Hume.empathicVoice.ChatsListChatEventsRequest} request - * @param {Chats.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -165,7 +170,7 @@ export class Chats { public async listChatEvents( id: string, request: Hume.empathicVoice.ChatsListChatEventsRequest = {}, - requestOptions?: Chats.RequestOptions, + requestOptions?: ChatsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -184,7 +189,6 @@ export class Chats { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -243,6 +247,11 @@ export class Chats { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/chats/{id}."); case "unknown": @@ -271,7 +280,7 @@ export class Chats { * Fetches the audio of a previous **Chat**. For more details, see our guide on audio reconstruction [here](/docs/speech-to-speech-evi/faq#can-i-access-the-audio-of-previous-conversations-with-evi). * * @param {string} id - Identifier for a chat. Formatted as a UUID. - * @param {Chats.RequestOptions} requestOptions - Request-specific configuration. + * @param {ChatsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -280,20 +289,16 @@ export class Chats { */ public getAudio( id: string, - requestOptions?: Chats.RequestOptions, + requestOptions?: ChatsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getAudio(id, requestOptions)); } private async __getAudio( id: string, - requestOptions?: Chats.RequestOptions, + requestOptions?: ChatsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -351,6 +356,11 @@ export class Chats { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/chats/{id}/audio."); case "unknown": @@ -360,9 +370,4 @@ export class Chats { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/empathicVoice/resources/configs/client/Client.ts b/src/api/resources/empathicVoice/resources/configs/client/Client.ts index 632a8127..4e8633ef 100644 --- a/src/api/resources/empathicVoice/resources/configs/client/Client.ts +++ b/src/api/resources/empathicVoice/resources/configs/client/Client.ts @@ -1,24 +1,25 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import * as environments from "../../../../../../environments.js"; import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import * as Hume from "../../../../../index.js"; -export declare namespace Configs { +export declare namespace ConfigsClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Configs { - protected readonly _options: Configs.Options; +export class ConfigsClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: Configs.Options = {}) { - this._options = _options; + constructor(options: ConfigsClient.Options = {}) { + this._options = normalizeClientOptions(options); } /** @@ -27,7 +28,7 @@ export class Configs { * For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). * * @param {Hume.empathicVoice.ConfigsListConfigsRequest} request - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -39,7 +40,7 @@ export class Configs { */ public async listConfigs( request: Hume.empathicVoice.ConfigsListConfigsRequest = {}, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -61,7 +62,6 @@ export class Configs { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -120,6 +120,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/configs."); case "unknown": @@ -150,7 +155,7 @@ export class Configs { * For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). * * @param {Hume.empathicVoice.PostedConfig} request - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -189,20 +194,16 @@ export class Configs { */ public createConfig( request: Hume.empathicVoice.PostedConfig, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__createConfig(request, requestOptions)); } private async __createConfig( request: Hume.empathicVoice.PostedConfig, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -266,6 +267,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/evi/configs."); case "unknown": @@ -283,7 +289,7 @@ export class Configs { * * @param {string} id - Identifier for a Config. Formatted as a UUID. * @param {Hume.empathicVoice.ConfigsListConfigVersionsRequest} request - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -293,7 +299,7 @@ export class Configs { public async listConfigVersions( id: string, request: Hume.empathicVoice.ConfigsListConfigVersionsRequest = {}, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -312,7 +318,6 @@ export class Configs { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -371,6 +376,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/configs/{id}."); case "unknown": @@ -402,7 +412,7 @@ export class Configs { * * @param {string} id - Identifier for a Config. Formatted as a UUID. * @param {Hume.empathicVoice.PostedConfigVersion} request - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -445,7 +455,7 @@ export class Configs { public createConfigVersion( id: string, request: Hume.empathicVoice.PostedConfigVersion, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__createConfigVersion(id, request, requestOptions)); } @@ -453,13 +463,9 @@ export class Configs { private async __createConfigVersion( id: string, request: Hume.empathicVoice.PostedConfigVersion, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -523,6 +529,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/evi/configs/{id}."); case "unknown": @@ -539,26 +550,22 @@ export class Configs { * For more details on configuration options and how to configure EVI, see our [configuration guide](/docs/speech-to-speech-evi/configuration). * * @param {string} id - Identifier for a Config. Formatted as a UUID. - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * * @example * await client.empathicVoice.configs.deleteConfig("1b60e1a0-cc59-424a-8d2c-189d354db3f3") */ - public deleteConfig(id: string, requestOptions?: Configs.RequestOptions): core.HttpResponsePromise { + public deleteConfig(id: string, requestOptions?: ConfigsClient.RequestOptions): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__deleteConfig(id, requestOptions)); } private async __deleteConfig( id: string, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -607,6 +614,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling DELETE /v0/evi/configs/{id}."); case "unknown": @@ -624,7 +636,7 @@ export class Configs { * * @param {string} id - Identifier for a Config. Formatted as a UUID. * @param {Hume.empathicVoice.PostedConfigName} request - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -636,7 +648,7 @@ export class Configs { public updateConfigName( id: string, request: Hume.empathicVoice.PostedConfigName, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__updateConfigName(id, request, requestOptions)); } @@ -644,13 +656,9 @@ export class Configs { private async __updateConfigName( id: string, request: Hume.empathicVoice.PostedConfigName, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -706,6 +714,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling PATCH /v0/evi/configs/{id}."); case "unknown": @@ -727,7 +740,7 @@ export class Configs { * Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. * * Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number. - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -737,7 +750,7 @@ export class Configs { public getConfigVersion( id: string, version: number, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getConfigVersion(id, version, requestOptions)); } @@ -745,13 +758,9 @@ export class Configs { private async __getConfigVersion( id: string, version: number, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -809,6 +818,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling GET /v0/evi/configs/{id}/version/{version}.", @@ -832,7 +846,7 @@ export class Configs { * Configs, Prompts, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine configurations and revert to previous versions if needed. * * Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number. - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -842,7 +856,7 @@ export class Configs { public deleteConfigVersion( id: string, version: number, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__deleteConfigVersion(id, version, requestOptions)); } @@ -850,13 +864,9 @@ export class Configs { private async __deleteConfigVersion( id: string, version: number, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -905,6 +915,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling DELETE /v0/evi/configs/{id}/version/{version}.", @@ -929,7 +944,7 @@ export class Configs { * * Version numbers are integer values representing different iterations of the Config. Each update to the Config increments its version number. * @param {Hume.empathicVoice.PostedConfigVersionDescription} request - * @param {Configs.RequestOptions} requestOptions - Request-specific configuration. + * @param {ConfigsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -942,7 +957,7 @@ export class Configs { id: string, version: number, request: Hume.empathicVoice.PostedConfigVersionDescription = {}, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise( this.__updateConfigDescription(id, version, request, requestOptions), @@ -953,13 +968,9 @@ export class Configs { id: string, version: number, request: Hume.empathicVoice.PostedConfigVersionDescription = {}, - requestOptions?: Configs.RequestOptions, + requestOptions?: ConfigsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -1023,6 +1034,11 @@ export class Configs { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling PATCH /v0/evi/configs/{id}/version/{version}.", @@ -1034,9 +1050,4 @@ export class Configs { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts.diff b/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts.diff deleted file mode 100644 index 61a11b81..00000000 --- a/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts b/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts -index e092f57..14ce2a9 100644 ---- a/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts -+++ b/src/api/resources/empathicVoice/resources/controlPlane/client/Client.ts -@@ -144,7 +144,7 @@ export class ControlPlane { - url: core.url.join( - (await core.Supplier.get(this._options.baseUrl)) ?? - ((await core.Supplier.get(this._options.environment)) ?? environments.HumeEnvironment.Prod).evi, -- `/chat/${core.url.encodePathParam(chat_id)}/connect`, -+ `/chat/${core.url.encodePathParam(chatId)}/connect`, - ), - protocols: [], - queryParameters: _queryParams, -@@ -153,12 +153,9 @@ export class ControlPlane { - }); - return new ControlPlaneSocket({ socket }); - } -- protected async _getCustomAuthorizationHeaders(): Promise> { -- const apiKeyValue = core.Supplier.get(this._options.apiKey); -- // This `authHeaderValue` is manually added as if you don't provide it it will -- // be omitted from the headers which means it won't reach the logic in ws.ts that -- // extracts values from the headers and adds them to query parameters. -- const authHeaderValue = core.Supplier.get(this._options.headers?.authorization); -- return { "X-Hume-Api-Key": apiKeyValue, Authorization: authHeaderValue }; -+ -+ protected async _getCustomAuthorizationHeaders(): Promise> { -+ const apiKeyValue = await core.Supplier.get(this._options.apiKey); -+ return { "X-Hume-Api-Key": apiKeyValue }; - } - } diff --git a/src/api/resources/empathicVoice/resources/prompts/client/Client.ts b/src/api/resources/empathicVoice/resources/prompts/client/Client.ts index 63debd8e..f1acca8b 100644 --- a/src/api/resources/empathicVoice/resources/prompts/client/Client.ts +++ b/src/api/resources/empathicVoice/resources/prompts/client/Client.ts @@ -1,24 +1,25 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import * as environments from "../../../../../../environments.js"; import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import * as Hume from "../../../../../index.js"; -export declare namespace Prompts { +export declare namespace PromptsClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Prompts { - protected readonly _options: Prompts.Options; +export class PromptsClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: Prompts.Options = {}) { - this._options = _options; + constructor(options: PromptsClient.Options = {}) { + this._options = normalizeClientOptions(options); } /** @@ -27,7 +28,7 @@ export class Prompts { * See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. * * @param {Hume.empathicVoice.PromptsListPromptsRequest} request - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -39,7 +40,7 @@ export class Prompts { */ public async listPrompts( request: Hume.empathicVoice.PromptsListPromptsRequest = {}, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async ( @@ -61,7 +62,6 @@ export class Prompts { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -120,6 +120,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/prompts."); case "unknown": @@ -150,7 +155,7 @@ export class Prompts { * See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. * * @param {Hume.empathicVoice.PostedPrompt} request - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -162,20 +167,16 @@ export class Prompts { */ public createPrompt( request: Hume.empathicVoice.PostedPrompt, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__createPrompt(request, requestOptions)); } private async __createPrompt( request: Hume.empathicVoice.PostedPrompt, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -239,6 +240,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/evi/prompts."); case "unknown": @@ -256,7 +262,7 @@ export class Prompts { * * @param {string} id - Identifier for a Prompt. Formatted as a UUID. * @param {Hume.empathicVoice.PromptsListPromptVersionsRequest} request - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -266,7 +272,7 @@ export class Prompts { public listPromptVersions( id: string, request: Hume.empathicVoice.PromptsListPromptVersionsRequest = {}, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__listPromptVersions(id, request, requestOptions)); } @@ -274,7 +280,7 @@ export class Prompts { private async __listPromptVersions( id: string, request: Hume.empathicVoice.PromptsListPromptVersionsRequest = {}, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { const { pageNumber, pageSize, restrictToMostRecent } = request; const _queryParams: Record = {}; @@ -290,11 +296,7 @@ export class Prompts { _queryParams.restrict_to_most_recent = restrictToMostRecent.toString(); } - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -352,6 +354,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/prompts/{id}."); case "unknown": @@ -369,7 +376,7 @@ export class Prompts { * * @param {string} id - Identifier for a Prompt. Formatted as a UUID. * @param {Hume.empathicVoice.PostedPromptVersion} request - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -382,7 +389,7 @@ export class Prompts { public createPromptVersion( id: string, request: Hume.empathicVoice.PostedPromptVersion, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__createPromptVersion(id, request, requestOptions)); } @@ -390,13 +397,9 @@ export class Prompts { private async __createPromptVersion( id: string, request: Hume.empathicVoice.PostedPromptVersion, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -460,6 +463,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/evi/prompts/{id}."); case "unknown": @@ -476,26 +484,22 @@ export class Prompts { * See our [prompting guide](/docs/speech-to-speech-evi/guides/phone-calling) for tips on crafting your system prompt. * * @param {string} id - Identifier for a Prompt. Formatted as a UUID. - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * * @example * await client.empathicVoice.prompts.deletePrompt("af699d45-2985-42cc-91b9-af9e5da3bac5") */ - public deletePrompt(id: string, requestOptions?: Prompts.RequestOptions): core.HttpResponsePromise { + public deletePrompt(id: string, requestOptions?: PromptsClient.RequestOptions): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__deletePrompt(id, requestOptions)); } private async __deletePrompt( id: string, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -544,6 +548,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling DELETE /v0/evi/prompts/{id}."); case "unknown": @@ -561,7 +570,7 @@ export class Prompts { * * @param {string} id - Identifier for a Prompt. Formatted as a UUID. * @param {Hume.empathicVoice.PostedPromptName} request - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -573,7 +582,7 @@ export class Prompts { public updatePromptName( id: string, request: Hume.empathicVoice.PostedPromptName, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__updatePromptName(id, request, requestOptions)); } @@ -581,13 +590,9 @@ export class Prompts { private async __updatePromptName( id: string, request: Hume.empathicVoice.PostedPromptName, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -643,6 +648,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling PATCH /v0/evi/prompts/{id}."); case "unknown": @@ -664,7 +674,7 @@ export class Prompts { * Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. * * Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number. - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -674,7 +684,7 @@ export class Prompts { public getPromptVersion( id: string, version: number, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getPromptVersion(id, version, requestOptions)); } @@ -682,13 +692,9 @@ export class Prompts { private async __getPromptVersion( id: string, version: number, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -746,6 +752,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling GET /v0/evi/prompts/{id}/version/{version}.", @@ -769,7 +780,7 @@ export class Prompts { * Prompts, Configs, Custom Voices, and Tools are versioned. This versioning system supports iterative development, allowing you to progressively refine prompts and revert to previous versions if needed. * * Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number. - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -779,7 +790,7 @@ export class Prompts { public deletePromptVersion( id: string, version: number, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__deletePromptVersion(id, version, requestOptions)); } @@ -787,13 +798,9 @@ export class Prompts { private async __deletePromptVersion( id: string, version: number, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -842,6 +849,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling DELETE /v0/evi/prompts/{id}/version/{version}.", @@ -866,7 +878,7 @@ export class Prompts { * * Version numbers are integer values representing different iterations of the Prompt. Each update to the Prompt increments its version number. * @param {Hume.empathicVoice.PostedPromptVersionDescription} request - * @param {Prompts.RequestOptions} requestOptions - Request-specific configuration. + * @param {PromptsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -879,7 +891,7 @@ export class Prompts { id: string, version: number, request: Hume.empathicVoice.PostedPromptVersionDescription = {}, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise( this.__updatePromptDescription(id, version, request, requestOptions), @@ -890,13 +902,9 @@ export class Prompts { id: string, version: number, request: Hume.empathicVoice.PostedPromptVersionDescription = {}, - requestOptions?: Prompts.RequestOptions, + requestOptions?: PromptsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -960,6 +968,11 @@ export class Prompts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling PATCH /v0/evi/prompts/{id}/version/{version}.", @@ -971,9 +984,4 @@ export class Prompts { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/empathicVoice/resources/tools/client/Client.ts b/src/api/resources/empathicVoice/resources/tools/client/Client.ts index f01bc4b9..8d4c4c9e 100644 --- a/src/api/resources/empathicVoice/resources/tools/client/Client.ts +++ b/src/api/resources/empathicVoice/resources/tools/client/Client.ts @@ -1,24 +1,25 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import * as environments from "../../../../../../environments.js"; import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import * as Hume from "../../../../../index.js"; -export declare namespace Tools { +export declare namespace ToolsClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Tools { - protected readonly _options: Tools.Options; +export class ToolsClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: Tools.Options = {}) { - this._options = _options; + constructor(options: ToolsClient.Options = {}) { + this._options = normalizeClientOptions(options); } /** @@ -27,7 +28,7 @@ export class Tools { * Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. * * @param {Hume.empathicVoice.ToolsListToolsRequest} request - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -39,7 +40,7 @@ export class Tools { */ public async listTools( request: Hume.empathicVoice.ToolsListToolsRequest = {}, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise< core.Page > { @@ -63,7 +64,6 @@ export class Tools { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -122,6 +122,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/tools."); case "unknown": @@ -155,7 +160,7 @@ export class Tools { * Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. * * @param {Hume.empathicVoice.PostedUserDefinedTool} request - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -170,20 +175,16 @@ export class Tools { */ public createTool( request: Hume.empathicVoice.PostedUserDefinedTool, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__createTool(request, requestOptions)); } private async __createTool( request: Hume.empathicVoice.PostedUserDefinedTool, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -247,6 +248,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/evi/tools."); case "unknown": @@ -264,7 +270,7 @@ export class Tools { * * @param {string} id - Identifier for a Tool. Formatted as a UUID. * @param {Hume.empathicVoice.ToolsListToolVersionsRequest} request - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -274,7 +280,7 @@ export class Tools { public async listToolVersions( id: string, request: Hume.empathicVoice.ToolsListToolVersionsRequest = {}, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise< core.Page > { @@ -295,7 +301,6 @@ export class Tools { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -354,6 +359,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/evi/tools/{id}."); case "unknown": @@ -388,7 +398,7 @@ export class Tools { * * @param {string} id - Identifier for a Tool. Formatted as a UUID. * @param {Hume.empathicVoice.PostedUserDefinedToolVersion} request - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -403,7 +413,7 @@ export class Tools { public createToolVersion( id: string, request: Hume.empathicVoice.PostedUserDefinedToolVersion, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__createToolVersion(id, request, requestOptions)); } @@ -411,13 +421,9 @@ export class Tools { private async __createToolVersion( id: string, request: Hume.empathicVoice.PostedUserDefinedToolVersion, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -481,6 +487,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/evi/tools/{id}."); case "unknown": @@ -497,23 +508,22 @@ export class Tools { * Refer to our [tool use](/docs/speech-to-speech-evi/features/tool-use#function-calling) guide for comprehensive instructions on defining and integrating tools into EVI. * * @param {string} id - Identifier for a Tool. Formatted as a UUID. - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * * @example * await client.empathicVoice.tools.deleteTool("00183a3f-79ba-413d-9f3b-609864268bea") */ - public deleteTool(id: string, requestOptions?: Tools.RequestOptions): core.HttpResponsePromise { + public deleteTool(id: string, requestOptions?: ToolsClient.RequestOptions): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__deleteTool(id, requestOptions)); } - private async __deleteTool(id: string, requestOptions?: Tools.RequestOptions): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + private async __deleteTool( + id: string, + requestOptions?: ToolsClient.RequestOptions, + ): Promise> { + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -562,6 +572,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling DELETE /v0/evi/tools/{id}."); case "unknown": @@ -579,7 +594,7 @@ export class Tools { * * @param {string} id - Identifier for a Tool. Formatted as a UUID. * @param {Hume.empathicVoice.PostedUserDefinedToolName} request - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -591,7 +606,7 @@ export class Tools { public updateToolName( id: string, request: Hume.empathicVoice.PostedUserDefinedToolName, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__updateToolName(id, request, requestOptions)); } @@ -599,13 +614,9 @@ export class Tools { private async __updateToolName( id: string, request: Hume.empathicVoice.PostedUserDefinedToolName, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -661,6 +672,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling PATCH /v0/evi/tools/{id}."); case "unknown": @@ -682,7 +698,7 @@ export class Tools { * Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. * * Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -692,7 +708,7 @@ export class Tools { public getToolVersion( id: string, version: number, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getToolVersion(id, version, requestOptions)); } @@ -700,13 +716,9 @@ export class Tools { private async __getToolVersion( id: string, version: number, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -764,6 +776,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling GET /v0/evi/tools/{id}/version/{version}.", @@ -787,7 +804,7 @@ export class Tools { * Tools, Configs, Custom Voices, and Prompts are versioned. This versioning system supports iterative development, allowing you to progressively refine tools and revert to previous versions if needed. * * Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -797,7 +814,7 @@ export class Tools { public deleteToolVersion( id: string, version: number, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__deleteToolVersion(id, version, requestOptions)); } @@ -805,13 +822,9 @@ export class Tools { private async __deleteToolVersion( id: string, version: number, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -860,6 +873,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling DELETE /v0/evi/tools/{id}/version/{version}.", @@ -884,7 +902,7 @@ export class Tools { * * Version numbers are integer values representing different iterations of the Tool. Each update to the Tool increments its version number. * @param {Hume.empathicVoice.PostedUserDefinedToolVersionDescription} request - * @param {Tools.RequestOptions} requestOptions - Request-specific configuration. + * @param {ToolsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.empathicVoice.BadRequestError} * @@ -897,7 +915,7 @@ export class Tools { id: string, version: number, request: Hume.empathicVoice.PostedUserDefinedToolVersionDescription = {}, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__updateToolDescription(id, version, request, requestOptions)); } @@ -906,13 +924,9 @@ export class Tools { id: string, version: number, request: Hume.empathicVoice.PostedUserDefinedToolVersionDescription = {}, - requestOptions?: Tools.RequestOptions, + requestOptions?: ToolsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -976,6 +990,11 @@ export class Tools { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError( "Timeout exceeded when calling PATCH /v0/evi/tools/{id}/version/{version}.", @@ -987,9 +1006,4 @@ export class Tools { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/expressionMeasurement/client/Client.ts b/src/api/resources/expressionMeasurement/client/Client.ts index 920ea7c4..a668cdeb 100644 --- a/src/api/resources/expressionMeasurement/client/Client.ts +++ b/src/api/resources/expressionMeasurement/client/Client.ts @@ -1,21 +1,22 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions } from "../../../../BaseClient.js"; -import { Batch } from "../resources/batch/client/Client.js"; +import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../BaseClient.js"; +import { BatchClient } from "../resources/batch/client/Client.js"; -export declare namespace ExpressionMeasurement { +export declare namespace ExpressionMeasurementClient { export interface Options extends BaseClientOptions {} } -export class ExpressionMeasurement { - protected readonly _options: ExpressionMeasurement.Options; - protected _batch: Batch | undefined; +export class ExpressionMeasurementClient { + protected readonly _options: NormalizedClientOptionsWithAuth; + protected _batch: BatchClient | undefined; - constructor(_options: ExpressionMeasurement.Options = {}) { - this._options = _options; + constructor(options: ExpressionMeasurementClient.Options = {}) { + this._options = normalizeClientOptionsWithAuth(options); } - public get batch(): Batch { - return (this._batch ??= new Batch(this._options)); + public get batch(): BatchClient { + return (this._batch ??= new BatchClient(this._options)); } } diff --git a/src/api/resources/expressionMeasurement/resources/batch/client/Client.ts b/src/api/resources/expressionMeasurement/resources/batch/client/Client.ts index 61428d5c..02b7fdb8 100644 --- a/src/api/resources/expressionMeasurement/resources/batch/client/Client.ts +++ b/src/api/resources/expressionMeasurement/resources/batch/client/Client.ts @@ -1,6 +1,7 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; +import { type NormalizedClientOptionsWithAuth, normalizeClientOptionsWithAuth } from "../../../../../../BaseClient.js"; import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import { toJson } from "../../../../../../core/json.js"; @@ -9,38 +10,38 @@ import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import type * as Hume from "../../../../../index.js"; -export declare namespace Batch { +export declare namespace BatchClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Batch { - protected readonly _options: Batch.Options; +export class BatchClient { + protected readonly _options: NormalizedClientOptionsWithAuth; - constructor(_options: Batch.Options = {}) { - this._options = _options; + constructor(options: BatchClient.Options = {}) { + this._options = normalizeClientOptionsWithAuth(options); } /** * Sort and filter jobs. * * @param {Hume.expressionMeasurement.batch.BatchListJobsRequest} request - * @param {Batch.RequestOptions} requestOptions - Request-specific configuration. + * @param {BatchClient.RequestOptions} requestOptions - Request-specific configuration. * * @example * await client.expressionMeasurement.batch.listJobs() */ public listJobs( request: Hume.expressionMeasurement.batch.BatchListJobsRequest = {}, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__listJobs(request, requestOptions)); } private async __listJobs( request: Hume.expressionMeasurement.batch.BatchListJobsRequest = {}, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): Promise> { const { limit, status, when, timestampMs, sortBy, direction } = request; const _queryParams: Record = {}; @@ -89,11 +90,7 @@ export class Batch { }); } - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -137,6 +134,11 @@ export class Batch { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/batch/jobs."); case "unknown": @@ -151,7 +153,7 @@ export class Batch { * Start a new measurement inference job. * * @param {Hume.expressionMeasurement.batch.InferenceBaseRequest} request - * @param {Batch.RequestOptions} requestOptions - Request-specific configuration. + * @param {BatchClient.RequestOptions} requestOptions - Request-specific configuration. * * @example * await client.expressionMeasurement.batch.startInferenceJob({ @@ -161,20 +163,16 @@ export class Batch { */ public startInferenceJob( request: Hume.expressionMeasurement.batch.InferenceBaseRequest, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__startInferenceJob(request, requestOptions)); } private async __startInferenceJob( request: Hume.expressionMeasurement.batch.InferenceBaseRequest, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -224,6 +222,11 @@ export class Batch { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/batch/jobs."); case "unknown": @@ -238,27 +241,23 @@ export class Batch { * Get the request details and state of a given job. * * @param {string} id - The unique identifier for the job. - * @param {Batch.RequestOptions} requestOptions - Request-specific configuration. + * @param {BatchClient.RequestOptions} requestOptions - Request-specific configuration. * * @example * await client.expressionMeasurement.batch.getJobDetails("job_id") */ public getJobDetails( id: string, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getJobDetails(id, requestOptions)); } private async __getJobDetails( id: string, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -302,6 +301,11 @@ export class Batch { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/batch/jobs/{id}."); case "unknown": @@ -316,27 +320,23 @@ export class Batch { * Get the JSON predictions of a completed inference job. * * @param {string} id - The unique identifier for the job. - * @param {Batch.RequestOptions} requestOptions - Request-specific configuration. + * @param {BatchClient.RequestOptions} requestOptions - Request-specific configuration. * * @example * await client.expressionMeasurement.batch.getJobPredictions("job_id") */ public getJobPredictions( id: string, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getJobPredictions(id, requestOptions)); } private async __getJobPredictions( id: string, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -380,6 +380,11 @@ export class Batch { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/batch/jobs/{id}/predictions."); case "unknown": @@ -395,20 +400,16 @@ export class Batch { */ public getJobArtifacts( id: string, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__getJobArtifacts(id, requestOptions)); } private async __getJobArtifacts( id: string, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -444,6 +445,11 @@ export class Batch { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/batch/jobs/{id}/artifacts."); case "unknown": @@ -458,7 +464,7 @@ export class Batch { * Start a new batch inference job. * * @param {Hume.expressionMeasurement.batch.BatchStartInferenceJobFromLocalFileRequest} request - * @param {Batch.RequestOptions} requestOptions - Request-specific configuration. + * @param {BatchClient.RequestOptions} requestOptions - Request-specific configuration. * * @example * import { createReadStream } from "fs"; @@ -468,14 +474,14 @@ export class Batch { */ public startInferenceJobFromLocalFile( request: Hume.expressionMeasurement.batch.BatchStartInferenceJobFromLocalFileRequest, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__startInferenceJobFromLocalFile(request, requestOptions)); } private async __startInferenceJobFromLocalFile( request: Hume.expressionMeasurement.batch.BatchStartInferenceJobFromLocalFileRequest, - requestOptions?: Batch.RequestOptions, + requestOptions?: BatchClient.RequestOptions, ): Promise> { const _request = await core.newFormData(); if (request.json != null) { @@ -495,12 +501,11 @@ export class Batch { } const _maybeEncodedRequest = await _request.getRequest(); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, this._options?.headers, - mergeOnlyDefinedHeaders({ - ...(await this._getCustomAuthorizationHeaders()), - ..._maybeEncodedRequest.headers, - }), + mergeOnlyDefinedHeaders({ ..._maybeEncodedRequest.headers }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -549,6 +554,11 @@ export class Batch { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/batch/jobs."); case "unknown": @@ -558,9 +568,4 @@ export class Batch { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/expressionMeasurement/resources/stream/resources/stream/client/Client.ts b/src/api/resources/expressionMeasurement/resources/stream/resources/stream/client/Client.ts index 930acf28..c4792dea 100644 --- a/src/api/resources/expressionMeasurement/resources/stream/resources/stream/client/Client.ts +++ b/src/api/resources/expressionMeasurement/resources/stream/resources/stream/client/Client.ts @@ -1,12 +1,13 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions } from "../../../../../../../../BaseClient.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../../../BaseClient.js"; import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../../../core/headers.js"; import * as core from "../../../../../../../../core/index.js"; import * as environments from "../../../../../../../../environments.js"; import { StreamSocket } from "./Socket.js"; -export declare namespace Stream { +export declare namespace StreamClient { export interface Options extends BaseClientOptions {} export interface ConnectArgs { @@ -20,20 +21,19 @@ export declare namespace Stream { } } -export class Stream { - protected readonly _options: Stream.Options; +export class StreamClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: Stream.Options = {}) { - this._options = _options; + constructor(options: StreamClient.Options = {}) { + this._options = normalizeClientOptions(options); } - public async connect(args: Stream.ConnectArgs): Promise { + public async connect(args: StreamClient.ConnectArgs): Promise { const { headers, debug, reconnectAttempts } = args; + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); const _headers: Record = mergeHeaders( - mergeOnlyDefinedHeaders({ - ...(await this._getCustomAuthorizationHeaders()), - "X-Hume-Api-Key": args["X-Hume-Api-Key"], - }), + _authRequest.headers, + mergeOnlyDefinedHeaders({ "X-Hume-Api-Key": args["X-Hume-Api-Key"] }), headers, ); const socket = new core.ReconnectingWebSocket({ @@ -49,9 +49,4 @@ export class Stream { }); return new StreamSocket({ socket }); } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/tts/client/Client.ts b/src/api/resources/tts/client/Client.ts index 1eb1a9b9..493d387c 100644 --- a/src/api/resources/tts/client/Client.ts +++ b/src/api/resources/tts/client/Client.ts @@ -1,6 +1,7 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../BaseClient.js"; import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../core/headers.js"; import * as core from "../../../../core/index.js"; import { toJson } from "../../../../core/json.js"; @@ -8,30 +9,30 @@ import * as environments from "../../../../environments.js"; import * as errors from "../../../../errors/index.js"; import * as serializers from "../../../../serialization/index.js"; import * as Hume from "../../../index.js"; -import { StreamInput } from "../resources/streamInput/client/Client.js"; -import { Voices } from "../resources/voices/client/Client.js"; +import { StreamInputClient } from "../resources/streamInput/client/Client.js"; +import { VoicesClient } from "../resources/voices/client/Client.js"; -export declare namespace Tts { +export declare namespace TtsClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Tts { - protected readonly _options: Tts.Options; - protected _voices: Voices | undefined; - protected _streamInput: StreamInput | undefined; +export class TtsClient { + protected readonly _options: NormalizedClientOptions; + protected _voices: VoicesClient | undefined; + protected _streamInput: StreamInputClient | undefined; - constructor(_options: Tts.Options = {}) { - this._options = _options; + constructor(options: TtsClient.Options = {}) { + this._options = normalizeClientOptions(options); } - public get voices(): Voices { - return (this._voices ??= new Voices(this._options)); + public get voices(): VoicesClient { + return (this._voices ??= new VoicesClient(this._options)); } - public get streamInput(): StreamInput { - return (this._streamInput ??= new StreamInput(this._options)); + public get streamInput(): StreamInputClient { + return (this._streamInput ??= new StreamInputClient(this._options)); } /** @@ -40,7 +41,7 @@ export class Tts { * The response includes the base64-encoded audio and metadata in JSON format. * * @param {Hume.tts.PostedTts} request - * @param {Tts.RequestOptions} requestOptions - Request-specific configuration. + * @param {TtsClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.tts.UnprocessableEntityError} * @@ -64,20 +65,16 @@ export class Tts { */ public synthesizeJson( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__synthesizeJson(request, requestOptions)); } private async __synthesizeJson( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -141,6 +138,11 @@ export class Tts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts."); case "unknown": @@ -159,20 +161,16 @@ export class Tts { */ public synthesizeFile( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__synthesizeFile(request, requestOptions)); } private async __synthesizeFile( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -228,6 +226,11 @@ export class Tts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts/file."); case "unknown": @@ -244,20 +247,16 @@ export class Tts { */ public synthesizeFileStreaming( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__synthesizeFileStreaming(request, requestOptions)); } private async __synthesizeFileStreaming( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -313,6 +312,11 @@ export class Tts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts/stream/file."); case "unknown": @@ -330,20 +334,16 @@ export class Tts { */ public synthesizeJsonStreaming( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): core.HttpResponsePromise> { return core.HttpResponsePromise.fromPromise(this.__synthesizeJsonStreaming(request, requestOptions)); } private async __synthesizeJsonStreaming( request: Hume.tts.PostedTts, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): Promise>> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -418,6 +418,11 @@ export class Tts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts/stream/json."); case "unknown": @@ -433,14 +438,14 @@ export class Tts { */ public convertVoiceFile( request: Hume.tts.ConvertVoiceFileRequest, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__convertVoiceFile(request, requestOptions)); } private async __convertVoiceFile( request: Hume.tts.ConvertVoiceFileRequest, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): Promise> { const _request = await core.newFormData(); if (request.stripHeaders != null) { @@ -502,10 +507,7 @@ export class Tts { const _maybeEncodedRequest = await _request.getRequest(); const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ - ...(await this._getCustomAuthorizationHeaders()), - ..._maybeEncodedRequest.headers, - }), + mergeOnlyDefinedHeaders({ ..._maybeEncodedRequest.headers }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -560,6 +562,11 @@ export class Tts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts/voice_conversion/file."); case "unknown": @@ -572,14 +579,14 @@ export class Tts { public convertVoiceJson( request: Hume.tts.ConvertVoiceJsonRequest, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): core.HttpResponsePromise> { return core.HttpResponsePromise.fromPromise(this.__convertVoiceJson(request, requestOptions)); } private async __convertVoiceJson( request: Hume.tts.ConvertVoiceJsonRequest, - requestOptions?: Tts.RequestOptions, + requestOptions?: TtsClient.RequestOptions, ): Promise>> { const _request = await core.newFormData(); if (request.stripHeaders != null) { @@ -644,10 +651,7 @@ export class Tts { const _maybeEncodedRequest = await _request.getRequest(); const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ - ...(await this._getCustomAuthorizationHeaders()), - ..._maybeEncodedRequest.headers, - }), + mergeOnlyDefinedHeaders({ ..._maybeEncodedRequest.headers }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -721,6 +725,11 @@ export class Tts { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts/voice_conversion/json."); case "unknown": @@ -730,9 +739,4 @@ export class Tts { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/api/resources/tts/resources/streamInput/client/Client.ts.diff b/src/api/resources/tts/resources/streamInput/client/Client.ts.diff deleted file mode 100644 index c6ba326c..00000000 --- a/src/api/resources/tts/resources/streamInput/client/Client.ts.diff +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/src/api/resources/tts/resources/streamInput/client/Client.ts b/src/api/resources/tts/resources/streamInput/client/Client.ts -index 55e3be8..3870133 100644 ---- a/src/api/resources/tts/resources/streamInput/client/Client.ts -+++ b/src/api/resources/tts/resources/streamInput/client/Client.ts -@@ -125,12 +125,8 @@ export class StreamInput { - return new StreamInputSocket({ socket }); - } - -- protected async _getCustomAuthorizationHeaders(): Promise> { -- const apiKeyValue = core.Supplier.get(this._options.apiKey); -- // This `authHeaderValue` is manually added as if you don't provide it it will -- // be omitted from the headers which means it won't reach the logic in ws.ts that -- // extracts values from the headers and adds them to query parameters. -- const authHeaderValue = core.Supplier.get(this._options.headers?.authorization); -- return { "X-Hume-Api-Key": apiKeyValue, Authorization: authHeaderValue }; -+ protected async _getCustomAuthorizationHeaders(): Promise> { -+ const apiKeyValue = await core.Supplier.get(this._options.apiKey); -+ return { "X-Hume-Api-Key": apiKeyValue }; - } - } diff --git a/src/api/resources/tts/resources/voices/client/Client.ts b/src/api/resources/tts/resources/voices/client/Client.ts index d15f5ed1..8d339a6e 100644 --- a/src/api/resources/tts/resources/voices/client/Client.ts +++ b/src/api/resources/tts/resources/voices/client/Client.ts @@ -1,31 +1,32 @@ // This file was auto-generated by Fern from our API Definition. import type { BaseClientOptions, BaseRequestOptions } from "../../../../../../BaseClient.js"; -import { mergeHeaders, mergeOnlyDefinedHeaders } from "../../../../../../core/headers.js"; +import { type NormalizedClientOptions, normalizeClientOptions } from "../../../../../../BaseClient.js"; +import { mergeHeaders } from "../../../../../../core/headers.js"; import * as core from "../../../../../../core/index.js"; import * as environments from "../../../../../../environments.js"; import * as errors from "../../../../../../errors/index.js"; import * as serializers from "../../../../../../serialization/index.js"; import * as Hume from "../../../../../index.js"; -export declare namespace Voices { +export declare namespace VoicesClient { export interface Options extends BaseClientOptions {} export interface RequestOptions extends BaseRequestOptions {} } -export class Voices { - protected readonly _options: Voices.Options; +export class VoicesClient { + protected readonly _options: NormalizedClientOptions; - constructor(_options: Voices.Options = {}) { - this._options = _options; + constructor(options: VoicesClient.Options = {}) { + this._options = normalizeClientOptions(options); } /** * Lists voices you have saved in your account, or voices from the [Voice Library](https://platform.hume.ai/tts/voice-library). * * @param {Hume.tts.VoicesListRequest} request - * @param {Voices.RequestOptions} requestOptions - Request-specific configuration. + * @param {VoicesClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.tts.BadRequestError} * @@ -36,7 +37,7 @@ export class Voices { */ public async list( request: Hume.tts.VoicesListRequest, - requestOptions?: Voices.RequestOptions, + requestOptions?: VoicesClient.RequestOptions, ): Promise> { const list = core.HttpResponsePromise.interceptFunction( async (request: Hume.tts.VoicesListRequest): Promise> => { @@ -64,7 +65,6 @@ export class Voices { } const _headers: core.Fetcher.Args["headers"] = mergeHeaders( this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), requestOptions?.headers, ); const _response = await (this._options.fetcher ?? core.fetcher)({ @@ -123,6 +123,11 @@ export class Voices { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling GET /v0/tts/voices."); case "unknown": @@ -153,7 +158,7 @@ export class Voices { * Once saved, this voice can be reused in subsequent TTS requests, ensuring consistent speech style and prosody. For more details on voice creation, see the [Voices Guide](/docs/text-to-speech-tts/voices). * * @param {Hume.tts.PostedVoice} request - * @param {Voices.RequestOptions} requestOptions - Request-specific configuration. + * @param {VoicesClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.tts.UnprocessableEntityError} * @@ -165,20 +170,16 @@ export class Voices { */ public create( request: Hume.tts.PostedVoice, - requestOptions?: Voices.RequestOptions, + requestOptions?: VoicesClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__create(request, requestOptions)); } private async __create( request: Hume.tts.PostedVoice, - requestOptions?: Voices.RequestOptions, + requestOptions?: VoicesClient.RequestOptions, ): Promise> { - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -242,6 +243,11 @@ export class Voices { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling POST /v0/tts/voices."); case "unknown": @@ -256,7 +262,7 @@ export class Voices { * Deletes a previously generated custom voice. * * @param {Hume.tts.VoicesDeleteRequest} request - * @param {Voices.RequestOptions} requestOptions - Request-specific configuration. + * @param {VoicesClient.RequestOptions} requestOptions - Request-specific configuration. * * @throws {@link Hume.tts.BadRequestError} * @@ -267,23 +273,19 @@ export class Voices { */ public delete( request: Hume.tts.VoicesDeleteRequest, - requestOptions?: Voices.RequestOptions, + requestOptions?: VoicesClient.RequestOptions, ): core.HttpResponsePromise { return core.HttpResponsePromise.fromPromise(this.__delete(request, requestOptions)); } private async __delete( request: Hume.tts.VoicesDeleteRequest, - requestOptions?: Voices.RequestOptions, + requestOptions?: VoicesClient.RequestOptions, ): Promise> { const { name } = request; const _queryParams: Record = {}; _queryParams.name = name; - const _headers: core.Fetcher.Args["headers"] = mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ ...(await this._getCustomAuthorizationHeaders()) }), - requestOptions?.headers, - ); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders(this._options?.headers, requestOptions?.headers); const _response = await (this._options.fetcher ?? core.fetcher)({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -332,6 +334,11 @@ export class Voices { body: _response.error.rawBody, rawResponse: _response.rawResponse, }); + case "body-is-null": + throw new errors.HumeError({ + statusCode: _response.error.statusCode, + rawResponse: _response.rawResponse, + }); case "timeout": throw new errors.HumeTimeoutError("Timeout exceeded when calling DELETE /v0/tts/voices."); case "unknown": @@ -341,9 +348,4 @@ export class Voices { }); } } - - protected async _getCustomAuthorizationHeaders(): Promise> { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-Hume-Api-Key": apiKeyValue }; - } } diff --git a/src/auth/HeaderAuthProvider.ts b/src/auth/HeaderAuthProvider.ts new file mode 100644 index 00000000..9dd16d5b --- /dev/null +++ b/src/auth/HeaderAuthProvider.ts @@ -0,0 +1,37 @@ +// This file was auto-generated by Fern from our API Definition. + +import * as core from "../core/index.js"; +import * as errors from "../errors/index.js"; + +export namespace HeaderAuthProvider { + export interface Options { + apiKey?: core.Supplier; + } +} + +export class HeaderAuthProvider implements core.AuthProvider { + private readonly headerValue: core.Supplier; + + constructor(options: HeaderAuthProvider.Options) { + this.headerValue = options.apiKey; + } + + public static canCreate(options: HeaderAuthProvider.Options): boolean { + return options.apiKey != null; + } + + public async getAuthRequest(_arg?: { endpointMetadata?: core.EndpointMetadata }): Promise { + const apiKey = await core.Supplier.get(this.headerValue); + if (apiKey == null) { + throw new errors.HumeError({ + message: "Please specify a apiKey by passing it in to the constructor", + }); + } + + const headerValue = apiKey; + + return { + headers: { "X-Hume-Api-Key": headerValue }, + }; + } +} diff --git a/src/auth/index.ts b/src/auth/index.ts new file mode 100644 index 00000000..c3485ef1 --- /dev/null +++ b/src/auth/index.ts @@ -0,0 +1 @@ +export { HeaderAuthProvider } from "./HeaderAuthProvider.js"; diff --git a/src/core/auth/AuthProvider.ts b/src/core/auth/AuthProvider.ts new file mode 100644 index 00000000..895a50ff --- /dev/null +++ b/src/core/auth/AuthProvider.ts @@ -0,0 +1,6 @@ +import type { EndpointMetadata } from "../fetcher/EndpointMetadata.js"; +import type { AuthRequest } from "./AuthRequest.js"; + +export interface AuthProvider { + getAuthRequest(arg?: { endpointMetadata?: EndpointMetadata }): Promise; +} diff --git a/src/core/auth/AuthRequest.ts b/src/core/auth/AuthRequest.ts new file mode 100644 index 00000000..f6218b42 --- /dev/null +++ b/src/core/auth/AuthRequest.ts @@ -0,0 +1,9 @@ +/** + * Request parameters for authentication requests. + */ +export interface AuthRequest { + /** + * The headers to be included in the request. + */ + headers: Record; +} diff --git a/src/core/auth/BasicAuth.ts b/src/core/auth/BasicAuth.ts new file mode 100644 index 00000000..a6423591 --- /dev/null +++ b/src/core/auth/BasicAuth.ts @@ -0,0 +1,32 @@ +import { base64Decode, base64Encode } from "../base64.js"; + +export interface BasicAuth { + username: string; + password: string; +} + +const BASIC_AUTH_HEADER_PREFIX = /^Basic /i; + +export const BasicAuth = { + toAuthorizationHeader: (basicAuth: BasicAuth | undefined): string | undefined => { + if (basicAuth == null) { + return undefined; + } + const token = base64Encode(`${basicAuth.username}:${basicAuth.password}`); + return `Basic ${token}`; + }, + fromAuthorizationHeader: (header: string): BasicAuth => { + const credentials = header.replace(BASIC_AUTH_HEADER_PREFIX, ""); + const decoded = base64Decode(credentials); + const [username, ...passwordParts] = decoded.split(":"); + const password = passwordParts.length > 0 ? passwordParts.join(":") : undefined; + + if (username == null || password == null) { + throw new Error("Invalid basic auth"); + } + return { + username, + password, + }; + }, +}; diff --git a/src/core/auth/BearerToken.ts b/src/core/auth/BearerToken.ts new file mode 100644 index 00000000..c44a06c3 --- /dev/null +++ b/src/core/auth/BearerToken.ts @@ -0,0 +1,20 @@ +export type BearerToken = string; + +const BEARER_AUTH_HEADER_PREFIX = /^Bearer /i; + +function toAuthorizationHeader(token: string | undefined): string | undefined { + if (token == null) { + return undefined; + } + return `Bearer ${token}`; +} + +export const BearerToken: { + toAuthorizationHeader: typeof toAuthorizationHeader; + fromAuthorizationHeader: (header: string) => BearerToken; +} = { + toAuthorizationHeader: toAuthorizationHeader, + fromAuthorizationHeader: (header: string): BearerToken => { + return header.replace(BEARER_AUTH_HEADER_PREFIX, "").trim() as BearerToken; + }, +}; diff --git a/src/core/auth/NoOpAuthProvider.ts b/src/core/auth/NoOpAuthProvider.ts new file mode 100644 index 00000000..5b7acfd2 --- /dev/null +++ b/src/core/auth/NoOpAuthProvider.ts @@ -0,0 +1,8 @@ +import type { AuthProvider } from "./AuthProvider.js"; +import type { AuthRequest } from "./AuthRequest.js"; + +export class NoOpAuthProvider implements AuthProvider { + public getAuthRequest(): Promise { + return Promise.resolve({ headers: {} }); + } +} diff --git a/src/core/auth/index.ts b/src/core/auth/index.ts new file mode 100644 index 00000000..2215b227 --- /dev/null +++ b/src/core/auth/index.ts @@ -0,0 +1,5 @@ +export type { AuthProvider } from "./AuthProvider.js"; +export type { AuthRequest } from "./AuthRequest.js"; +export { BasicAuth } from "./BasicAuth.js"; +export { BearerToken } from "./BearerToken.js"; +export { NoOpAuthProvider } from "./NoOpAuthProvider.js"; diff --git a/src/core/base64.ts b/src/core/base64.ts new file mode 100644 index 00000000..448a0db6 --- /dev/null +++ b/src/core/base64.ts @@ -0,0 +1,27 @@ +function base64ToBytes(base64: string): Uint8Array { + const binString = atob(base64); + return Uint8Array.from(binString, (m) => m.codePointAt(0)!); +} + +function bytesToBase64(bytes: Uint8Array): string { + const binString = String.fromCodePoint(...bytes); + return btoa(binString); +} + +export function base64Encode(input: string): string { + if (typeof Buffer !== "undefined") { + return Buffer.from(input, "utf8").toString("base64"); + } + + const bytes = new TextEncoder().encode(input); + return bytesToBase64(bytes); +} + +export function base64Decode(input: string): string { + if (typeof Buffer !== "undefined") { + return Buffer.from(input, "base64").toString("utf8"); + } + + const bytes = base64ToBytes(input); + return new TextDecoder().decode(bytes); +} diff --git a/src/core/fetcher/BinaryResponse.ts b/src/core/fetcher/BinaryResponse.ts index 4b4d0e89..bca7f4c7 100644 --- a/src/core/fetcher/BinaryResponse.ts +++ b/src/core/fetcher/BinaryResponse.ts @@ -1,25 +1,23 @@ -import type { ResponseWithBody } from "./ResponseWithBody.js"; - export type BinaryResponse = { /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ - bodyUsed: boolean; + bodyUsed: Response["bodyUsed"]; /** * Returns a ReadableStream of the response body. * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */ - stream: () => ReadableStream; + stream: () => Response["body"]; /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ - arrayBuffer: () => Promise; + arrayBuffer: () => ReturnType; /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ - blob: () => Promise; + blob: () => ReturnType; /** * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) * Some versions of the Fetch API may not support this method. */ - bytes?(): Promise; + bytes?(): ReturnType; }; -export function getBinaryResponse(response: ResponseWithBody): BinaryResponse { +export function getBinaryResponse(response: Response): BinaryResponse { const binaryResponse: BinaryResponse = { get bodyUsed() { return response.bodyUsed; diff --git a/src/core/fetcher/Fetcher.ts b/src/core/fetcher/Fetcher.ts index ef020d4d..58bb0e3e 100644 --- a/src/core/fetcher/Fetcher.ts +++ b/src/core/fetcher/Fetcher.ts @@ -8,6 +8,7 @@ import { getErrorResponseBody } from "./getErrorResponseBody.js"; import { getFetchFn } from "./getFetchFn.js"; import { getRequestBody } from "./getRequestBody.js"; import { getResponseBody } from "./getResponseBody.js"; +import { Headers } from "./Headers.js"; import { makeRequest } from "./makeRequest.js"; import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; import { requestWithRetries } from "./requestWithRetries.js"; @@ -34,7 +35,7 @@ export declare namespace Fetcher { logging?: LogConfig | Logger; } - export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError; + export type Error = FailedStatusCodeError | NonJsonError | BodyIsNullError | TimeoutError | UnknownError; export interface FailedStatusCodeError { reason: "status-code"; @@ -48,6 +49,11 @@ export declare namespace Fetcher { rawBody: string; } + export interface BodyIsNullError { + reason: "body-is-null"; + statusCode: number; + } + export interface TimeoutError { reason: "timeout"; } @@ -60,19 +66,26 @@ export declare namespace Fetcher { const SENSITIVE_HEADERS = new Set([ "authorization", + "www-authenticate", "x-api-key", "api-key", + "apikey", + "x-api-token", "x-auth-token", + "auth-token", "cookie", "set-cookie", "proxy-authorization", + "proxy-authenticate", "x-csrf-token", "x-xsrf-token", + "x-session-token", + "x-access-token", ]); -function redactHeaders(headers: Record): Record { +function redactHeaders(headers: Headers | Record): Record { const filtered: Record = {}; - for (const [key, value] of Object.entries(headers)) { + for (const [key, value] of headers instanceof Headers ? headers.entries() : Object.entries(headers)) { if (SENSITIVE_HEADERS.has(key.toLowerCase())) { filtered[key] = "[REDACTED]"; } else { @@ -123,28 +136,35 @@ function redactUrl(url: string): string { if (protocolIndex === -1) return url; const afterProtocol = protocolIndex + 3; - const atIndex = url.indexOf("@", afterProtocol); - - if (atIndex !== -1) { - const pathStart = url.indexOf("/", afterProtocol); - const queryStart = url.indexOf("?", afterProtocol); - const fragmentStart = url.indexOf("#", afterProtocol); - - const firstDelimiter = Math.min( - pathStart === -1 ? url.length : pathStart, - queryStart === -1 ? url.length : queryStart, - fragmentStart === -1 ? url.length : fragmentStart, - ); - if (atIndex < firstDelimiter) { - url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`; + // Find the first delimiter that marks the end of the authority section + const pathStart = url.indexOf("/", afterProtocol); + let queryStart = url.indexOf("?", afterProtocol); + let fragmentStart = url.indexOf("#", afterProtocol); + + const firstDelimiter = Math.min( + pathStart === -1 ? url.length : pathStart, + queryStart === -1 ? url.length : queryStart, + fragmentStart === -1 ? url.length : fragmentStart, + ); + + // Find the LAST @ before the delimiter (handles multiple @ in credentials) + let atIndex = -1; + for (let i = afterProtocol; i < firstDelimiter; i++) { + if (url[i] === "@") { + atIndex = i; } } - const queryStart = url.indexOf("?"); + if (atIndex !== -1) { + url = `${url.slice(0, afterProtocol)}[REDACTED]@${url.slice(atIndex + 1)}`; + } + + // Recalculate queryStart since url might have changed + queryStart = url.indexOf("?"); if (queryStart === -1) return url; - const fragmentStart = url.indexOf("#", queryStart); + fragmentStart = url.indexOf("#", queryStart); const queryEnd = fragmentStart !== -1 ? fragmentStart : url.length; const queryString = url.slice(queryStart + 1, queryEnd); @@ -154,16 +174,16 @@ function redactUrl(url: string): string { // Using indexOf is faster than regex for simple substring matching const lower = queryString.toLowerCase(); const hasSensitive = - lower.includes("token") || // catches token, access_token, auth_token, etc. - lower.includes("key") || // catches key, api_key, apikey, api-key, etc. - lower.includes("password") || // catches password - lower.includes("passwd") || // catches passwd - lower.includes("secret") || // catches secret, api_secret, etc. - lower.includes("session") || // catches session, session_id, session-id - lower.includes("auth"); // catches auth_token, auth-token, etc. + lower.includes("token") || + lower.includes("key") || + lower.includes("password") || + lower.includes("passwd") || + lower.includes("secret") || + lower.includes("session") || + lower.includes("auth"); if (!hasSensitive) { - return url; // Early exit - no sensitive params + return url; } // SLOW PATH: Parse and redact @@ -193,10 +213,15 @@ function redactUrl(url: string): string { return url.slice(0, queryStart + 1) + redactedParams.join("&") + url.slice(queryEnd); } -async function getHeaders(args: Fetcher.Args): Promise> { - const newHeaders: Record = {}; +async function getHeaders(args: Fetcher.Args): Promise { + const newHeaders: Headers = new Headers(); + + newHeaders.set( + "Accept", + args.responseType === "json" ? "application/json" : args.responseType === "text" ? "text/plain" : "*/*", + ); if (args.body !== undefined && args.contentType != null) { - newHeaders["Content-Type"] = args.contentType; + newHeaders.set("Content-Type", args.contentType); } if (args.headers == null) { @@ -206,13 +231,13 @@ async function getHeaders(args: Fetcher.Args): Promise> { for (const [key, value] of Object.entries(args.headers)) { const result = await EndpointSupplier.get(value, { endpointMetadata: args.endpointMetadata ?? {} }); if (typeof result === "string") { - newHeaders[key] = result; + newHeaders.set(key, result); continue; } if (result == null) { continue; } - newHeaders[key] = `${result}`; + newHeaders.set(key, `${result}`); } return newHeaders; } @@ -261,12 +286,14 @@ export async function fetcherImpl(args: Fetcher.Args): Promise(args: Fetcher.Args): Promise; -}; - -export function isResponseWithBody(response: Response): response is ResponseWithBody { - return (response as ResponseWithBody).body != null; -} diff --git a/src/core/fetcher/Supplier.ts.diff b/src/core/fetcher/Supplier.ts.diff deleted file mode 100644 index 2700e2d3..00000000 --- a/src/core/fetcher/Supplier.ts.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/core/fetcher/Supplier.ts b/src/core/fetcher/Supplier.ts -index aa95dd8..867c931 100644 ---- a/src/core/fetcher/Supplier.ts -+++ b/src/core/fetcher/Supplier.ts -@@ -1,19 +1,11 @@ --/** THIS FILE IS MANUALLY MAINAINED: see .fernignore */ --export type Supplier = T | (() => T); -+export type Supplier = T | Promise | (() => T | Promise); - - export const Supplier = { -- get: (supplier: Supplier): T => { -+ get: async (supplier: Supplier): Promise => { - if (typeof supplier === "function") { - return (supplier as () => T)(); - } else { - return supplier; - } - }, -- map: (supplier: Supplier, f: (value: T) => R): Supplier => { -- if (typeof supplier === "function") { -- return () => f(Supplier.get(supplier)); -- } else { -- return f(supplier); -- } -- }, - }; diff --git a/src/core/fetcher/getResponseBody.ts b/src/core/fetcher/getResponseBody.ts index 0f24de17..708d5572 100644 --- a/src/core/fetcher/getResponseBody.ts +++ b/src/core/fetcher/getResponseBody.ts @@ -1,11 +1,7 @@ import { fromJson } from "../json.js"; import { getBinaryResponse } from "./BinaryResponse.js"; -import { isResponseWithBody } from "./ResponseWithBody.js"; export async function getResponseBody(response: Response, responseType?: string): Promise { - if (!isResponseWithBody(response)) { - return undefined; - } switch (responseType) { case "binary-response": return getBinaryResponse(response); @@ -14,8 +10,27 @@ export async function getResponseBody(response: Response, responseType?: string) case "arrayBuffer": return await response.arrayBuffer(); case "sse": + if (response.body == null) { + return { + ok: false, + error: { + reason: "body-is-null", + statusCode: response.status, + }, + }; + } return response.body; case "streaming": + if (response.body == null) { + return { + ok: false, + error: { + reason: "body-is-null", + statusCode: response.status, + }, + }; + } + return response.body; case "text": diff --git a/src/core/fetcher/makeRequest.ts b/src/core/fetcher/makeRequest.ts index 5edce698..921565eb 100644 --- a/src/core/fetcher/makeRequest.ts +++ b/src/core/fetcher/makeRequest.ts @@ -4,7 +4,7 @@ export const makeRequest = async ( fetchFn: (url: string, init: RequestInit) => Promise, url: string, method: string, - headers: Record, + headers: Headers | Record, requestBody: BodyInit | undefined, timeoutMs?: number, abortSignal?: AbortSignal, @@ -13,15 +13,13 @@ export const makeRequest = async ( ): Promise => { const signals: AbortSignal[] = []; - // Add timeout signal - let timeoutAbortId: NodeJS.Timeout | undefined; + let timeoutAbortId: ReturnType | undefined; if (timeoutMs != null) { const { signal, abortId } = getTimeoutSignal(timeoutMs); timeoutAbortId = abortId; signals.push(signal); } - // Add arbitrary signal if (abortSignal != null) { signals.push(abortSignal); } diff --git a/src/core/fetcher/requestWithRetries.ts b/src/core/fetcher/requestWithRetries.ts index 3d30bd1c..1f689688 100644 --- a/src/core/fetcher/requestWithRetries.ts +++ b/src/core/fetcher/requestWithRetries.ts @@ -4,28 +4,23 @@ const DEFAULT_MAX_RETRIES = 2; const JITTER_FACTOR = 0.2; // 20% random jitter function addPositiveJitter(delay: number): number { - // Generate a random value between 0 and +JITTER_FACTOR const jitterMultiplier = 1 + Math.random() * JITTER_FACTOR; return delay * jitterMultiplier; } function addSymmetricJitter(delay: number): number { - // Generate a random value in a JITTER_FACTOR-sized percentage range around delay const jitterMultiplier = 1 + (Math.random() - 0.5) * JITTER_FACTOR; return delay * jitterMultiplier; } function getRetryDelayFromHeaders(response: Response, retryAttempt: number): number { - // Check for Retry-After header first (RFC 7231), with no jitter const retryAfter = response.headers.get("Retry-After"); if (retryAfter) { - // Parse as number of seconds... const retryAfterSeconds = parseInt(retryAfter, 10); if (!Number.isNaN(retryAfterSeconds) && retryAfterSeconds > 0) { return Math.min(retryAfterSeconds * 1000, MAX_RETRY_DELAY); } - // ...or as an HTTP date; both are valid const retryAfterDate = new Date(retryAfter); if (!Number.isNaN(retryAfterDate.getTime())) { const delay = retryAfterDate.getTime() - Date.now(); @@ -35,12 +30,10 @@ function getRetryDelayFromHeaders(response: Response, retryAttempt: number): num } } - // Then check for industry-standard X-RateLimit-Reset header, with positive jitter const rateLimitReset = response.headers.get("X-RateLimit-Reset"); if (rateLimitReset) { const resetTime = parseInt(rateLimitReset, 10); if (!Number.isNaN(resetTime)) { - // Assume Unix timestamp in epoch seconds const delay = resetTime * 1000 - Date.now(); if (delay > 0) { return addPositiveJitter(Math.min(delay, MAX_RETRY_DELAY)); @@ -48,7 +41,6 @@ function getRetryDelayFromHeaders(response: Response, retryAttempt: number): num } } - // Fall back to exponential backoff, with symmetric jitter return addSymmetricJitter(Math.min(INITIAL_RETRY_DELAY * 2 ** retryAttempt, MAX_RETRY_DELAY)); } @@ -60,7 +52,6 @@ export async function requestWithRetries( for (let i = 0; i < maxRetries; ++i) { if ([408, 429].includes(response.status) || response.status >= 500) { - // Get delay with appropriate jitter applied const delay = getRetryDelayFromHeaders(response, i); await new Promise((resolve) => setTimeout(resolve, delay)); diff --git a/src/core/fetcher/signals.ts b/src/core/fetcher/signals.ts index a8d32a2e..7bd3757e 100644 --- a/src/core/fetcher/signals.ts +++ b/src/core/fetcher/signals.ts @@ -1,34 +1,22 @@ const TIMEOUT = "timeout"; -export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: NodeJS.Timeout } { +export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: ReturnType } { const controller = new AbortController(); const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); return { signal: controller.signal, abortId }; } -/** - * Returns an abort signal that is getting aborted when - * at least one of the specified abort signals is aborted. - * - * Requires at least node.js 18. - */ export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { - // Allowing signals to be passed either as array - // of signals or as multiple arguments. const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args) as AbortSignal[]; const controller = new AbortController(); for (const signal of signals) { if (signal.aborted) { - // Exiting early if one of the signals - // is already aborted. controller.abort((signal as any)?.reason); break; } - // Listening for signals and removing the listeners - // when at least one symbol is aborted. signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { signal: controller.signal, }); diff --git a/src/core/index.ts b/src/core/index.ts index 07869158..74859ec5 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,3 +1,5 @@ +export * from "./auth/index.js"; +export * from "./base64.js"; export * from "./fetcher/index.js"; export * as file from "./file/index.js"; export * from "./form-data-utils/index.js"; diff --git a/src/core/logging/logger.ts b/src/core/logging/logger.ts index a2bdef4c..a3f3673c 100644 --- a/src/core/logging/logger.ts +++ b/src/core/logging/logger.ts @@ -102,7 +102,7 @@ export class Logger { * @returns True if the level should be logged */ public shouldLog(level: LogLevel): boolean { - return !this.silent && this.level >= logLevelMap[level]; + return !this.silent && this.level <= logLevelMap[level]; } /** diff --git a/src/core/runtime/runtime.ts b/src/core/runtime/runtime.ts index 08fd2563..56ebbb87 100644 --- a/src/core/runtime/runtime.ts +++ b/src/core/runtime/runtime.ts @@ -99,6 +99,18 @@ function evaluateRuntime(): Runtime { }; } + /** + * A constant that indicates whether the environment the code is running is in React-Native. + * This check should come before Node.js detection since React Native may have a process polyfill. + * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js + */ + const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; + if (isReactNative) { + return { + type: "react-native", + }; + } + /** * A constant that indicates whether the environment the code is running is Node.JS. */ @@ -116,17 +128,6 @@ function evaluateRuntime(): Runtime { }; } - /** - * A constant that indicates whether the environment the code is running is in React-Native. - * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js - */ - const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; - if (isReactNative) { - return { - type: "react-native", - }; - } - return { type: "unknown", }; diff --git a/src/core/schemas/Schema.ts b/src/core/schemas/Schema.ts index 179edf6a..4cd8b1d9 100644 --- a/src/core/schemas/Schema.ts +++ b/src/core/schemas/Schema.ts @@ -24,6 +24,7 @@ export const SchemaType = { NUMBER: "number", STRING: "string", UNKNOWN: "unknown", + NEVER: "never", RECORD: "record", SET: "set", UNION: "union", @@ -32,6 +33,7 @@ export const SchemaType = { OPTIONAL: "optional", OPTIONAL_NULLABLE: "optionalNullable", } as const; + export type SchemaType = (typeof SchemaType)[keyof typeof SchemaType]; export type MaybeValid = Valid | Invalid; diff --git a/src/core/schemas/builders/object/object.ts b/src/core/schemas/builders/object/object.ts index ca6d2270..624d4d38 100644 --- a/src/core/schemas/builders/object/object.ts +++ b/src/core/schemas/builders/object/object.ts @@ -244,17 +244,19 @@ export function getObjectUtils(schema: BaseObjectSchema { return validateAndTransformExtendedObject({ extensionKeys: extension._getRawProperties(), - value: raw as object, + value: raw, transformBase: (rawBase) => schema.parse(rawBase, opts), transformExtension: (rawExtension) => extension.parse(rawExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, }); }, json: (parsed, opts) => { return validateAndTransformExtendedObject({ extensionKeys: extension._getParsedProperties(), - value: parsed as object, + value: parsed, transformBase: (parsedBase) => schema.json(parsedBase, opts), transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, }); }, getType: () => SchemaType.OBJECT, @@ -316,12 +318,26 @@ function validateAndTransformExtendedObject MaybeValid; transformExtension: (value: object) => MaybeValid; + breadcrumbsPrefix?: string[]; }): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + const extensionPropertiesSet = new Set(extensionKeys); const [extensionProperties, baseProperties] = partition(keys(value), (key) => extensionPropertiesSet.has(key as keyof PreTransformedExtension), diff --git a/src/core/schemas/builders/primitives/index.ts b/src/core/schemas/builders/primitives/index.ts index 462a6d11..7a3ee015 100644 --- a/src/core/schemas/builders/primitives/index.ts +++ b/src/core/schemas/builders/primitives/index.ts @@ -1,5 +1,6 @@ export { any } from "./any.js"; export { boolean } from "./boolean.js"; +export { never } from "./never.js"; export { number } from "./number.js"; export { string } from "./string.js"; export { unknown } from "./unknown.js"; diff --git a/src/core/schemas/builders/primitives/never.ts b/src/core/schemas/builders/primitives/never.ts new file mode 100644 index 00000000..91f85d74 --- /dev/null +++ b/src/core/schemas/builders/primitives/never.ts @@ -0,0 +1,15 @@ +import { type Schema, SchemaType } from "../../Schema.js"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator.js"; + +export const never: () => Schema = createIdentitySchemaCreator( + SchemaType.NEVER, + (_value, { breadcrumbsPrefix = [] } = {}) => ({ + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: "Expected never", + }, + ], + }), +); diff --git a/src/core/url/join.ts b/src/core/url/join.ts index b872a90a..7ca7daef 100644 --- a/src/core/url/join.ts +++ b/src/core/url/join.ts @@ -12,7 +12,6 @@ export function join(base: string, ...segments: string[]): string { try { url = new URL(base); } catch { - // Fallback to path joining if URL is malformed return joinPath(base, ...segments); } diff --git a/src/core/websocket/ws.ts.diff b/src/core/websocket/ws.ts.diff deleted file mode 100644 index a46a8098..00000000 --- a/src/core/websocket/ws.ts.diff +++ /dev/null @@ -1,137 +0,0 @@ -diff --git a/src/core/websocket/ws.ts b/src/core/websocket/ws.ts -index 9a16477..cba4f69 100644 ---- a/src/core/websocket/ws.ts -+++ b/src/core/websocket/ws.ts -@@ -3,7 +3,6 @@ import { WebSocket as NodeWebSocket } from "ws"; - import { RUNTIME } from "../runtime/index.js"; - import { toQueryString } from "../url/qs.js"; - import * as Events from "./events.js"; --import { SDK_VERSION } from "../../version.js"; - - const getGlobalWebSocket = (): WebSocket | undefined => { - if (typeof WebSocket !== "undefined") { -@@ -70,49 +69,6 @@ const DEFAULT_OPTIONS = { - debug: false, - }; - --function addApiKeyFromHeader({ -- headers, -- queryParameters, --}: { -- headers: Record | undefined; -- queryParameters: Record | undefined; --}) { -- const apiKeyValue = Object.entries(headers ?? {}).find(([k]) => k.toLowerCase() === "x-hume-api-key")?.[1]; -- if (apiKeyValue && !queryParameters?.["api_key"]) { -- return { ...queryParameters, api_key: apiKeyValue }; -- } -- return queryParameters; --} -- --function addAccessTokenFromHeader({ -- headers, -- queryParameters, --}: { -- headers: Record | undefined; -- queryParameters: Record | undefined; --}) { -- const authHeaderValue = headers?.["Authorization"] || headers?.["authorization"]; -- if (!authHeaderValue) { -- return queryParameters; -- } -- if (!authHeaderValue.startsWith("Bearer ")) { -- return queryParameters; -- } -- if (queryParameters?.["access_token"]) { -- return queryParameters; -- } -- const token = authHeaderValue.substring("Bearer ".length); -- return { ...queryParameters, access_token: token }; --} -- --function addSdkTracking(queryParameters: Record | undefined) { -- return { -- ...queryParameters, -- fernSdkLanguage: "JavaScript", -- fernSdkVersion: SDK_VERSION, -- }; --} -- - export class ReconnectingWebSocket { - private _ws?: WebSocket; - private _listeners: ReconnectingWebSocket.ListenersMap = { -@@ -141,47 +97,22 @@ export class ReconnectingWebSocket { - this._protocols = protocols; - this._options = options ?? DEFAULT_OPTIONS; - this._headers = headers; -- this._queryParameters = addSdkTracking( -- addAccessTokenFromHeader({ -- headers, -- queryParameters: addApiKeyFromHeader({ -- headers, -- queryParameters, -- }), -- }), -- ); -- -+ this._queryParameters = queryParameters; - if (this._options.startClosed) { - this._shouldReconnect = false; - } - this._connect(); - } - -- static get CONNECTING() { -- return 0; -- } -- static get OPEN() { -- return 1; -- } -- static get CLOSING() { -- return 2; -- } -- static get CLOSED() { -- return 3; -- } -+ public static readonly CONNECTING = 0; -+ public static readonly OPEN = 1; -+ public static readonly CLOSING = 2; -+ public static readonly CLOSED = 3; - -- get CONNECTING(): number { -- return ReconnectingWebSocket.CONNECTING; -- } -- get OPEN(): number { -- return ReconnectingWebSocket.OPEN; -- } -- get CLOSING(): number { -- return ReconnectingWebSocket.CLOSING; -- } -- get CLOSED(): number { -- return ReconnectingWebSocket.CLOSED; -- } -+ public readonly CONNECTING: typeof ReconnectingWebSocket.CONNECTING = ReconnectingWebSocket.CONNECTING; -+ public readonly OPEN: typeof ReconnectingWebSocket.OPEN = ReconnectingWebSocket.OPEN; -+ public readonly CLOSING: typeof ReconnectingWebSocket.CLOSING = ReconnectingWebSocket.CLOSING; -+ public readonly CLOSED: typeof ReconnectingWebSocket.CLOSED = ReconnectingWebSocket.CLOSED; - - get binaryType() { - return this._ws ? this._ws.binaryType : this._binaryType; -@@ -383,7 +314,7 @@ export class ReconnectingWebSocket { - } = this._options; - let delay = 0; - if (this._retryCount > 0) { -- delay = minReconnectionDelay * Math.pow(reconnectionDelayGrowFactor, this._retryCount - 1); -+ delay = minReconnectionDelay * reconnectionDelayGrowFactor ** (this._retryCount - 1); - if (delay > maxReconnectionDelay) { - delay = maxReconnectionDelay; - } -@@ -478,7 +409,7 @@ export class ReconnectingWebSocket { - try { - this._ws.close(code, reason); - this._handleClose(new Events.CloseEvent(code, reason, this)); -- } catch (error) { -+ } catch (_error) { - // ignore - } - } diff --git a/src/index.ts.diff b/src/index.ts.diff deleted file mode 100644 index 66b05aa1..00000000 --- a/src/index.ts.diff +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/src/index.ts b/src/index.ts -index 4d19902..c814670 100644 ---- a/src/index.ts -+++ b/src/index.ts -@@ -1,8 +1,7 @@ - export * as Hume from "./api/index.js"; --export * as serialization from "./serialization/index.js"; -+export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js"; -+export { HumeClient } from "./Client.js"; -+export { HumeEnvironment, type HumeEnvironmentUrls } from "./environments.js"; - export { HumeError, HumeTimeoutError } from "./errors/index.js"; --export { HumeEnvironment } from "./environments.js"; --export type { HumeEnvironmentUrls } from "./environments.js"; - export * from "./exports.js"; -- --export * from "./wrapper/index.js"; -+export * as serialization from "./serialization/index.js"; diff --git a/src/serialization/resources/empathicVoice/resources/chat/index.ts.diff b/src/serialization/resources/empathicVoice/resources/chat/index.ts.diff deleted file mode 100644 index 4ddb2df8..00000000 --- a/src/serialization/resources/empathicVoice/resources/chat/index.ts.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/src/serialization/resources/empathicVoice/resources/chat/index.ts b/src/serialization/resources/empathicVoice/resources/chat/index.ts -index f5ca113..d9adb1a 100644 ---- a/src/serialization/resources/empathicVoice/resources/chat/index.ts -+++ b/src/serialization/resources/empathicVoice/resources/chat/index.ts -@@ -1,7 +1,2 @@ - export * from "./client/index.js"; - export * from "./types/index.js"; --/** -- * @deprecated Use `serialization.empathicVoice.SubscribeEvent` instead. -- * This serializer alias will be removed in a future version. -- */ --export { SubscribeEvent } from "./types/SubscribeEvent.js"; diff --git a/src/version.ts b/src/version.ts index 8e95dbf0..480f0f51 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = "0.15.9"; +export const SDK_VERSION = "0.15.10"; diff --git a/tests/mock-server/MockServer.ts b/tests/mock-server/MockServer.ts index 5b30fe7c..95487215 100644 --- a/tests/mock-server/MockServer.ts +++ b/tests/mock-server/MockServer.ts @@ -19,7 +19,7 @@ export class MockServer { public mockEndpoint(options?: RequestHandlerOptions): ReturnType { const builder = mockEndpointBuilder({ - once: options?.once, + once: options?.once ?? true, onBuild: (handler) => { this.server.use(handler); }, diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 00000000..a5651f81 --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,80 @@ +import { expect } from "vitest"; + +interface CustomMatchers { + toContainHeaders(expectedHeaders: Record): R; +} + +declare module "vitest" { + interface Assertion extends CustomMatchers {} + interface AsymmetricMatchersContaining extends CustomMatchers {} +} + +expect.extend({ + toContainHeaders(actual: unknown, expectedHeaders: Record) { + const isHeaders = actual instanceof Headers; + const isPlainObject = typeof actual === "object" && actual !== null && !Array.isArray(actual); + + if (!isHeaders && !isPlainObject) { + throw new TypeError("Received value must be an instance of Headers or a plain object!"); + } + + if (typeof expectedHeaders !== "object" || expectedHeaders === null || Array.isArray(expectedHeaders)) { + throw new TypeError("Expected headers must be a plain object!"); + } + + const missingHeaders: string[] = []; + const mismatchedHeaders: Array<{ key: string; expected: string; actual: string | null }> = []; + + for (const [key, value] of Object.entries(expectedHeaders)) { + let actualValue: string | null = null; + + if (isHeaders) { + // Headers.get() is already case-insensitive + actualValue = (actual as Headers).get(key); + } else { + // For plain objects, do case-insensitive lookup + const actualObj = actual as Record; + const lowerKey = key.toLowerCase(); + const foundKey = Object.keys(actualObj).find((k) => k.toLowerCase() === lowerKey); + actualValue = foundKey ? actualObj[foundKey] : null; + } + + if (actualValue === null || actualValue === undefined) { + missingHeaders.push(key); + } else if (actualValue !== value) { + mismatchedHeaders.push({ key, expected: value, actual: actualValue }); + } + } + + const pass = missingHeaders.length === 0 && mismatchedHeaders.length === 0; + + const actualType = isHeaders ? "Headers" : "object"; + + if (pass) { + return { + message: () => `expected ${actualType} not to contain ${this.utils.printExpected(expectedHeaders)}`, + pass: true, + }; + } else { + const messages: string[] = []; + + if (missingHeaders.length > 0) { + messages.push(`Missing headers: ${this.utils.printExpected(missingHeaders.join(", "))}`); + } + + if (mismatchedHeaders.length > 0) { + const mismatches = mismatchedHeaders.map( + ({ key, expected, actual }) => + `${key}: expected ${this.utils.printExpected(expected)} but got ${this.utils.printReceived(actual)}`, + ); + messages.push(mismatches.join("\n")); + } + + return { + message: () => + `expected ${actualType} to contain ${this.utils.printExpected(expectedHeaders)}\n\n${messages.join("\n")}`, + pass: false, + }; + } + }, +}); diff --git a/tests/unit/auth/BasicAuth.test.ts b/tests/unit/auth/BasicAuth.test.ts new file mode 100644 index 00000000..9b512336 --- /dev/null +++ b/tests/unit/auth/BasicAuth.test.ts @@ -0,0 +1,92 @@ +import { BasicAuth } from "../../../src/core/auth/BasicAuth"; + +describe("BasicAuth", () => { + interface ToHeaderTestCase { + description: string; + input: { username: string; password: string }; + expected: string; + } + + interface FromHeaderTestCase { + description: string; + input: string; + expected: { username: string; password: string }; + } + + interface ErrorTestCase { + description: string; + input: string; + expectedError: string; + } + + describe("toAuthorizationHeader", () => { + const toHeaderTests: ToHeaderTestCase[] = [ + { + description: "correctly converts to header", + input: { username: "username", password: "password" }, + expected: "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", + }, + ]; + + toHeaderTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(BasicAuth.toAuthorizationHeader(input)).toBe(expected); + }); + }); + }); + + describe("fromAuthorizationHeader", () => { + const fromHeaderTests: FromHeaderTestCase[] = [ + { + description: "correctly parses header", + input: "Basic dXNlcm5hbWU6cGFzc3dvcmQ=", + expected: { username: "username", password: "password" }, + }, + { + description: "handles password with colons", + input: "Basic dXNlcjpwYXNzOndvcmQ=", + expected: { username: "user", password: "pass:word" }, + }, + { + description: "handles empty username and password (just colon)", + input: "Basic Og==", + expected: { username: "", password: "" }, + }, + { + description: "handles empty username", + input: "Basic OnBhc3N3b3Jk", + expected: { username: "", password: "password" }, + }, + { + description: "handles empty password", + input: "Basic dXNlcm5hbWU6", + expected: { username: "username", password: "" }, + }, + ]; + + fromHeaderTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(BasicAuth.fromAuthorizationHeader(input)).toEqual(expected); + }); + }); + + const errorTests: ErrorTestCase[] = [ + { + description: "throws error for completely empty credentials", + input: "Basic ", + expectedError: "Invalid basic auth", + }, + { + description: "throws error for credentials without colon", + input: "Basic dXNlcm5hbWU=", + expectedError: "Invalid basic auth", + }, + ]; + + errorTests.forEach(({ description, input, expectedError }) => { + it(description, () => { + expect(() => BasicAuth.fromAuthorizationHeader(input)).toThrow(expectedError); + }); + }); + }); +}); diff --git a/tests/unit/auth/BearerToken.test.ts b/tests/unit/auth/BearerToken.test.ts new file mode 100644 index 00000000..7757b87c --- /dev/null +++ b/tests/unit/auth/BearerToken.test.ts @@ -0,0 +1,14 @@ +import { BearerToken } from "../../../src/core/auth/BearerToken"; + +describe("BearerToken", () => { + describe("toAuthorizationHeader", () => { + it("correctly converts to header", () => { + expect(BearerToken.toAuthorizationHeader("my-token")).toBe("Bearer my-token"); + }); + }); + describe("fromAuthorizationHeader", () => { + it("correctly parses header", () => { + expect(BearerToken.fromAuthorizationHeader("Bearer my-token")).toBe("my-token"); + }); + }); +}); diff --git a/tests/unit/base64.test.ts b/tests/unit/base64.test.ts new file mode 100644 index 00000000..939594ca --- /dev/null +++ b/tests/unit/base64.test.ts @@ -0,0 +1,53 @@ +import { base64Decode, base64Encode } from "../../src/core/base64"; + +describe("base64", () => { + describe("base64Encode", () => { + it("should encode ASCII strings", () => { + expect(base64Encode("hello")).toBe("aGVsbG8="); + expect(base64Encode("")).toBe(""); + }); + + it("should encode UTF-8 strings", () => { + expect(base64Encode("café")).toBe("Y2Fmw6k="); + expect(base64Encode("🎉")).toBe("8J+OiQ=="); + }); + + it("should handle basic auth credentials", () => { + expect(base64Encode("username:password")).toBe("dXNlcm5hbWU6cGFzc3dvcmQ="); + }); + }); + + describe("base64Decode", () => { + it("should decode ASCII strings", () => { + expect(base64Decode("aGVsbG8=")).toBe("hello"); + expect(base64Decode("")).toBe(""); + }); + + it("should decode UTF-8 strings", () => { + expect(base64Decode("Y2Fmw6k=")).toBe("café"); + expect(base64Decode("8J+OiQ==")).toBe("🎉"); + }); + + it("should handle basic auth credentials", () => { + expect(base64Decode("dXNlcm5hbWU6cGFzc3dvcmQ=")).toBe("username:password"); + }); + }); + + describe("round-trip encoding", () => { + const testStrings = [ + "hello world", + "test@example.com", + "café", + "username:password", + "user@domain.com:super$ecret123!", + ]; + + testStrings.forEach((testString) => { + it(`should round-trip encode/decode: "${testString}"`, () => { + const encoded = base64Encode(testString); + const decoded = base64Decode(encoded); + expect(decoded).toBe(testString); + }); + }); + }); +}); diff --git a/tests/unit/fetcher/createRequestUrl.test.ts b/tests/unit/fetcher/createRequestUrl.test.ts index 06e03b2c..a92f1b5e 100644 --- a/tests/unit/fetcher/createRequestUrl.test.ts +++ b/tests/unit/fetcher/createRequestUrl.test.ts @@ -1,160 +1,163 @@ import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; describe("Test createRequestUrl", () => { - it("should return the base URL when no query parameters are provided", () => { - const baseUrl = "https://api.example.com"; - expect(createRequestUrl(baseUrl)).toBe(baseUrl); - }); - - it("should append simple query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { key: "value", another: "param" }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?key=value&another=param"); - }); - - it("should handle array query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { items: ["a", "b", "c"] }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?items=a&items=b&items=c"); - }); - - it("should handle object query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { filter: { name: "John", age: 30 } }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30", - ); - }); - - it("should handle mixed types of query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - simple: "value", - array: ["x", "y"], - object: { key: "value" }, - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value", - ); - }); - - it("should handle empty query parameters object", () => { - const baseUrl = "https://api.example.com"; - expect(createRequestUrl(baseUrl, {})).toBe(baseUrl); - }); - - it("should encode special characters in query parameters", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { special: "a&b=c d" }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?special=a%26b%3Dc%20d"); - }); - - // Additional tests for edge cases and different value types - it("should handle numeric values", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { count: 42, price: 19.99, active: 1, inactive: 0 }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?count=42&price=19.99&active=1&inactive=0", - ); - }); - - it("should handle boolean values", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { enabled: true, disabled: false }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?enabled=true&disabled=false"); - }); - - it("should handle null and undefined values", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - valid: "value", - nullValue: null, - undefinedValue: undefined, - emptyString: "", - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?valid=value&nullValue=&emptyString=", - ); - }); - - it("should handle deeply nested objects", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - user: { - profile: { - name: "John", - settings: { theme: "dark" }, + const BASE_URL = "https://api.example.com"; + + interface TestCase { + description: string; + baseUrl: string; + queryParams?: Record; + expected: string; + } + + const testCases: TestCase[] = [ + { + description: "should return the base URL when no query parameters are provided", + baseUrl: BASE_URL, + expected: BASE_URL, + }, + { + description: "should append simple query parameters", + baseUrl: BASE_URL, + queryParams: { key: "value", another: "param" }, + expected: "https://api.example.com?key=value&another=param", + }, + { + description: "should handle array query parameters", + baseUrl: BASE_URL, + queryParams: { items: ["a", "b", "c"] }, + expected: "https://api.example.com?items=a&items=b&items=c", + }, + { + description: "should handle object query parameters", + baseUrl: BASE_URL, + queryParams: { filter: { name: "John", age: 30 } }, + expected: "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30", + }, + { + description: "should handle mixed types of query parameters", + baseUrl: BASE_URL, + queryParams: { + simple: "value", + array: ["x", "y"], + object: { key: "value" }, + }, + expected: "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value", + }, + { + description: "should handle empty query parameters object", + baseUrl: BASE_URL, + queryParams: {}, + expected: BASE_URL, + }, + { + description: "should encode special characters in query parameters", + baseUrl: BASE_URL, + queryParams: { special: "a&b=c d" }, + expected: "https://api.example.com?special=a%26b%3Dc%20d", + }, + { + description: "should handle numeric values", + baseUrl: BASE_URL, + queryParams: { count: 42, price: 19.99, active: 1, inactive: 0 }, + expected: "https://api.example.com?count=42&price=19.99&active=1&inactive=0", + }, + { + description: "should handle boolean values", + baseUrl: BASE_URL, + queryParams: { enabled: true, disabled: false }, + expected: "https://api.example.com?enabled=true&disabled=false", + }, + { + description: "should handle null and undefined values", + baseUrl: BASE_URL, + queryParams: { + valid: "value", + nullValue: null, + undefinedValue: undefined, + emptyString: "", + }, + expected: "https://api.example.com?valid=value&nullValue=&emptyString=", + }, + { + description: "should handle deeply nested objects", + baseUrl: BASE_URL, + queryParams: { + user: { + profile: { + name: "John", + settings: { theme: "dark" }, + }, }, }, - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", - ); - }); - - it("should handle arrays of objects", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - users: [ - { name: "John", age: 30 }, - { name: "Jane", age: 25 }, - ], - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?users%5Bname%5D=John&users%5Bage%5D=30&users%5Bname%5D=Jane&users%5Bage%5D=25", - ); - }); - - it("should handle mixed arrays", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - mixed: ["string", 42, true, { key: "value" }], - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?mixed=string&mixed=42&mixed=true&mixed%5Bkey%5D=value", - ); - }); - - it("should handle empty arrays", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { emptyArray: [] }; - expect(createRequestUrl(baseUrl, queryParams)).toBe(baseUrl); - }); - - it("should handle empty objects", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { emptyObject: {} }; - expect(createRequestUrl(baseUrl, queryParams)).toBe(baseUrl); - }); - - it("should handle special characters in keys", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { "key with spaces": "value", "key[with]brackets": "value" }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?key%20with%20spaces=value&key%5Bwith%5Dbrackets=value", - ); - }); - - it("should handle URL with existing query parameters", () => { - const baseUrl = "https://api.example.com?existing=param"; - const queryParams = { new: "value" }; - expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?existing=param?new=value"); - }); - - it("should handle complex nested structures", () => { - const baseUrl = "https://api.example.com"; - const queryParams = { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], + expected: + "https://api.example.com?user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", + }, + { + description: "should handle arrays of objects", + baseUrl: BASE_URL, + queryParams: { + users: [ + { name: "John", age: 30 }, + { name: "Jane", age: 25 }, + ], + }, + expected: + "https://api.example.com?users%5Bname%5D=John&users%5Bage%5D=30&users%5Bname%5D=Jane&users%5Bage%5D=25", + }, + { + description: "should handle mixed arrays", + baseUrl: BASE_URL, + queryParams: { + mixed: ["string", 42, true, { key: "value" }], + }, + expected: "https://api.example.com?mixed=string&mixed=42&mixed=true&mixed%5Bkey%5D=value", + }, + { + description: "should handle empty arrays", + baseUrl: BASE_URL, + queryParams: { emptyArray: [] }, + expected: BASE_URL, + }, + { + description: "should handle empty objects", + baseUrl: BASE_URL, + queryParams: { emptyObject: {} }, + expected: BASE_URL, + }, + { + description: "should handle special characters in keys", + baseUrl: BASE_URL, + queryParams: { "key with spaces": "value", "key[with]brackets": "value" }, + expected: "https://api.example.com?key%20with%20spaces=value&key%5Bwith%5Dbrackets=value", + }, + { + description: "should handle URL with existing query parameters", + baseUrl: "https://api.example.com?existing=param", + queryParams: { new: "value" }, + expected: "https://api.example.com?existing=param?new=value", + }, + { + description: "should handle complex nested structures", + baseUrl: BASE_URL, + queryParams: { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, }, + sort: { field: "name", direction: "asc" }, }, - sort: { field: "name", direction: "asc" }, - }; - expect(createRequestUrl(baseUrl, queryParams)).toBe( - "https://api.example.com?filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - ); + expected: + "https://api.example.com?filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + }, + ]; + + testCases.forEach(({ description, baseUrl, queryParams, expected }) => { + it(description, () => { + expect(createRequestUrl(baseUrl, queryParams)).toBe(expected); + }); }); }); diff --git a/tests/unit/fetcher/getRequestBody.test.ts b/tests/unit/fetcher/getRequestBody.test.ts index e3da10c0..8a6c3a57 100644 --- a/tests/unit/fetcher/getRequestBody.test.ts +++ b/tests/unit/fetcher/getRequestBody.test.ts @@ -2,89 +2,70 @@ import { getRequestBody } from "../../../src/core/fetcher/getRequestBody"; import { RUNTIME } from "../../../src/core/runtime"; describe("Test getRequestBody", () => { - it("should stringify body if not FormData in Node environment", async () => { - if (RUNTIME.type === "node") { - const body = { key: "value" }; - const result = await getRequestBody({ - body, - type: "json", - }); - expect(result).toBe('{"key":"value"}'); - } - }); + interface TestCase { + description: string; + input: any; + type: "json" | "form" | "file" | "bytes" | "other"; + expected: any; + skipCondition?: () => boolean; + } - it("should return FormData in browser environment", async () => { - if (RUNTIME.type === "browser") { - const formData = new FormData(); - formData.append("key", "value"); - const result = await getRequestBody({ - body: formData, - type: "file", - }); - expect(result).toBe(formData); - } - }); - - it("should stringify body if not FormData in browser environment", async () => { - if (RUNTIME.type === "browser") { - const body = { key: "value" }; - const result = await getRequestBody({ - body, - type: "json", - }); - expect(result).toBe('{"key":"value"}'); - } - }); - - it("should return the Uint8Array", async () => { - const input = new Uint8Array([1, 2, 3]); - const result = await getRequestBody({ - body: input, + const testCases: TestCase[] = [ + { + description: "should stringify body if not FormData in Node environment", + input: { key: "value" }, + type: "json", + expected: '{"key":"value"}', + skipCondition: () => RUNTIME.type !== "node", + }, + { + description: "should stringify body if not FormData in browser environment", + input: { key: "value" }, + type: "json", + expected: '{"key":"value"}', + skipCondition: () => RUNTIME.type !== "browser", + }, + { + description: "should return the Uint8Array", + input: new Uint8Array([1, 2, 3]), type: "bytes", - }); - expect(result).toBe(input); - }); - - it("should serialize objects for form-urlencoded content type", async () => { - const input = { username: "johndoe", email: "john@example.com" }; - const result = await getRequestBody({ - body: input, + expected: new Uint8Array([1, 2, 3]), + }, + { + description: "should serialize objects for form-urlencoded content type", + input: { username: "johndoe", email: "john@example.com" }, type: "form", - }); - expect(result).toBe("username=johndoe&email=john%40example.com"); - }); - - it("should serialize complex nested objects and arrays for form-urlencoded content type", async () => { - const input = { - user: { - profile: { - name: "John Doe", - settings: { - theme: "dark", - notifications: true, + expected: "username=johndoe&email=john%40example.com", + }, + { + description: "should serialize complex nested objects and arrays for form-urlencoded content type", + input: { + user: { + profile: { + name: "John Doe", + settings: { + theme: "dark", + notifications: true, + }, }, + tags: ["admin", "user"], + contacts: [ + { type: "email", value: "john@example.com" }, + { type: "phone", value: "+1234567890" }, + ], }, - tags: ["admin", "user"], - contacts: [ - { type: "email", value: "john@example.com" }, - { type: "phone", value: "+1234567890" }, - ], - }, - filters: { - status: ["active", "pending"], - metadata: { - created: "2024-01-01", - categories: ["electronics", "books"], + filters: { + status: ["active", "pending"], + metadata: { + created: "2024-01-01", + categories: ["electronics", "books"], + }, }, + preferences: ["notifications", "updates"], }, - preferences: ["notifications", "updates"], - }; - const result = await getRequestBody({ - body: input, type: "form", - }); - expect(result).toBe( - "user%5Bprofile%5D%5Bname%5D=John%20Doe&" + + expected: + "user%5Bprofile%5D%5Bname%5D=John%20Doe&" + "user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark&" + "user%5Bprofile%5D%5Bsettings%5D%5Bnotifications%5D=true&" + "user%5Btags%5D=admin&" + @@ -100,24 +81,49 @@ describe("Test getRequestBody", () => { "filters%5Bmetadata%5D%5Bcategories%5D=books&" + "preferences=notifications&" + "preferences=updates", - ); - }); - - it("should return the input for pre-serialized form-urlencoded strings", async () => { - const input = "key=value&another=param"; - const result = await getRequestBody({ - body: input, + }, + { + description: "should return the input for pre-serialized form-urlencoded strings", + input: "key=value&another=param", type: "other", + expected: "key=value&another=param", + }, + { + description: "should JSON stringify objects", + input: { key: "value" }, + type: "json", + expected: '{"key":"value"}', + }, + ]; + + testCases.forEach(({ description, input, type, expected, skipCondition }) => { + it(description, async () => { + if (skipCondition?.()) { + return; + } + + const result = await getRequestBody({ + body: input, + type, + }); + + if (input instanceof Uint8Array) { + expect(result).toBe(input); + } else { + expect(result).toBe(expected); + } }); - expect(result).toBe(input); }); - it("should JSON stringify objects", async () => { - const input = { key: "value" }; - const result = await getRequestBody({ - body: input, - type: "json", - }); - expect(result).toBe('{"key":"value"}'); + it("should return FormData in browser environment", async () => { + if (RUNTIME.type === "browser") { + const formData = new FormData(); + formData.append("key", "value"); + const result = await getRequestBody({ + body: formData, + type: "file", + }); + expect(result).toBe(formData); + } }); }); diff --git a/tests/unit/fetcher/getResponseBody.test.ts b/tests/unit/fetcher/getResponseBody.test.ts index 151843ae..ad6be7fc 100644 --- a/tests/unit/fetcher/getResponseBody.test.ts +++ b/tests/unit/fetcher/getResponseBody.test.ts @@ -1,7 +1,61 @@ import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; + import { RUNTIME } from "../../../src/core/runtime"; describe("Test getResponseBody", () => { + interface SimpleTestCase { + description: string; + responseData: string | Record; + responseType?: "blob" | "sse" | "streaming" | "text"; + expected: any; + skipCondition?: () => boolean; + } + + const simpleTestCases: SimpleTestCase[] = [ + { + description: "should handle text response type", + responseData: "test text", + responseType: "text", + expected: "test text", + }, + { + description: "should handle JSON response", + responseData: { key: "value" }, + expected: { key: "value" }, + }, + { + description: "should handle empty response", + responseData: "", + expected: undefined, + }, + { + description: "should handle non-JSON response", + responseData: "invalid json", + expected: { + ok: false, + error: { + reason: "non-json", + statusCode: 200, + rawBody: "invalid json", + }, + }, + }, + ]; + + simpleTestCases.forEach(({ description, responseData, responseType, expected, skipCondition }) => { + it(description, async () => { + if (skipCondition?.()) { + return; + } + + const mockResponse = new Response( + typeof responseData === "string" ? responseData : JSON.stringify(responseData), + ); + const result = await getResponseBody(mockResponse, responseType); + expect(result).toEqual(expected); + }); + }); + it("should handle blob response type", async () => { const mockBlob = new Blob(["test"], { type: "text/plain" }); const mockResponse = new Response(mockBlob); @@ -20,7 +74,6 @@ describe("Test getResponseBody", () => { }); it("should handle streaming response type", async () => { - // Create a ReadableStream with some test data const encoder = new TextEncoder(); const testData = "test stream data"; const mockStream = new ReadableStream({ @@ -35,43 +88,10 @@ describe("Test getResponseBody", () => { expect(result).toBeInstanceOf(ReadableStream); - // Read and verify the stream content const reader = result.getReader(); const decoder = new TextDecoder(); const { value } = await reader.read(); const streamContent = decoder.decode(value); expect(streamContent).toBe(testData); }); - - it("should handle text response type", async () => { - const mockResponse = new Response("test text"); - const result = await getResponseBody(mockResponse, "text"); - expect(result).toBe("test text"); - }); - - it("should handle JSON response", async () => { - const mockJson = { key: "value" }; - const mockResponse = new Response(JSON.stringify(mockJson)); - const result = await getResponseBody(mockResponse); - expect(result).toEqual(mockJson); - }); - - it("should handle empty response", async () => { - const mockResponse = new Response(""); - const result = await getResponseBody(mockResponse); - expect(result).toBeUndefined(); - }); - - it("should handle non-JSON response", async () => { - const mockResponse = new Response("invalid json"); - const result = await getResponseBody(mockResponse); - expect(result).toEqual({ - ok: false, - error: { - reason: "non-json", - statusCode: 200, - rawBody: "invalid json", - }, - }); - }); }); diff --git a/tests/unit/fetcher/logging.test.ts b/tests/unit/fetcher/logging.test.ts new file mode 100644 index 00000000..366c9b6c --- /dev/null +++ b/tests/unit/fetcher/logging.test.ts @@ -0,0 +1,517 @@ +import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; + +function createMockLogger() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify(data), { + status, + statusText, + }), + ); +} + +function mockErrorResponse(data: unknown = { error: "Error" }, status = 404, statusText = "Not Found") { + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify(data), { + status, + statusText, + }), + ); +} + +describe("Fetcher Logging Integration", () => { + describe("Request Logging", () => { + it("should log successful request at debug level", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + headers: { "Content-Type": "application/json" }, + body: { test: "data" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "POST", + url: "https://example.com/api", + headers: expect.toContainHeaders({ + "Content-Type": "application/json", + }), + hasBody: true, + }), + ); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + method: "POST", + url: "https://example.com/api", + statusCode: 200, + }), + ); + }); + + it("should not log debug messages at info level for successful requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "info", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + }); + + it("should log request with body flag", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + hasBody: true, + }), + ); + }); + + it("should log request without body flag", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + hasBody: false, + }), + ); + }); + + it("should not log when silent mode is enabled", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: true, + }, + }); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).not.toHaveBeenCalled(); + expect(mockLogger.error).not.toHaveBeenCalled(); + }); + + it("should not log when no logging config is provided", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + }); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + }); + }); + + describe("Error Logging", () => { + it("should log 4xx errors at error level", async () => { + const mockLogger = createMockLogger(); + mockErrorResponse({ error: "Not found" }, 404, "Not Found"); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + statusCode: 404, + }), + ); + }); + + it("should log 5xx errors at error level", async () => { + const mockLogger = createMockLogger(); + mockErrorResponse({ error: "Internal error" }, 500, "Internal Server Error"); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + statusCode: 500, + }), + ); + }); + + it("should log aborted request errors", async () => { + const mockLogger = createMockLogger(); + + const abortController = new AbortController(); + abortController.abort(); + + global.fetch = vi.fn().mockRejectedValue(new Error("Aborted")); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + abortSignal: abortController.signal, + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request was aborted", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + }), + ); + }); + + it("should log timeout errors", async () => { + const mockLogger = createMockLogger(); + + const timeoutError = new Error("Request timeout"); + timeoutError.name = "AbortError"; + + global.fetch = vi.fn().mockRejectedValue(timeoutError); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request timed out", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + timeoutMs: undefined, + }), + ); + }); + + it("should log unknown errors", async () => { + const mockLogger = createMockLogger(); + + const unknownError = new Error("Unknown error"); + + global.fetch = vi.fn().mockRejectedValue(unknownError); + + const result = await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(result.ok).toBe(false); + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error", + expect.objectContaining({ + method: "GET", + url: "https://example.com/api", + errorMessage: "Unknown error", + }), + ); + }); + }); + + describe("Logging with Redaction", () => { + it("should redact sensitive data in error logs", async () => { + const mockLogger = createMockLogger(); + mockErrorResponse({ error: "Unauthorized" }, 401, "Unauthorized"); + + await fetcherImpl({ + url: "https://example.com/api?api_key=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + url: "https://example.com/api?api_key=[REDACTED]", + }), + ); + }); + }); + + describe("Different HTTP Methods", () => { + it("should log GET requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "GET", + }), + ); + }); + + it("should log POST requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse({ data: "test" }, 201, "Created"); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "POST", + }), + ); + }); + + it("should log PUT requests", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "PUT", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "PUT", + }), + ); + }); + + it("should log DELETE requests", async () => { + const mockLogger = createMockLogger(); + global.fetch = vi.fn().mockResolvedValue( + new Response(null, { + status: 200, + statusText: "OK", + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "DELETE", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + method: "DELETE", + }), + ); + }); + }); + + describe("Status Code Logging", () => { + it("should log 2xx success status codes", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse({ data: "test" }, 201, "Created"); + + await fetcherImpl({ + url: "https://example.com/api", + method: "POST", + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + statusCode: 201, + }), + ); + }); + + it("should log 3xx redirect status codes as success", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse({ data: "test" }, 301, "Moved Permanently"); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + statusCode: 301, + }), + ); + }); + }); +}); diff --git a/tests/unit/fetcher/makeRequest.test.ts b/tests/unit/fetcher/makeRequest.test.ts index f6203cdc..ea49466a 100644 --- a/tests/unit/fetcher/makeRequest.test.ts +++ b/tests/unit/fetcher/makeRequest.test.ts @@ -1,3 +1,4 @@ +import type { Mock } from "vitest"; import { makeRequest } from "../../../src/core/fetcher/makeRequest"; describe("Test makeRequest", () => { @@ -6,7 +7,7 @@ describe("Test makeRequest", () => { const mockHeaders = { "Content-Type": "application/json" }; const mockBody = JSON.stringify({ key: "value" }); - let mockFetch: import("vitest").Mock; + let mockFetch: Mock; beforeEach(() => { mockFetch = vi.fn(); diff --git a/tests/unit/fetcher/redacting.test.ts b/tests/unit/fetcher/redacting.test.ts new file mode 100644 index 00000000..d599376b --- /dev/null +++ b/tests/unit/fetcher/redacting.test.ts @@ -0,0 +1,1115 @@ +import { fetcherImpl } from "../../../src/core/fetcher/Fetcher"; + +function createMockLogger() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +function mockSuccessResponse(data: unknown = { data: "test" }, status = 200, statusText = "OK") { + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify(data), { + status, + statusText, + }), + ); +} + +describe("Redacting Logic", () => { + describe("Header Redaction", () => { + it("should redact authorization header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { Authorization: "Bearer secret-token-12345" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + Authorization: "[REDACTED]", + }), + }), + ); + }); + + it("should redact api-key header (case-insensitive)", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "X-API-KEY": "secret-api-key" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "X-API-KEY": "[REDACTED]", + }), + }), + ); + }); + + it("should redact cookie header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { Cookie: "session=abc123; token=xyz789" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + Cookie: "[REDACTED]", + }), + }), + ); + }); + + it("should redact x-auth-token header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "x-auth-token": "auth-token-12345" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "x-auth-token": "[REDACTED]", + }), + }), + ); + }); + + it("should redact proxy-authorization header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "Proxy-Authorization": "Basic credentials" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "Proxy-Authorization": "[REDACTED]", + }), + }), + ); + }); + + it("should redact x-csrf-token header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "X-CSRF-Token": "csrf-token-abc" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "X-CSRF-Token": "[REDACTED]", + }), + }), + ); + }); + + it("should redact www-authenticate header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "WWW-Authenticate": "Bearer realm=example" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "WWW-Authenticate": "[REDACTED]", + }), + }), + ); + }); + + it("should redact x-session-token header", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { "X-Session-Token": "session-token-xyz" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "X-Session-Token": "[REDACTED]", + }), + }), + ); + }); + + it("should not redact non-sensitive headers", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { + "Content-Type": "application/json", + "User-Agent": "Test/1.0", + Accept: "application/json", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + "Content-Type": "application/json", + "User-Agent": "Test/1.0", + Accept: "application/json", + }), + }), + ); + }); + + it("should redact multiple sensitive headers at once", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + headers: { + Authorization: "Bearer token", + "X-API-Key": "api-key", + Cookie: "session=123", + "Content-Type": "application/json", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + headers: expect.toContainHeaders({ + Authorization: "[REDACTED]", + "X-API-Key": "[REDACTED]", + Cookie: "[REDACTED]", + "Content-Type": "application/json", + }), + }), + ); + }); + }); + + describe("Response Header Redaction", () => { + it("should redact Set-Cookie in response headers", async () => { + const mockLogger = createMockLogger(); + + const mockHeaders = new Headers(); + mockHeaders.set("Set-Cookie", "session=abc123; HttpOnly; Secure"); + mockHeaders.set("Content-Type", "application/json"); + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + headers: mockHeaders, + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + responseHeaders: expect.toContainHeaders({ + "set-cookie": "[REDACTED]", + "content-type": "application/json", + }), + }), + ); + }); + + it("should redact authorization in response headers", async () => { + const mockLogger = createMockLogger(); + + const mockHeaders = new Headers(); + mockHeaders.set("Authorization", "Bearer token-123"); + mockHeaders.set("Content-Type", "application/json"); + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ data: "test" }), { + status: 200, + statusText: "OK", + headers: mockHeaders, + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "HTTP request succeeded", + expect.objectContaining({ + responseHeaders: expect.toContainHeaders({ + authorization: "[REDACTED]", + "content-type": "application/json", + }), + }), + ); + }); + + it("should redact response headers in error responses", async () => { + const mockLogger = createMockLogger(); + + const mockHeaders = new Headers(); + mockHeaders.set("WWW-Authenticate", "Bearer realm=example"); + mockHeaders.set("Content-Type", "application/json"); + + global.fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + statusText: "Unauthorized", + headers: mockHeaders, + }), + ); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "error", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.error).toHaveBeenCalledWith( + "HTTP request failed with error status", + expect.objectContaining({ + responseHeaders: expect.toContainHeaders({ + "www-authenticate": "[REDACTED]", + "content-type": "application/json", + }), + }), + ); + }); + }); + + describe("Query Parameter Redaction", () => { + it("should redact api_key query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { api_key: "secret-key" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + api_key: "[REDACTED]", + }), + }), + ); + }); + + it("should redact token query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { token: "secret-token" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + token: "[REDACTED]", + }), + }), + ); + }); + + it("should redact access_token query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { access_token: "secret-access-token" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + access_token: "[REDACTED]", + }), + }), + ); + }); + + it("should redact password query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { password: "secret-password" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + password: "[REDACTED]", + }), + }), + ); + }); + + it("should redact secret query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { secret: "secret-value" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + secret: "[REDACTED]", + }), + }), + ); + }); + + it("should redact session_id query parameter", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { session_id: "session-123" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + session_id: "[REDACTED]", + }), + }), + ); + }); + + it("should not redact non-sensitive query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { + page: "1", + limit: "10", + sort: "name", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + page: "1", + limit: "10", + sort: "name", + }), + }), + ); + }); + + it("should not redact parameters containing 'auth' substring like 'author'", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { + author: "john", + authenticate: "false", + authorization_level: "user", + }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + author: "john", + authenticate: "false", + authorization_level: "user", + }), + }), + ); + }); + + it("should handle undefined query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: undefined, + }), + ); + }); + + it("should redact case-insensitive query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + queryParameters: { API_KEY: "secret-key", Token: "secret-token" }, + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + queryParameters: expect.objectContaining({ + API_KEY: "[REDACTED]", + Token: "[REDACTED]", + }), + }), + ); + }); + }); + + describe("URL Redaction", () => { + it("should redact credentials in URL", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user:password@example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@example.com/api", + }), + ); + }); + + it("should redact api_key in query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?api_key=secret-key&page=1", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?api_key=[REDACTED]&page=1", + }), + ); + }); + + it("should redact token in query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?token=secret-token", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?token=[REDACTED]", + }), + ); + }); + + it("should redact password in query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?username=user&password=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?username=user&password=[REDACTED]", + }), + ); + }); + + it("should not redact non-sensitive query strings", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?page=1&limit=10&sort=name", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?page=1&limit=10&sort=name", + }), + ); + }); + + it("should not redact URL parameters containing 'auth' substring like 'author'", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?author=john&authenticate=false&page=1", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?author=john&authenticate=false&page=1", + }), + ); + }); + + it("should handle URL with fragment", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?token=secret#section", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?token=[REDACTED]#section", + }), + ); + }); + + it("should redact URL-encoded query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?api%5Fkey=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?api%5Fkey=[REDACTED]", + }), + ); + }); + + it("should handle URL without query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api", + }), + ); + }); + + it("should handle empty query string", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?", + }), + ); + }); + + it("should redact multiple sensitive parameters in URL", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?api_key=secret1&token=secret2&page=1", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?api_key=[REDACTED]&token=[REDACTED]&page=1", + }), + ); + }); + + it("should redact both credentials and query parameters", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user:pass@example.com/api?token=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@example.com/api?token=[REDACTED]", + }), + ); + }); + + it("should use fast path for URLs without sensitive keywords", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?page=1&limit=10&sort=name&filter=value", + }), + ); + }); + + it("should handle query parameter without value", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?flag&token=secret", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?flag&token=[REDACTED]", + }), + ); + }); + + it("should handle URL with multiple @ symbols in credentials", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user@example.com:pass@host.com/api", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@host.com/api", + }), + ); + }); + + it("should handle URL with @ in query parameter but not in credentials", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://example.com/api?email=user@example.com", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://example.com/api?email=user@example.com", + }), + ); + }); + + it("should handle URL with both credentials and @ in path", async () => { + const mockLogger = createMockLogger(); + mockSuccessResponse(); + + await fetcherImpl({ + url: "https://user:pass@example.com/users/@username", + method: "GET", + responseType: "json", + maxRetries: 0, + logging: { + level: "debug", + logger: mockLogger, + silent: false, + }, + }); + + expect(mockLogger.debug).toHaveBeenCalledWith( + "Making HTTP request", + expect.objectContaining({ + url: "https://[REDACTED]@example.com/users/@username", + }), + ); + }); + }); +}); diff --git a/tests/unit/fetcher/requestWithRetries.test.ts b/tests/unit/fetcher/requestWithRetries.test.ts index 7d46082d..d2266136 100644 --- a/tests/unit/fetcher/requestWithRetries.test.ts +++ b/tests/unit/fetcher/requestWithRetries.test.ts @@ -1,15 +1,15 @@ +import type { Mock, MockInstance } from "vitest"; import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; describe("requestWithRetries", () => { - let mockFetch: import("vitest").Mock; + let mockFetch: Mock; let originalMathRandom: typeof Math.random; - let setTimeoutSpy: import("vitest").MockInstance; + let setTimeoutSpy: MockInstance; beforeEach(() => { mockFetch = vi.fn(); originalMathRandom = Math.random; - // Mock Math.random for consistent jitter Math.random = vi.fn(() => 0.5); vi.useFakeTimers({ @@ -99,6 +99,67 @@ describe("requestWithRetries", () => { } }); + interface RetryHeaderTestCase { + description: string; + headerName: string; + headerValue: string | (() => string); + expectedDelayMin: number; + expectedDelayMax: number; + } + + const retryHeaderTests: RetryHeaderTestCase[] = [ + { + description: "should respect retry-after header with seconds value", + headerName: "retry-after", + headerValue: "5", + expectedDelayMin: 4000, + expectedDelayMax: 6000, + }, + { + description: "should respect retry-after header with HTTP date value", + headerName: "retry-after", + headerValue: () => new Date(Date.now() + 3000).toUTCString(), + expectedDelayMin: 2000, + expectedDelayMax: 4000, + }, + { + description: "should respect x-ratelimit-reset header", + headerName: "x-ratelimit-reset", + headerValue: () => Math.floor((Date.now() + 4000) / 1000).toString(), + expectedDelayMin: 3000, + expectedDelayMax: 6000, + }, + ]; + + retryHeaderTests.forEach(({ description, headerName, headerValue, expectedDelayMin, expectedDelayMax }) => { + it(description, async () => { + setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const value = typeof headerValue === "function" ? headerValue() : headerValue; + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ [headerName]: value }), + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await vi.runAllTimersAsync(); + const response = await responsePromise; + + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); + const actualDelay = setTimeoutSpy.mock.calls[0][1]; + expect(actualDelay).toBeGreaterThan(expectedDelayMin); + expect(actualDelay).toBeLessThan(expectedDelayMax); + expect(response.status).toBe(200); + }); + }); + it("should apply correct exponential backoff with jitter", async () => { setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { process.nextTick(callback); @@ -113,7 +174,6 @@ describe("requestWithRetries", () => { await vi.runAllTimersAsync(); await responsePromise; - // Verify setTimeout calls expect(setTimeoutSpy).toHaveBeenCalledTimes(expectedDelays.length); expectedDelays.forEach((delay, index) => { @@ -145,85 +205,6 @@ describe("requestWithRetries", () => { expect(response2.status).toBe(200); }); - it("should respect retry-after header with seconds value", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ "retry-after": "5" }), - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 5000); // 5 seconds = 5000ms - expect(response.status).toBe(200); - }); - - it("should respect retry-after header with HTTP date value", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const futureDate = new Date(Date.now() + 3000); // 3 seconds from now - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ "retry-after": futureDate.toUTCString() }), - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - // Should use the date-based delay (approximately 3000ms, but with jitter) - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); - const actualDelay = setTimeoutSpy.mock.calls[0][1]; - expect(actualDelay).toBeGreaterThan(2000); - expect(actualDelay).toBeLessThan(4000); - expect(response.status).toBe(200); - }); - - it("should respect x-ratelimit-reset header", async () => { - setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { - process.nextTick(callback); - return null as any; - }); - - const resetTime = Math.floor((Date.now() + 4000) / 1000); // 4 seconds from now in Unix timestamp - mockFetch - .mockResolvedValueOnce( - new Response("", { - status: 429, - headers: new Headers({ "x-ratelimit-reset": resetTime.toString() }), - }), - ) - .mockResolvedValueOnce(new Response("", { status: 200 })); - - const responsePromise = requestWithRetries(() => mockFetch(), 1); - await vi.runAllTimersAsync(); - const response = await responsePromise; - - // Should use the x-ratelimit-reset delay (approximately 4000ms, but with positive jitter) - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); - const actualDelay = setTimeoutSpy.mock.calls[0][1]; - expect(actualDelay).toBeGreaterThan(3000); - expect(actualDelay).toBeLessThan(6000); - expect(response.status).toBe(200); - }); - it("should cap delay at MAX_RETRY_DELAY for large header values", async () => { setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { process.nextTick(callback); @@ -243,8 +224,7 @@ describe("requestWithRetries", () => { await vi.runAllTimersAsync(); const response = await responsePromise; - // Should be capped at MAX_RETRY_DELAY (60000ms) with jitter applied - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 60000); // Exactly MAX_RETRY_DELAY since jitter with 0.5 random keeps it at 60000 + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 60000); expect(response.status).toBe(200); }); }); diff --git a/tests/unit/logging/logger.test.ts b/tests/unit/logging/logger.test.ts new file mode 100644 index 00000000..2e0b5fe5 --- /dev/null +++ b/tests/unit/logging/logger.test.ts @@ -0,0 +1,454 @@ +import { ConsoleLogger, createLogger, Logger, LogLevel } from "../../../src/core/logging/logger"; + +function createMockLogger() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +describe("Logger", () => { + describe("LogLevel", () => { + it("should have correct log levels", () => { + expect(LogLevel.Debug).toBe("debug"); + expect(LogLevel.Info).toBe("info"); + expect(LogLevel.Warn).toBe("warn"); + expect(LogLevel.Error).toBe("error"); + }); + }); + + describe("ConsoleLogger", () => { + let consoleLogger: ConsoleLogger; + let consoleSpy: { + debug: ReturnType; + info: ReturnType; + warn: ReturnType; + error: ReturnType; + }; + + beforeEach(() => { + consoleLogger = new ConsoleLogger(); + consoleSpy = { + debug: vi.spyOn(console, "debug").mockImplementation(() => {}), + info: vi.spyOn(console, "info").mockImplementation(() => {}), + warn: vi.spyOn(console, "warn").mockImplementation(() => {}), + error: vi.spyOn(console, "error").mockImplementation(() => {}), + }; + }); + + afterEach(() => { + consoleSpy.debug.mockRestore(); + consoleSpy.info.mockRestore(); + consoleSpy.warn.mockRestore(); + consoleSpy.error.mockRestore(); + }); + + it("should log debug messages", () => { + consoleLogger.debug("debug message", { data: "test" }); + expect(consoleSpy.debug).toHaveBeenCalledWith("debug message", { data: "test" }); + }); + + it("should log info messages", () => { + consoleLogger.info("info message", { data: "test" }); + expect(consoleSpy.info).toHaveBeenCalledWith("info message", { data: "test" }); + }); + + it("should log warn messages", () => { + consoleLogger.warn("warn message", { data: "test" }); + expect(consoleSpy.warn).toHaveBeenCalledWith("warn message", { data: "test" }); + }); + + it("should log error messages", () => { + consoleLogger.error("error message", { data: "test" }); + expect(consoleSpy.error).toHaveBeenCalledWith("error message", { data: "test" }); + }); + + it("should handle multiple arguments", () => { + consoleLogger.debug("message", "arg1", "arg2", { key: "value" }); + expect(consoleSpy.debug).toHaveBeenCalledWith("message", "arg1", "arg2", { key: "value" }); + }); + }); + + describe("Logger with level filtering", () => { + let mockLogger: { + debug: ReturnType; + info: ReturnType; + warn: ReturnType; + error: ReturnType; + }; + + beforeEach(() => { + mockLogger = createMockLogger(); + }); + + describe("Debug level", () => { + it("should log all levels when set to debug", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).toHaveBeenCalledWith("debug"); + expect(mockLogger.info).toHaveBeenCalledWith("info"); + expect(mockLogger.warn).toHaveBeenCalledWith("warn"); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(true); + expect(logger.isInfo()).toBe(true); + expect(logger.isWarn()).toBe(true); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Info level", () => { + it("should log info, warn, and error when set to info", () => { + const logger = new Logger({ + level: LogLevel.Info, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).toHaveBeenCalledWith("info"); + expect(mockLogger.warn).toHaveBeenCalledWith("warn"); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Info, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(true); + expect(logger.isWarn()).toBe(true); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Warn level", () => { + it("should log warn and error when set to warn", () => { + const logger = new Logger({ + level: LogLevel.Warn, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).toHaveBeenCalledWith("warn"); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Warn, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(false); + expect(logger.isWarn()).toBe(true); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Error level", () => { + it("should only log error when set to error", () => { + const logger = new Logger({ + level: LogLevel.Error, + logger: mockLogger, + silent: false, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).not.toHaveBeenCalled(); + expect(mockLogger.error).toHaveBeenCalledWith("error"); + }); + + it("should report correct level checks", () => { + const logger = new Logger({ + level: LogLevel.Error, + logger: mockLogger, + silent: false, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(false); + expect(logger.isWarn()).toBe(false); + expect(logger.isError()).toBe(true); + }); + }); + + describe("Silent mode", () => { + it("should not log anything when silent is true", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: true, + }); + + logger.debug("debug"); + logger.info("info"); + logger.warn("warn"); + logger.error("error"); + + expect(mockLogger.debug).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalled(); + expect(mockLogger.warn).not.toHaveBeenCalled(); + expect(mockLogger.error).not.toHaveBeenCalled(); + }); + + it("should report all level checks as false when silent", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: true, + }); + + expect(logger.isDebug()).toBe(false); + expect(logger.isInfo()).toBe(false); + expect(logger.isWarn()).toBe(false); + expect(logger.isError()).toBe(false); + }); + }); + + describe("shouldLog", () => { + it("should correctly determine if level should be logged", () => { + const logger = new Logger({ + level: LogLevel.Info, + logger: mockLogger, + silent: false, + }); + + expect(logger.shouldLog(LogLevel.Debug)).toBe(false); + expect(logger.shouldLog(LogLevel.Info)).toBe(true); + expect(logger.shouldLog(LogLevel.Warn)).toBe(true); + expect(logger.shouldLog(LogLevel.Error)).toBe(true); + }); + + it("should return false for all levels when silent", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: true, + }); + + expect(logger.shouldLog(LogLevel.Debug)).toBe(false); + expect(logger.shouldLog(LogLevel.Info)).toBe(false); + expect(logger.shouldLog(LogLevel.Warn)).toBe(false); + expect(logger.shouldLog(LogLevel.Error)).toBe(false); + }); + }); + + describe("Multiple arguments", () => { + it("should pass multiple arguments to logger", () => { + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("message", "arg1", { key: "value" }, 123); + expect(mockLogger.debug).toHaveBeenCalledWith("message", "arg1", { key: "value" }, 123); + }); + }); + }); + + describe("createLogger", () => { + it("should return default logger when no config provided", () => { + const logger = createLogger(); + expect(logger).toBeInstanceOf(Logger); + }); + + it("should return same logger instance when Logger is passed", () => { + const customLogger = new Logger({ + level: LogLevel.Debug, + logger: new ConsoleLogger(), + silent: false, + }); + + const result = createLogger(customLogger); + expect(result).toBe(customLogger); + }); + + it("should create logger with custom config", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + level: LogLevel.Warn, + logger: mockLogger, + silent: false, + }); + + expect(logger).toBeInstanceOf(Logger); + logger.warn("test"); + expect(mockLogger.warn).toHaveBeenCalledWith("test"); + }); + + it("should use default values for missing config", () => { + const logger = createLogger({}); + expect(logger).toBeInstanceOf(Logger); + }); + + it("should override default level", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("test"); + expect(mockLogger.debug).toHaveBeenCalledWith("test"); + }); + + it("should override default silent mode", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + logger: mockLogger, + silent: false, + }); + + logger.info("test"); + expect(mockLogger.info).toHaveBeenCalledWith("test"); + }); + + it("should use provided logger implementation", () => { + const customLogger = createMockLogger(); + + const logger = createLogger({ + logger: customLogger, + level: LogLevel.Debug, + silent: false, + }); + + logger.debug("test"); + expect(customLogger.debug).toHaveBeenCalledWith("test"); + }); + + it("should default to silent: true", () => { + const mockLogger = createMockLogger(); + + const logger = createLogger({ + logger: mockLogger, + level: LogLevel.Debug, + }); + + logger.debug("test"); + expect(mockLogger.debug).not.toHaveBeenCalled(); + }); + }); + + describe("Default logger", () => { + it("should have silent: true by default", () => { + const logger = createLogger(); + expect(logger.shouldLog(LogLevel.Info)).toBe(false); + }); + + it("should not log when using default logger", () => { + const logger = createLogger(); + + logger.info("test"); + expect(logger.isInfo()).toBe(false); + }); + }); + + describe("Edge cases", () => { + it("should handle empty message", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug(""); + expect(mockLogger.debug).toHaveBeenCalledWith(""); + }); + + it("should handle no arguments", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + logger.debug("message"); + expect(mockLogger.debug).toHaveBeenCalledWith("message"); + }); + + it("should handle complex objects", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Debug, + logger: mockLogger, + silent: false, + }); + + const complexObject = { + nested: { key: "value" }, + array: [1, 2, 3], + fn: () => "test", + }; + + logger.debug("message", complexObject); + expect(mockLogger.debug).toHaveBeenCalledWith("message", complexObject); + }); + + it("should handle errors as arguments", () => { + const mockLogger = createMockLogger(); + + const logger = new Logger({ + level: LogLevel.Error, + logger: mockLogger, + silent: false, + }); + + const error = new Error("Test error"); + logger.error("Error occurred", error); + expect(mockLogger.error).toHaveBeenCalledWith("Error occurred", error); + }); + }); +}); diff --git a/tests/unit/schemas/primitives/never.test.ts b/tests/unit/schemas/primitives/never.test.ts new file mode 100644 index 00000000..1d18eba0 --- /dev/null +++ b/tests/unit/schemas/primitives/never.test.ts @@ -0,0 +1,54 @@ +import { never } from "../../../../src/core/schemas/builders"; + +describe("never", () => { + it("always fails to parse", () => { + const schema = never(); + const result = schema.parse("test"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.errors).toHaveLength(1); + expect(result.errors[0]?.message).toBe("Expected never"); + } + }); + + it("always fails to json", () => { + const schema = never(); + const result = schema.json("test"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.errors).toHaveLength(1); + expect(result.errors[0]?.message).toBe("Expected never"); + } + }); + + it("fails with any value including undefined", () => { + const schema = never(); + expect(schema.parse(undefined).ok).toBe(false); + expect(schema.parse(null).ok).toBe(false); + expect(schema.parse(0).ok).toBe(false); + expect(schema.parse("").ok).toBe(false); + expect(schema.parse({}).ok).toBe(false); + expect(schema.parse([]).ok).toBe(false); + }); + + it("works when called without options parameter", () => { + const schema = never(); + // This tests that the default = {} parameter works correctly + const result = schema.parse("test"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.errors).toHaveLength(1); + expect(result.errors[0]?.message).toBe("Expected never"); + expect(result.errors[0]?.path).toEqual([]); + } + }); + + it("succeeds with skipValidation", () => { + const schema = never(); + const result = schema.parse("test", { skipValidation: true }); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value).toBe("test"); + } + }); +}); diff --git a/tests/unit/url/join.test.ts b/tests/unit/url/join.test.ts index 1956a8c0..123488f0 100644 --- a/tests/unit/url/join.test.ts +++ b/tests/unit/url/join.test.ts @@ -1,88 +1,223 @@ import { join } from "../../../src/core/url/index"; describe("join", () => { - describe("basic functionality", () => { - it("should return empty string for empty base", () => { - expect(join("")).toBe(""); - expect(join("", "path")).toBe(""); - }); + interface TestCase { + description: string; + base: string; + segments: string[]; + expected: string; + } - it("should handle single segment", () => { - expect(join("base", "segment")).toBe("base/segment"); - expect(join("base/", "segment")).toBe("base/segment"); - expect(join("base", "/segment")).toBe("base/segment"); - expect(join("base/", "/segment")).toBe("base/segment"); - }); + describe("basic functionality", () => { + const basicTests: TestCase[] = [ + { description: "should return empty string for empty base", base: "", segments: [], expected: "" }, + { + description: "should return empty string for empty base with path", + base: "", + segments: ["path"], + expected: "", + }, + { + description: "should handle single segment", + base: "base", + segments: ["segment"], + expected: "base/segment", + }, + { + description: "should handle single segment with trailing slash on base", + base: "base/", + segments: ["segment"], + expected: "base/segment", + }, + { + description: "should handle single segment with leading slash", + base: "base", + segments: ["/segment"], + expected: "base/segment", + }, + { + description: "should handle single segment with both slashes", + base: "base/", + segments: ["/segment"], + expected: "base/segment", + }, + { + description: "should handle multiple segments", + base: "base", + segments: ["path1", "path2", "path3"], + expected: "base/path1/path2/path3", + }, + { + description: "should handle multiple segments with slashes", + base: "base/", + segments: ["/path1/", "/path2/", "/path3/"], + expected: "base/path1/path2/path3/", + }, + ]; - it("should handle multiple segments", () => { - expect(join("base", "path1", "path2", "path3")).toBe("base/path1/path2/path3"); - expect(join("base/", "/path1/", "/path2/", "/path3/")).toBe("base/path1/path2/path3/"); + basicTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); }); }); describe("URL handling", () => { - it("should handle absolute URLs", () => { - expect(join("https://example.com", "api", "v1")).toBe("https://example.com/api/v1"); - expect(join("https://example.com/", "/api/", "/v1/")).toBe("https://example.com/api/v1/"); - expect(join("https://example.com/base", "api", "v1")).toBe("https://example.com/base/api/v1"); - }); + const urlTests: TestCase[] = [ + { + description: "should handle absolute URLs", + base: "https://example.com", + segments: ["api", "v1"], + expected: "https://example.com/api/v1", + }, + { + description: "should handle absolute URLs with slashes", + base: "https://example.com/", + segments: ["/api/", "/v1/"], + expected: "https://example.com/api/v1/", + }, + { + description: "should handle absolute URLs with base path", + base: "https://example.com/base", + segments: ["api", "v1"], + expected: "https://example.com/base/api/v1", + }, + { + description: "should preserve URL query parameters", + base: "https://example.com?query=1", + segments: ["api"], + expected: "https://example.com/api?query=1", + }, + { + description: "should preserve URL fragments", + base: "https://example.com#fragment", + segments: ["api"], + expected: "https://example.com/api#fragment", + }, + { + description: "should preserve URL query and fragments", + base: "https://example.com?query=1#fragment", + segments: ["api"], + expected: "https://example.com/api?query=1#fragment", + }, + { + description: "should handle http protocol", + base: "http://example.com", + segments: ["api"], + expected: "http://example.com/api", + }, + { + description: "should handle ftp protocol", + base: "ftp://example.com", + segments: ["files"], + expected: "ftp://example.com/files", + }, + { + description: "should handle ws protocol", + base: "ws://example.com", + segments: ["socket"], + expected: "ws://example.com/socket", + }, + { + description: "should fallback to path joining for malformed URLs", + base: "not-a-url://", + segments: ["path"], + expected: "not-a-url:///path", + }, + ]; - it("should preserve URL query parameters and fragments", () => { - expect(join("https://example.com?query=1", "api")).toBe("https://example.com/api?query=1"); - expect(join("https://example.com#fragment", "api")).toBe("https://example.com/api#fragment"); - expect(join("https://example.com?query=1#fragment", "api")).toBe( - "https://example.com/api?query=1#fragment", - ); - }); - - it("should handle different protocols", () => { - expect(join("http://example.com", "api")).toBe("http://example.com/api"); - expect(join("ftp://example.com", "files")).toBe("ftp://example.com/files"); - expect(join("ws://example.com", "socket")).toBe("ws://example.com/socket"); - }); - - it("should fallback to path joining for malformed URLs", () => { - expect(join("not-a-url://", "path")).toBe("not-a-url:///path"); + urlTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); }); }); describe("edge cases", () => { - it("should handle empty segments", () => { - expect(join("base", "", "path")).toBe("base/path"); - expect(join("base", null as any, "path")).toBe("base/path"); - expect(join("base", undefined as any, "path")).toBe("base/path"); - }); - - it("should handle segments with only slashes", () => { - expect(join("base", "/", "path")).toBe("base/path"); - expect(join("base", "//", "path")).toBe("base/path"); - }); - - it("should handle base paths with trailing slashes", () => { - expect(join("base/", "path")).toBe("base/path"); - }); + const edgeCaseTests: TestCase[] = [ + { + description: "should handle empty segments", + base: "base", + segments: ["", "path"], + expected: "base/path", + }, + { + description: "should handle null segments", + base: "base", + segments: [null as any, "path"], + expected: "base/path", + }, + { + description: "should handle undefined segments", + base: "base", + segments: [undefined as any, "path"], + expected: "base/path", + }, + { + description: "should handle segments with only single slash", + base: "base", + segments: ["/", "path"], + expected: "base/path", + }, + { + description: "should handle segments with only double slash", + base: "base", + segments: ["//", "path"], + expected: "base/path", + }, + { + description: "should handle base paths with trailing slashes", + base: "base/", + segments: ["path"], + expected: "base/path", + }, + { + description: "should handle complex nested paths", + base: "api/v1/", + segments: ["/users/", "/123/", "/profile"], + expected: "api/v1/users/123/profile", + }, + ]; - it("should handle complex nested paths", () => { - expect(join("api/v1/", "/users/", "/123/", "/profile")).toBe("api/v1/users/123/profile"); + edgeCaseTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); }); }); describe("real-world scenarios", () => { - it("should handle API endpoint construction", () => { - const baseUrl = "https://api.example.com/v1"; - expect(join(baseUrl, "users", "123", "posts")).toBe("https://api.example.com/v1/users/123/posts"); - }); - - it("should handle file path construction", () => { - expect(join("/var/www", "html", "assets", "images")).toBe("/var/www/html/assets/images"); - }); + const realWorldTests: TestCase[] = [ + { + description: "should handle API endpoint construction", + base: "https://api.example.com/v1", + segments: ["users", "123", "posts"], + expected: "https://api.example.com/v1/users/123/posts", + }, + { + description: "should handle file path construction", + base: "/var/www", + segments: ["html", "assets", "images"], + expected: "/var/www/html/assets/images", + }, + { + description: "should handle relative path construction", + base: "../parent", + segments: ["child", "grandchild"], + expected: "../parent/child/grandchild", + }, + { + description: "should handle Windows-style paths", + base: "C:\\Users", + segments: ["Documents", "file.txt"], + expected: "C:\\Users/Documents/file.txt", + }, + ]; - it("should handle relative path construction", () => { - expect(join("../parent", "child", "grandchild")).toBe("../parent/child/grandchild"); - }); - - it("should handle Windows-style paths", () => { - expect(join("C:\\Users", "Documents", "file.txt")).toBe("C:\\Users/Documents/file.txt"); + realWorldTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); }); }); @@ -100,21 +235,50 @@ describe("join", () => { }); describe("trailing slash preservation", () => { - it("should preserve trailing slash on final result when base has trailing slash and no segments", () => { - expect(join("https://api.example.com/")).toBe("https://api.example.com/"); - expect(join("https://api.example.com/v1/")).toBe("https://api.example.com/v1/"); - }); - - it("should preserve trailing slash when last segment has trailing slash", () => { - expect(join("https://api.example.com", "users/")).toBe("https://api.example.com/users/"); - expect(join("api/v1", "users/")).toBe("api/v1/users/"); - }); + const trailingSlashTests: TestCase[] = [ + { + description: + "should preserve trailing slash on final result when base has trailing slash and no segments", + base: "https://api.example.com/", + segments: [], + expected: "https://api.example.com/", + }, + { + description: "should preserve trailing slash on v1 path", + base: "https://api.example.com/v1/", + segments: [], + expected: "https://api.example.com/v1/", + }, + { + description: "should preserve trailing slash when last segment has trailing slash", + base: "https://api.example.com", + segments: ["users/"], + expected: "https://api.example.com/users/", + }, + { + description: "should preserve trailing slash with relative path", + base: "api/v1", + segments: ["users/"], + expected: "api/v1/users/", + }, + { + description: "should preserve trailing slash with multiple segments", + base: "https://api.example.com", + segments: ["v1", "collections/"], + expected: "https://api.example.com/v1/collections/", + }, + { + description: "should preserve trailing slash with base path", + base: "base", + segments: ["path1", "path2/"], + expected: "base/path1/path2/", + }, + ]; - it("should preserve trailing slash with multiple segments where last has trailing slash", () => { - expect(join("https://api.example.com", "v1", "collections/")).toBe( - "https://api.example.com/v1/collections/", - ); - expect(join("base", "path1", "path2/")).toBe("base/path1/path2/"); + trailingSlashTests.forEach(({ description, base, segments, expected }) => { + it(description, () => { + expect(join(base, ...segments)).toBe(expected); + }); }); }); }); diff --git a/tests/unit/url/qs.test.ts b/tests/unit/url/qs.test.ts index 80e7e044..42cdffb9 100644 --- a/tests/unit/url/qs.test.ts +++ b/tests/unit/url/qs.test.ts @@ -1,187 +1,278 @@ import { toQueryString } from "../../../src/core/url/index"; describe("Test qs toQueryString", () => { - describe("Basic functionality", () => { - it("should return empty string for null/undefined", () => { - expect(toQueryString(null)).toBe(""); - expect(toQueryString(undefined)).toBe(""); - }); + interface BasicTestCase { + description: string; + input: any; + expected: string; + } - it("should return empty string for primitive values", () => { - expect(toQueryString("hello")).toBe(""); - expect(toQueryString(42)).toBe(""); - expect(toQueryString(true)).toBe(""); - expect(toQueryString(false)).toBe(""); - }); - - it("should handle empty objects", () => { - expect(toQueryString({})).toBe(""); - }); + describe("Basic functionality", () => { + const basicTests: BasicTestCase[] = [ + { description: "should return empty string for null", input: null, expected: "" }, + { description: "should return empty string for undefined", input: undefined, expected: "" }, + { description: "should return empty string for string primitive", input: "hello", expected: "" }, + { description: "should return empty string for number primitive", input: 42, expected: "" }, + { description: "should return empty string for true boolean", input: true, expected: "" }, + { description: "should return empty string for false boolean", input: false, expected: "" }, + { description: "should handle empty objects", input: {}, expected: "" }, + { + description: "should handle simple key-value pairs", + input: { name: "John", age: 30 }, + expected: "name=John&age=30", + }, + ]; - it("should handle simple key-value pairs", () => { - const obj = { name: "John", age: 30 }; - expect(toQueryString(obj)).toBe("name=John&age=30"); + basicTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(toQueryString(input)).toBe(expected); + }); }); }); describe("Array handling", () => { - it("should handle arrays with indices format (default)", () => { - const obj = { items: ["a", "b", "c"] }; - expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=b&items%5B2%5D=c"); - }); - - it("should handle arrays with repeat format", () => { - const obj = { items: ["a", "b", "c"] }; - expect(toQueryString(obj, { arrayFormat: "repeat" })).toBe("items=a&items=b&items=c"); - }); + interface ArrayTestCase { + description: string; + input: any; + options?: { arrayFormat?: "repeat" | "indices" }; + expected: string; + } - it("should handle empty arrays", () => { - const obj = { items: [] }; - expect(toQueryString(obj)).toBe(""); - }); - - it("should handle arrays with mixed types", () => { - const obj = { mixed: ["string", 42, true, false] }; - expect(toQueryString(obj)).toBe("mixed%5B0%5D=string&mixed%5B1%5D=42&mixed%5B2%5D=true&mixed%5B3%5D=false"); - }); - - it("should handle arrays with objects", () => { - const obj = { users: [{ name: "John" }, { name: "Jane" }] }; - expect(toQueryString(obj)).toBe("users%5B0%5D%5Bname%5D=John&users%5B1%5D%5Bname%5D=Jane"); - }); + const arrayTests: ArrayTestCase[] = [ + { + description: "should handle arrays with indices format (default)", + input: { items: ["a", "b", "c"] }, + expected: "items%5B0%5D=a&items%5B1%5D=b&items%5B2%5D=c", + }, + { + description: "should handle arrays with repeat format", + input: { items: ["a", "b", "c"] }, + options: { arrayFormat: "repeat" }, + expected: "items=a&items=b&items=c", + }, + { + description: "should handle empty arrays", + input: { items: [] }, + expected: "", + }, + { + description: "should handle arrays with mixed types", + input: { mixed: ["string", 42, true, false] }, + expected: "mixed%5B0%5D=string&mixed%5B1%5D=42&mixed%5B2%5D=true&mixed%5B3%5D=false", + }, + { + description: "should handle arrays with objects", + input: { users: [{ name: "John" }, { name: "Jane" }] }, + expected: "users%5B0%5D%5Bname%5D=John&users%5B1%5D%5Bname%5D=Jane", + }, + { + description: "should handle arrays with objects in repeat format", + input: { users: [{ name: "John" }, { name: "Jane" }] }, + options: { arrayFormat: "repeat" }, + expected: "users%5Bname%5D=John&users%5Bname%5D=Jane", + }, + ]; - it("should handle arrays with objects in repeat format", () => { - const obj = { users: [{ name: "John" }, { name: "Jane" }] }; - expect(toQueryString(obj, { arrayFormat: "repeat" })).toBe("users%5Bname%5D=John&users%5Bname%5D=Jane"); + arrayTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); }); }); describe("Nested objects", () => { - it("should handle nested objects", () => { - const obj = { user: { name: "John", age: 30 } }; - expect(toQueryString(obj)).toBe("user%5Bname%5D=John&user%5Bage%5D=30"); - }); - - it("should handle deeply nested objects", () => { - const obj = { user: { profile: { name: "John", settings: { theme: "dark" } } } }; - expect(toQueryString(obj)).toBe( - "user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", - ); - }); + const nestedTests: BasicTestCase[] = [ + { + description: "should handle nested objects", + input: { user: { name: "John", age: 30 } }, + expected: "user%5Bname%5D=John&user%5Bage%5D=30", + }, + { + description: "should handle deeply nested objects", + input: { user: { profile: { name: "John", settings: { theme: "dark" } } } }, + expected: "user%5Bprofile%5D%5Bname%5D=John&user%5Bprofile%5D%5Bsettings%5D%5Btheme%5D=dark", + }, + { + description: "should handle empty nested objects", + input: { user: {} }, + expected: "", + }, + ]; - it("should handle empty nested objects", () => { - const obj = { user: {} }; - expect(toQueryString(obj)).toBe(""); + nestedTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(toQueryString(input)).toBe(expected); + }); }); }); describe("Encoding", () => { - it("should encode by default", () => { - const obj = { name: "John Doe", email: "john@example.com" }; - expect(toQueryString(obj)).toBe("name=John%20Doe&email=john%40example.com"); - }); + interface EncodingTestCase { + description: string; + input: any; + options?: { encode?: boolean }; + expected: string; + } - it("should not encode when encode is false", () => { - const obj = { name: "John Doe", email: "john@example.com" }; - expect(toQueryString(obj, { encode: false })).toBe("name=John Doe&email=john@example.com"); - }); - - it("should encode special characters in keys", () => { - const obj = { "user name": "John", "email[primary]": "john@example.com" }; - expect(toQueryString(obj)).toBe("user%20name=John&email%5Bprimary%5D=john%40example.com"); - }); + const encodingTests: EncodingTestCase[] = [ + { + description: "should encode by default", + input: { name: "John Doe", email: "john@example.com" }, + expected: "name=John%20Doe&email=john%40example.com", + }, + { + description: "should not encode when encode is false", + input: { name: "John Doe", email: "john@example.com" }, + options: { encode: false }, + expected: "name=John Doe&email=john@example.com", + }, + { + description: "should encode special characters in keys", + input: { "user name": "John", "email[primary]": "john@example.com" }, + expected: "user%20name=John&email%5Bprimary%5D=john%40example.com", + }, + { + description: "should not encode special characters in keys when encode is false", + input: { "user name": "John", "email[primary]": "john@example.com" }, + options: { encode: false }, + expected: "user name=John&email[primary]=john@example.com", + }, + ]; - it("should not encode special characters in keys when encode is false", () => { - const obj = { "user name": "John", "email[primary]": "john@example.com" }; - expect(toQueryString(obj, { encode: false })).toBe("user name=John&email[primary]=john@example.com"); + encodingTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); }); }); describe("Mixed scenarios", () => { - it("should handle complex nested structures", () => { - const obj = { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], + interface MixedTestCase { + description: string; + input: any; + options?: { arrayFormat?: "repeat" | "indices" }; + expected: string; + } + + const mixedTests: MixedTestCase[] = [ + { + description: "should handle complex nested structures", + input: { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, }, + sort: { field: "name", direction: "asc" }, }, - sort: { field: "name", direction: "asc" }, - }; - expect(toQueryString(obj)).toBe( - "filters%5Bstatus%5D%5B0%5D=active&filters%5Bstatus%5D%5B1%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D%5B0%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D%5B1%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - ); - }); - - it("should handle complex nested structures with repeat format", () => { - const obj = { - filters: { - status: ["active", "pending"], - category: { - type: "electronics", - subcategories: ["phones", "laptops"], + expected: + "filters%5Bstatus%5D%5B0%5D=active&filters%5Bstatus%5D%5B1%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D%5B0%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D%5B1%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + }, + { + description: "should handle complex nested structures with repeat format", + input: { + filters: { + status: ["active", "pending"], + category: { + type: "electronics", + subcategories: ["phones", "laptops"], + }, }, + sort: { field: "name", direction: "asc" }, }, - sort: { field: "name", direction: "asc" }, - }; - expect(toQueryString(obj, { arrayFormat: "repeat" })).toBe( - "filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", - ); - }); - - it("should handle arrays with null/undefined values", () => { - const obj = { items: ["a", null, "c", undefined, "e"] }; - expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c&items%5B4%5D=e"); - }); + options: { arrayFormat: "repeat" }, + expected: + "filters%5Bstatus%5D=active&filters%5Bstatus%5D=pending&filters%5Bcategory%5D%5Btype%5D=electronics&filters%5Bcategory%5D%5Bsubcategories%5D=phones&filters%5Bcategory%5D%5Bsubcategories%5D=laptops&sort%5Bfield%5D=name&sort%5Bdirection%5D=asc", + }, + { + description: "should handle arrays with null/undefined values", + input: { items: ["a", null, "c", undefined, "e"] }, + expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c&items%5B4%5D=e", + }, + { + description: "should handle objects with null/undefined values", + input: { name: "John", age: null, email: undefined, active: true }, + expected: "name=John&age=&active=true", + }, + ]; - it("should handle objects with null/undefined values", () => { - const obj = { name: "John", age: null, email: undefined, active: true }; - expect(toQueryString(obj)).toBe("name=John&age=&active=true"); + mixedTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); }); }); describe("Edge cases", () => { - it("should handle numeric keys", () => { - const obj = { "0": "zero", "1": "one" }; - expect(toQueryString(obj)).toBe("0=zero&1=one"); - }); - - it("should handle boolean values in objects", () => { - const obj = { enabled: true, disabled: false }; - expect(toQueryString(obj)).toBe("enabled=true&disabled=false"); - }); - - it("should handle empty strings", () => { - const obj = { name: "", description: "test" }; - expect(toQueryString(obj)).toBe("name=&description=test"); - }); + const edgeCaseTests: BasicTestCase[] = [ + { + description: "should handle numeric keys", + input: { "0": "zero", "1": "one" }, + expected: "0=zero&1=one", + }, + { + description: "should handle boolean values in objects", + input: { enabled: true, disabled: false }, + expected: "enabled=true&disabled=false", + }, + { + description: "should handle empty strings", + input: { name: "", description: "test" }, + expected: "name=&description=test", + }, + { + description: "should handle zero values", + input: { count: 0, price: 0.0 }, + expected: "count=0&price=0", + }, + { + description: "should handle arrays with empty strings", + input: { items: ["a", "", "c"] }, + expected: "items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c", + }, + ]; - it("should handle zero values", () => { - const obj = { count: 0, price: 0.0 }; - expect(toQueryString(obj)).toBe("count=0&price=0"); - }); - - it("should handle arrays with empty strings", () => { - const obj = { items: ["a", "", "c"] }; - expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=&items%5B2%5D=c"); + edgeCaseTests.forEach(({ description, input, expected }) => { + it(description, () => { + expect(toQueryString(input)).toBe(expected); + }); }); }); describe("Options combinations", () => { - it("should respect both arrayFormat and encode options", () => { - const obj = { items: ["a & b", "c & d"] }; - expect(toQueryString(obj, { arrayFormat: "repeat", encode: false })).toBe("items=a & b&items=c & d"); - }); + interface OptionsTestCase { + description: string; + input: any; + options?: { arrayFormat?: "repeat" | "indices"; encode?: boolean }; + expected: string; + } - it("should use default options when none provided", () => { - const obj = { items: ["a", "b"] }; - expect(toQueryString(obj)).toBe("items%5B0%5D=a&items%5B1%5D=b"); - }); + const optionsTests: OptionsTestCase[] = [ + { + description: "should respect both arrayFormat and encode options", + input: { items: ["a & b", "c & d"] }, + options: { arrayFormat: "repeat", encode: false }, + expected: "items=a & b&items=c & d", + }, + { + description: "should use default options when none provided", + input: { items: ["a", "b"] }, + expected: "items%5B0%5D=a&items%5B1%5D=b", + }, + { + description: "should merge provided options with defaults", + input: { items: ["a", "b"], name: "John Doe" }, + options: { encode: false }, + expected: "items[0]=a&items[1]=b&name=John Doe", + }, + ]; - it("should merge provided options with defaults", () => { - const obj = { items: ["a", "b"], name: "John Doe" }; - expect(toQueryString(obj, { encode: false })).toBe("items[0]=a&items[1]=b&name=John Doe"); + optionsTests.forEach(({ description, input, options, expected }) => { + it(description, () => { + expect(toQueryString(input, options)).toBe(expected); + }); }); }); }); diff --git a/tests/wire/empathic-voice/chatGroups.test.ts b/tests/wire/empathic-voice/chatGroups.test.ts index 00df612e..349decb1 100644 --- a/tests/wire/empathic-voice/chatGroups.test.ts +++ b/tests/wire/empathic-voice/chatGroups.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("ChatGroups", () => { +describe("ChatGroupsClient", () => { test("list-chat-groups (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -29,7 +30,7 @@ describe("ChatGroups", () => { ], }; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/chat_groups") .respondWith() .statusCode(200) @@ -68,13 +69,14 @@ describe("ChatGroups", () => { test("list-chat-groups (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/chat_groups") .respondWith() .statusCode(400) @@ -89,6 +91,7 @@ describe("ChatGroups", () => { test("get-chat-group (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -153,6 +156,7 @@ describe("ChatGroups", () => { test("get-chat-group (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -174,6 +178,7 @@ describe("ChatGroups", () => { test("get-audio (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -238,6 +243,7 @@ describe("ChatGroups", () => { test("get-audio (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -259,6 +265,7 @@ describe("ChatGroups", () => { test("list-chat-group-events (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -307,7 +314,7 @@ describe("ChatGroups", () => { ], }; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/chat_groups/697056f0-6c7e-487d-9bd8-9c19df79f05f/events") .respondWith() .statusCode(200) @@ -372,13 +379,14 @@ describe("ChatGroups", () => { test("list-chat-group-events (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/chat_groups/id/events") .respondWith() .statusCode(400) diff --git a/tests/wire/empathic-voice/chats.test.ts b/tests/wire/empathic-voice/chats.test.ts index 169cd49e..cb7828e6 100644 --- a/tests/wire/empathic-voice/chats.test.ts +++ b/tests/wire/empathic-voice/chats.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("Chats", () => { +describe("ChatsClient", () => { test("list-chats (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -30,7 +31,13 @@ describe("Chats", () => { }, ], }; - server.mockEndpoint().get("/v0/evi/chats").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/chats") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); const expected = { pageNumber: 0, @@ -68,12 +75,19 @@ describe("Chats", () => { test("list-chats (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/chats").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/chats") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.chats.listChats(); @@ -83,6 +97,7 @@ describe("Chats", () => { test("list-chat-events (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -137,7 +152,7 @@ describe("Chats", () => { config: { id: "1b60e1a0-cc59-424a-8d2c-189d354db3f3", version: 0 }, }; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/chats/470a49f6-1dec-4afe-8b61-035d3b2d63b0") .respondWith() .statusCode(200) @@ -211,12 +226,19 @@ describe("Chats", () => { test("list-chat-events (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/chats/id").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/chats/id") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.chats.listChatEvents("id"); @@ -226,6 +248,7 @@ describe("Chats", () => { test("get-audio (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -264,6 +287,7 @@ describe("Chats", () => { test("get-audio (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/empathic-voice/configs.test.ts b/tests/wire/empathic-voice/configs.test.ts index f45d8bb4..a7b2b239 100644 --- a/tests/wire/empathic-voice/configs.test.ts +++ b/tests/wire/empathic-voice/configs.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("Configs", () => { +describe("ConfigsClient", () => { test("list-configs (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -56,7 +57,13 @@ describe("Configs", () => { }, ], }; - server.mockEndpoint().get("/v0/evi/configs").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/configs") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); const expected = { pageNumber: 0, @@ -137,12 +144,19 @@ describe("Configs", () => { test("list-configs (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/configs").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/configs") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.configs.listConfigs(); @@ -152,6 +166,7 @@ describe("Configs", () => { test("create-config (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -303,6 +318,7 @@ describe("Configs", () => { test("create-config (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -328,6 +344,7 @@ describe("Configs", () => { test("list-config-versions (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -377,7 +394,7 @@ describe("Configs", () => { ], }; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/configs/1b60e1a0-cc59-424a-8d2c-189d354db3f3") .respondWith() .statusCode(200) @@ -460,12 +477,19 @@ describe("Configs", () => { test("list-config-versions (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/configs/id").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/configs/id") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.configs.listConfigVersions("id"); @@ -475,6 +499,7 @@ describe("Configs", () => { test("create-config-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -633,6 +658,7 @@ describe("Configs", () => { test("create-config-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -657,6 +683,7 @@ describe("Configs", () => { test("delete-config (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -675,6 +702,7 @@ describe("Configs", () => { test("delete-config (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -696,6 +724,7 @@ describe("Configs", () => { test("get-config-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -805,6 +834,7 @@ describe("Configs", () => { test("get-config-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -826,6 +856,7 @@ describe("Configs", () => { test("delete-config-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -847,6 +878,7 @@ describe("Configs", () => { test("delete-config-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -868,6 +900,7 @@ describe("Configs", () => { test("update-config-description (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -984,6 +1017,7 @@ describe("Configs", () => { test("update-config-description (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/empathic-voice/controlPlane.test.ts b/tests/wire/empathic-voice/controlPlane.test.ts index 4ff6a562..e27648d7 100644 --- a/tests/wire/empathic-voice/controlPlane.test.ts +++ b/tests/wire/empathic-voice/controlPlane.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("ControlPlane", () => { +describe("ControlPlaneClient", () => { test("send (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -30,6 +31,7 @@ describe("ControlPlane", () => { test("send (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/empathic-voice/prompts.test.ts b/tests/wire/empathic-voice/prompts.test.ts index 635edbc0..db00e50b 100644 --- a/tests/wire/empathic-voice/prompts.test.ts +++ b/tests/wire/empathic-voice/prompts.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("Prompts", () => { +describe("PromptsClient", () => { test("list-prompts (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -39,7 +40,13 @@ describe("Prompts", () => { }, ], }; - server.mockEndpoint().get("/v0/evi/prompts").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/prompts") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); const expected = { pageNumber: 0, @@ -82,12 +89,19 @@ describe("Prompts", () => { test("list-prompts (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/prompts").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/prompts") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.prompts.listPrompts(); @@ -97,6 +111,7 @@ describe("Prompts", () => { test("create-prompt (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -140,6 +155,7 @@ describe("Prompts", () => { test("create-prompt (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -165,6 +181,7 @@ describe("Prompts", () => { test("list-prompt-versions (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -217,6 +234,7 @@ describe("Prompts", () => { test("list-prompt-versions (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -232,6 +250,7 @@ describe("Prompts", () => { test("create-prompt-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -280,6 +299,7 @@ describe("Prompts", () => { test("create-prompt-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -304,6 +324,7 @@ describe("Prompts", () => { test("delete-prompt (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -322,6 +343,7 @@ describe("Prompts", () => { test("delete-prompt (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -343,6 +365,7 @@ describe("Prompts", () => { test("get-prompt-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -381,6 +404,7 @@ describe("Prompts", () => { test("get-prompt-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -402,6 +426,7 @@ describe("Prompts", () => { test("delete-prompt-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -423,6 +448,7 @@ describe("Prompts", () => { test("delete-prompt-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -444,6 +470,7 @@ describe("Prompts", () => { test("update-prompt-description (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -489,6 +516,7 @@ describe("Prompts", () => { test("update-prompt-description (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/empathic-voice/tools.test.ts b/tests/wire/empathic-voice/tools.test.ts index 9ea82366..691b214c 100644 --- a/tests/wire/empathic-voice/tools.test.ts +++ b/tests/wire/empathic-voice/tools.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("Tools", () => { +describe("ToolsClient", () => { test("list-tools (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -48,7 +49,13 @@ describe("Tools", () => { }, ], }; - server.mockEndpoint().get("/v0/evi/tools").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/tools") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); const expected = { pageNumber: 0, @@ -100,12 +107,19 @@ describe("Tools", () => { test("list-tools (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/tools").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/tools") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.tools.listTools(); @@ -115,6 +129,7 @@ describe("Tools", () => { test("create-tool (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -176,6 +191,7 @@ describe("Tools", () => { test("create-tool (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -201,6 +217,7 @@ describe("Tools", () => { test("list-tool-versions (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -228,7 +245,7 @@ describe("Tools", () => { ], }; server - .mockEndpoint() + .mockEndpoint({ once: false }) .get("/v0/evi/tools/00183a3f-79ba-413d-9f3b-609864268bea") .respondWith() .statusCode(200) @@ -268,12 +285,19 @@ describe("Tools", () => { test("list-tool-versions (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/evi/tools/id").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/evi/tools/id") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.empathicVoice.tools.listToolVersions("id"); @@ -283,6 +307,7 @@ describe("Tools", () => { test("create-tool-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -346,6 +371,7 @@ describe("Tools", () => { test("create-tool-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -370,6 +396,7 @@ describe("Tools", () => { test("delete-tool (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -388,6 +415,7 @@ describe("Tools", () => { test("delete-tool (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -409,6 +437,7 @@ describe("Tools", () => { test("get-tool-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -457,6 +486,7 @@ describe("Tools", () => { test("get-tool-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -478,6 +508,7 @@ describe("Tools", () => { test("delete-tool-version (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -496,6 +527,7 @@ describe("Tools", () => { test("delete-tool-version (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -517,6 +549,7 @@ describe("Tools", () => { test("update-tool-description (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -576,6 +609,7 @@ describe("Tools", () => { test("update-tool-description (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/expression-measurement/batch/main.test.ts b/tests/wire/expression-measurement/batch/main.test.ts index b734b858..cf11a479 100644 --- a/tests/wire/expression-measurement/batch/main.test.ts +++ b/tests/wire/expression-measurement/batch/main.test.ts @@ -3,10 +3,11 @@ import { HumeClient } from "../../../../src/Client"; import { mockServerPool } from "../../../mock-server/MockServerPool"; -describe("Batch", () => { +describe("BatchClient", () => { test("list-jobs", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -101,6 +102,7 @@ describe("Batch", () => { test("start-inference-job", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -127,6 +129,7 @@ describe("Batch", () => { test("get-job-details", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -217,6 +220,7 @@ describe("Batch", () => { test("get-job-predictions", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/tts/main.test.ts b/tests/wire/tts/main.test.ts index b8963bc9..1dc2c925 100644 --- a/tests/wire/tts/main.test.ts +++ b/tests/wire/tts/main.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("Tts", () => { +describe("TtsClient", () => { test("synthesize-json (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -118,6 +119,7 @@ describe("Tts", () => { test("synthesize-json (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/tests/wire/tts/voices.test.ts b/tests/wire/tts/voices.test.ts index 8cb4e837..fcfe3487 100644 --- a/tests/wire/tts/voices.test.ts +++ b/tests/wire/tts/voices.test.ts @@ -4,10 +4,11 @@ import * as Hume from "../../../src/api/index"; import { HumeClient } from "../../../src/Client"; import { mockServerPool } from "../../mock-server/MockServerPool"; -describe("Voices", () => { +describe("VoicesClient", () => { test("list (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -21,7 +22,13 @@ describe("Voices", () => { { id: "d87352b0-26a3-4b11-081b-d157a5674d19", name: "Goliath Hume", provider: "CUSTOM_VOICE" }, ], }; - server.mockEndpoint().get("/v0/tts/voices").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/tts/voices") + .respondWith() + .statusCode(200) + .jsonBody(rawResponseBody) + .build(); const expected = { pageNumber: 0, @@ -53,12 +60,19 @@ describe("Voices", () => { test("list (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); const rawResponseBody = {}; - server.mockEndpoint().get("/v0/tts/voices").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + server + .mockEndpoint({ once: false }) + .get("/v0/tts/voices") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); await expect(async () => { return await client.tts.voices.list({ @@ -70,6 +84,7 @@ describe("Voices", () => { test("create (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -102,6 +117,7 @@ describe("Voices", () => { test("create (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -127,6 +143,7 @@ describe("Voices", () => { test("delete (1)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); @@ -142,6 +159,7 @@ describe("Voices", () => { test("delete (2)", async () => { const server = mockServerPool.createServer(); const client = new HumeClient({ + maxRetries: 0, apiKey: "test", environment: { base: server.baseUrl, evi: server.baseUrl, tts: server.baseUrl, stream: server.baseUrl }, }); diff --git a/vitest.config.ts b/vitest.config.mts similarity index 85% rename from vitest.config.ts rename to vitest.config.mts index 677c5855..ba2ec4f9 100644 --- a/vitest.config.ts +++ b/vitest.config.mts @@ -10,6 +10,7 @@ export default defineConfig({ root: "./tests", include: ["**/*.test.{js,ts,jsx,tsx}"], exclude: ["wire/**"], + setupFiles: ["./setup.ts"], }, }, { @@ -18,7 +19,7 @@ export default defineConfig({ name: "wire", environment: "node", root: "./tests/wire", - setupFiles: ["../mock-server/setup.ts"], + setupFiles: ["../setup.ts", "../mock-server/setup.ts"], }, }, ],