From 3235a086334b954a787a6fb6e60646cf96116403 Mon Sep 17 00:00:00 2001 From: mitchell Date: Sun, 7 Jun 2026 22:13:19 -0400 Subject: [PATCH] refactor(tts): replace Bun-specific APIs with execa and Node.js stdlib - Replace `$` and `Bun.spawn` with `execa` for process execution - Swap `Bun.JSONL.parse` for native `JSON.parse` and use `node:readline` for stdin handling --- package.json | 3 +- pnpm-lock.yaml | 268 +++++++++++++++++++++++++++++++++++++++++-------- tts.ts | 89 +++++++++------- 3 files changed, 277 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index 45f8a6d..51abcf1 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "ai": "^6.0.161", "ansi-styles": "^6.2.3", "chalk": "^5.6.2", - "happy-dom-without-node": "^14.12.3", + "execa": "^9.6.1", + "happy-dom": "^20.10.2", "marked": "^18.0.4", "rolldown": "^1.0.3", "shiki": "^4.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fec87b7..5644768 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 1.4.0 '@cliffy/table': specifier: npm:@jsr/cliffy__table - version: '@jsr/cliffy__table@1.1.1' + version: '@jsr/cliffy__table@1.2.1' '@mozilla/readability': specifier: ^0.6.0 version: 0.6.0 @@ -35,9 +35,12 @@ importers: chalk: specifier: ^5.6.2 version: 5.6.2 - happy-dom-without-node: - specifier: ^14.12.3 - version: 14.12.3 + execa: + specifier: ^9.6.1 + version: 9.6.1 + happy-dom: + specifier: ^20.10.2 + version: 20.10.2 marked: specifier: ^18.0.4 version: 18.0.4 @@ -158,8 +161,8 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@jsr/cliffy__table@1.1.1': - resolution: {integrity: sha512-LcmLDrR/5uUzNklMNCqIK2L8RYdyyX27/QydFxxzi+HeB5sMyjOWOI636HPbYBGcr+gB7xlNGRi3EYgBXZnfWg==, tarball: https://npm.jsr.io/~/11/@jsr/cliffy__table/1.1.1.tgz} + '@jsr/cliffy__table@1.2.1': + resolution: {integrity: sha512-C/edvcMwtnbFx0zyoeGRcC+ZRvGYqxEhRi19HpfbfiYS4ZErKjEwMJmavO/DN7Wxbk34UpJP+UQKL7nKe+dYkA==, tarball: https://npm.jsr.io/~/11/@jsr/cliffy__table/1.2.1.tgz} '@jsr/std__fmt@1.0.10': resolution: {integrity: sha512-Q2N8XDNkh/c1IKTi+sO4Vm64M5m4W0o47qTURmMX0vgHm9AJ6jL7jEQ2kw0M1T2rBZiwAY78eUbYzFyOYWaCqQ==, tarball: https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.10.tgz} @@ -279,6 +282,9 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@shikijs/core@4.1.0': resolution: {integrity: sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==} engines: {node: '>=20'} @@ -310,6 +316,10 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -331,6 +341,12 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@ungap/structured-clone@1.3.1': resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} @@ -356,6 +372,10 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + bun-types@1.3.14: resolution: {integrity: sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==} @@ -375,6 +395,10 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -382,8 +406,8 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} environment@1.1.0: @@ -394,6 +418,10 @@ packages: resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} engines: {node: '>=18.0.0'} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -403,13 +431,21 @@ packages: fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + get-east-asian-width@1.6.0: resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} - happy-dom-without-node@14.12.3: - resolution: {integrity: sha512-1wp8+GFneT8mBjVnzancXHRuscEUH3vnb38lfCHPxuSu6OiRw1kQzHFbTGYlNCU2YXOlVIlzsS6xg9nAr7Xg6Q==} - engines: {node: '>=16.0.0'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + happy-dom@20.10.2: + resolution: {integrity: sha512-5p9Sxis3eowDJKqx90QCsgbNA02XXqJ59NOHvD4V6cxp+rP4d/xOyVx7uY3hS8hiUbY1VeiFH8lbJ81AyuDVLQ==} + engines: {node: '>=20.0.0'} has-flag@5.0.1: resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} @@ -424,6 +460,25 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -450,19 +505,35 @@ packages: micromark-util-types@2.0.2: resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + oniguruma-parser@0.12.2: resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -477,10 +548,22 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + shiki@4.1.0: resolution: {integrity: sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==} engines: {node: '>=20'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -498,6 +581,10 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -510,10 +597,6 @@ packages: resolution: {integrity: sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==} engines: {node: '>=20'} - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -528,6 +611,10 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -549,22 +636,35 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true wrap-ansi@10.0.0: resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} engines: {node: '>=20'} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -660,7 +760,7 @@ snapshots: tslib: 2.8.1 optional: true - '@jsr/cliffy__table@1.1.1': + '@jsr/cliffy__table@1.2.1': dependencies: '@jsr/std__fmt': 1.0.10 @@ -730,6 +830,8 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@sec-ant/readable-stream@0.4.1': {} + '@shikijs/core@4.1.0': dependencies: '@shikijs/primitive': 4.1.0 @@ -770,6 +872,8 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.1.0': {} '@tybys/wasm-util@0.10.2': @@ -795,6 +899,12 @@ snapshots: '@types/unist@3.0.3': {} + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.9.1 + '@ungap/structured-clone@1.3.1': {} '@vercel/oidc@3.2.0': {} @@ -815,6 +925,10 @@ snapshots: ansi-styles@6.2.3: {} + buffer-image-size@0.6.4: + dependencies: + '@types/node': 25.9.1 + bun-types@1.3.14: dependencies: '@types/node': 25.9.1 @@ -829,18 +943,39 @@ snapshots: comma-separated-tokens@2.0.3: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dequal@2.0.3: {} devlop@1.1.0: dependencies: dequal: 2.0.3 - entities@4.5.0: {} + entities@7.0.1: {} environment@1.1.0: {} eventsource-parser@3.0.8: {} + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: @@ -851,14 +986,29 @@ snapshots: dependencies: fast-string-width: 3.0.2 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + get-east-asian-width@1.6.0: {} - happy-dom-without-node@14.12.3: + get-stream@9.0.1: dependencies: - entities: 4.5.0 - webidl-conversions: 7.0.0 + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + happy-dom@20.10.2: + dependencies: + '@types/node': 25.9.1 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.1 + buffer-image-size: 0.6.4 + entities: 7.0.1 whatwg-mimetype: 3.0.0 - whatwg-url: 14.2.0 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate has-flag@5.0.1: {} @@ -882,6 +1032,16 @@ snapshots: html-void-elements@3.0.0: {} + human-signals@8.0.1: {} + + is-plain-obj@4.1.0: {} + + is-stream@4.0.1: {} + + is-unicode-supported@2.1.0: {} + + isexe@2.0.0: {} + json-schema@0.4.0: {} marked@18.0.4: {} @@ -915,6 +1075,11 @@ snapshots: micromark-util-types@2.0.2: {} + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + oniguruma-parser@0.12.2: {} oniguruma-to-es@4.3.6: @@ -923,9 +1088,17 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 - property-information@7.1.0: {} + parse-ms@4.0.0: {} - punycode@2.3.1: {} + path-key@3.1.1: {} + + path-key@4.0.0: {} + + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + property-information@7.1.0: {} regex-recursion@6.0.2: dependencies: @@ -958,6 +1131,12 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.3 '@rolldown/binding-win32-x64-msvc': 1.0.3 + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + shiki@4.1.0: dependencies: '@shikijs/core': 4.1.0 @@ -969,6 +1148,8 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + signal-exit@4.1.0: {} + sisteransi@1.0.5: {} space-separated-tokens@2.0.2: {} @@ -987,6 +1168,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-final-newline@4.0.0: {} + supports-color@10.2.2: {} supports-hyperlinks@4.4.0: @@ -999,10 +1182,6 @@ snapshots: ansi-escapes: 7.3.0 supports-hyperlinks: 4.4.0 - tr46@5.1.1: - dependencies: - punycode: 2.3.1 - trim-lines@3.0.1: {} tslib@2.8.1: @@ -1012,6 +1191,8 @@ snapshots: undici-types@7.24.6: {} + unicorn-magic@0.3.0: {} + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -1045,14 +1226,11 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - webidl-conversions@7.0.0: {} - whatwg-mimetype@3.0.0: {} - whatwg-url@14.2.0: + which@2.0.2: dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 + isexe: 2.0.0 wrap-ansi@10.0.0: dependencies: @@ -1060,6 +1238,10 @@ snapshots: string-width: 8.2.1 strip-ansi: 7.2.0 + ws@8.21.0: {} + + yoctocolors@2.1.2: {} + zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/tts.ts b/tts.ts index fc131a5..af47e3e 100644 --- a/tts.ts +++ b/tts.ts @@ -1,15 +1,17 @@ import os from "node:os"; import process from "node:process"; +import { createInterface } from "node:readline"; import { parseArgs } from "node:util"; import { Readability } from "@mozilla/readability"; -import { $, which } from "bun"; import chalk from "chalk"; -import { Window } from "happy-dom-without-node"; +import { execa } from "execa"; +import { Window } from "happy-dom"; +import wrapAnsi from "wrap-ansi"; const start = Date.now(); const { values: args } = parseArgs({ - args: Bun.argv, + args: process.argv, options: { wait: { type: "boolean" }, w: { type: "boolean" }, @@ -22,7 +24,15 @@ const { values: args } = parseArgs({ const wait = args.wait ?? args.w; const url = args.url ?? args.u; -let input = url ? url : await Bun.stdin.text(); + +let input = url + ? url + : await new Promise((resolve) => { + let data = ""; + process.stdin.setEncoding("utf8"); + process.stdin.on("data", (chunk) => (data += chunk)); + process.stdin.on("end", () => resolve(data)); + }); if (url !== undefined) { const result = await fetch(input.trim()); @@ -45,9 +55,8 @@ if (!input) { const home = os.homedir(); const cwd = `${home}/.ttsc/`; -$.cwd(cwd); -await $`rm *.wav`.nothrow(); +await execa({ cwd, shell: true, reject: false })`rm *.wav`; console.log(chalk.blue("Welcome to TTSC!")); console.log( @@ -56,16 +65,16 @@ console.log( ), ); -const koko = Bun.spawn({ - cmd: ["uv", "run", "--", "python", "-u", "main.py"], +const koko = execa({ cwd, - stdin: Buffer.from(input), + input: Buffer.from(input), // execa v6+ uses 'input'. Use 'stdin' for v5 stdout: "pipe", stderr: "inherit", -}); +})`uv run -- python -u main.py`; if (!wait) { - koko.exited.then(() => { + koko.catch(() => {}); + koko.then(() => { const stop = Date.now(); const duration = ((stop - start) / 1000).toFixed(2); console.log(chalk.green(`\nProcessing finished: ${duration}s`)); @@ -73,15 +82,17 @@ if (!wait) { } let first = true; -const decoder = new TextDecoder(); -for await (const line of koko.stdout) { - const decoded = decoder.decode(line).trim(); +const rl = createInterface({ input: koko.stdout, crlfDelay: Infinity }); - let infos: { file: number; text: string }[]; +for await (const line of rl) { + const trimmed = line.trim(); + if (!trimmed) continue; + + let info: { file: number; text: string }; try { - infos = Bun.JSONL.parse(decoded) as { file: number; text: string }[]; + info = JSON.parse(trimmed); } catch { - console.log(decoded); + console.log(trimmed); continue; } @@ -92,25 +103,22 @@ for await (const line of koko.stdout) { console.log(chalk.green(`\nTime-to-audio: ${duration}s`)); } - for (const info of infos) { - const [columns] = process.stdout.getWindowSize(); - const text = Bun.wrapAnsi(info.text, columns); - const file = `${home}/.ttsc/${info.file}.wav`; - console.log(`\n${chalk.blue(file)}:\n${text}\n`); + const [columns] = process.stdout.getWindowSize(); + const text = wrapAnsi(info.text, columns); + const file = `${home}/.ttsc/${info.file}.wav`; + console.log(`\n${chalk.blue(file)}:\n${text}\n`); - if (!wait) { - await Bun.spawn({ - cmd: ["mpv", file], - cwd, - stdin: "ignore", - stdout: "inherit", - stderr: "inherit", - }).exited; - } + if (!wait) { + await execa({ + cwd, + stdin: "ignore", + stdout: "inherit", + stderr: "inherit", + })`mpv ${file}`; } } -await koko.exited; +await koko; if (!wait) process.exit(); const stop = Date.now(); @@ -118,19 +126,22 @@ const duration = ((stop - start) / 1000).toFixed(2); console.log(chalk.green(`\nProcessing finished: ${duration}s`)); console.log(chalk.yellow(`Combining audio with sox...`)); -if (which("sox")) { - await $`sox *.wav output.wav`; +try { + await execa({ cwd, shell: true })`sox *.wav output.wav`; console.log(chalk.green(`Playing audio from ${cwd}output.wav`)); -} else { - console.error("Sox not found. Unable to combine outputs."); +} catch (err) { + if (err instanceof Object && "code" in err && err.code === "ENOENT") { + console.error("Sox not found. Unable to combine outputs."); + } else { + throw err; + } console.log(chalk.yellow(`Audio available in ${cwd}`)); process.exit(); } -await Bun.spawn({ - cmd: ["mpv", "--pause", "output.wav"], +await execa({ cwd, stdin: "ignore", stdout: "inherit", stderr: "inherit", -}).exited; +})`mpv --pause output.wav`;