diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..81f7211e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,5 @@ +{ + "name": "opencode-dev", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "postCreateCommand": "curl -fsSL https://bun.sh/install | bash && echo 'export PATH=\"$HOME/.bun/bin:$PATH\"' >> ~/.bashrc" +} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6410217e..0896d972 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,6 +64,11 @@ jobs: # contention issue typecheck.yml already caps for tsgo. run: GITHUB_ACTIONS=false bun turbo test --concurrency=4 + - name: Run upgrade-opentui tests + timeout-minutes: 5 + working-directory: script + run: bun test upgrade-opentui.test.ts + - name: Check generated client timeout-minutes: 5 working-directory: packages/client diff --git a/bun.lock b/bun.lock index 1e3a6425..47da2b1f 100644 --- a/bun.lock +++ b/bun.lock @@ -1442,8 +1442,6 @@ "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="], - "@bufbuild/protobuf": ["@bufbuild/protobuf@2.12.0", "", {}, "sha512-B/XlCaFIP8LOwzo+bz5uFzATYokcwCKQcghqnlfwSmM5eX/qTkvDBnDPs+gXtX/RyjxJ4DRikECcPJbyALA8FA=="], "@bufbuild/protoplugin": ["@bufbuild/protoplugin@2.12.0", "", { "dependencies": { "@bufbuild/protobuf": "2.12.0", "@typescript/vfs": "^1.6.2", "typescript": "5.4.5" } }, "sha512-ORlDITp8AFUXzIhLRoMCG+ud+D3MPKWb5HQXBoskMMnjeyEjE1H1qLonVNPyOr8lkx3xSfYUo8a0dvOZJVAzow=="], @@ -3002,8 +3000,6 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], - "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.8", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.8", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.8", "vitest": "4.1.8" }, "optionalPeers": ["@vitest/browser"] }, "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw=="], - "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], "@vitest/mocker": ["@vitest/mocker@4.1.7", "", { "dependencies": { "@vitest/spy": "4.1.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA=="], @@ -3124,8 +3120,6 @@ "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], - "ast-v8-to-istanbul": ["ast-v8-to-istanbul@1.0.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "estree-walker": "^3.0.3", "js-tokens": "^10.0.0" } }, "sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg=="], - "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], @@ -3610,8 +3604,6 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], - "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "engine.io-client": ["engine.io-client@6.6.5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.20.1", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg=="], @@ -4132,12 +4124,6 @@ "isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="], - "istanbul-lib-coverage": ["istanbul-lib-coverage@3.2.2", "", {}, "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg=="], - - "istanbul-lib-report": ["istanbul-lib-report@3.0.1", "", { "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" } }, "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw=="], - - "istanbul-reports": ["istanbul-reports@3.2.0", "", { "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA=="], - "iterate-iterator": ["iterate-iterator@1.0.2", "", {}, "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw=="], "iterate-value": ["iterate-value@1.0.2", "", { "dependencies": { "es-get-iterator": "^1.0.2", "iterate-iterator": "^1.0.1" } }, "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ=="], @@ -4306,8 +4292,6 @@ "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], - "make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="], - "make-fetch-happen": ["make-fetch-happen@15.0.6", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw=="], "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], @@ -6122,10 +6106,6 @@ "@solidjs/start/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], - "@standard-community/standard-json/effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="], - - "@standard-community/standard-openapi/effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="], - "@storybook/csf-plugin/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], "@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], @@ -6154,10 +6134,6 @@ "@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], - "@vitest/coverage-v8/@vitest/utils": ["@vitest/utils@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg=="], - - "@vitest/coverage-v8/magicast": ["magicast@0.5.3", "", { "dependencies": { "@babel/parser": "^7.29.3", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw=="], - "@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], "@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], @@ -6208,8 +6184,6 @@ "archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - "ast-v8-to-istanbul/js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="], - "astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], "astro/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], @@ -6300,8 +6274,6 @@ "electron-winstaller/fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], - "encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - "engine.io-client/ws": ["ws@8.20.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w=="], "esast-util-from-js/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], @@ -6352,8 +6324,6 @@ "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], - "istanbul-reports/html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], - "js-beautify/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], "js-beautify/nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="], @@ -6960,18 +6930,12 @@ "@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="], - "@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@storybook/csf-plugin/unplugin/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "@storybook/csf-plugin/unplugin/webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], - "@vitest/coverage-v8/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@4.1.8", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA=="], - "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], diff --git a/script/.gitignore b/script/.gitignore new file mode 100644 index 00000000..404abb22 --- /dev/null +++ b/script/.gitignore @@ -0,0 +1 @@ +coverage/ diff --git a/script/bunfig.toml b/script/bunfig.toml new file mode 100644 index 00000000..98c2e52e --- /dev/null +++ b/script/bunfig.toml @@ -0,0 +1,2 @@ +[test] +root = "." diff --git a/script/upgrade-opentui.test.ts b/script/upgrade-opentui.test.ts new file mode 100644 index 00000000..9846a88d --- /dev/null +++ b/script/upgrade-opentui.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, test } from "bun:test" +import { setVersion } from "./upgrade-opentui" + +describe("setVersion", () => { + test("passes through catalog: references unchanged", () => { + expect(setVersion("catalog:", "dep", "1.2.3", false)).toBe("catalog:") + expect(setVersion("catalog:", "peer", "1.2.3", false)).toBe("catalog:") + }) + + test("passes through workspace: references unchanged", () => { + expect(setVersion("workspace:*", "dep", "1.2.3", false)).toBe("workspace:*") + expect(setVersion("workspace:^", "peer", "1.2.3", false)).toBe("workspace:^") + }) + + test("returns the bare version when snapshot is set, regardless of prefix", () => { + expect(setVersion("^1.0.0", "dep", "1.2.3", true)).toBe("1.2.3") + expect(setVersion("~1.0.0", "peer", "1.2.3", true)).toBe("1.2.3") + expect(setVersion(">=1.0.0", "dep", "1.2.3", true)).toBe("1.2.3") + }) + + test("always prefixes peer dependencies with >=", () => { + expect(setVersion("1.0.0", "peer", "1.2.3", false)).toBe(">=1.2.3") + expect(setVersion("^1.0.0", "peer", "1.2.3", false)).toBe(">=1.2.3") + expect(setVersion("~1.0.0", "peer", "1.2.3", false)).toBe(">=1.2.3") + }) + + test("preserves a >= prefix for dep versions", () => { + expect(setVersion(">=1.0.0", "dep", "1.2.3", false)).toBe(">=1.2.3") + }) + + test("preserves a ^ prefix for dep versions", () => { + expect(setVersion("^1.0.0", "dep", "1.2.3", false)).toBe("^1.2.3") + }) + + test("preserves a ~ prefix for dep versions", () => { + expect(setVersion("~1.0.0", "dep", "1.2.3", false)).toBe("~1.2.3") + }) + + test("uses the bare version when the dep has no recognized prefix", () => { + expect(setVersion("1.0.0", "dep", "1.2.3", false)).toBe("1.2.3") + }) +}) \ No newline at end of file diff --git a/script/upgrade-opentui.ts b/script/upgrade-opentui.ts index fdc34491..0f432741 100644 --- a/script/upgrade-opentui.ts +++ b/script/upgrade-opentui.ts @@ -2,191 +2,198 @@ import path from "node:path" -const args = process.argv.slice(2) -const usage = "Usage: bun run script/upgrade-opentui.ts [--snapshot] " - -if (args.includes("--help") || args.includes("-h")) { - console.log(usage) - process.exit(0) -} - -const snapshotArg = args.find((arg) => arg.startsWith("--snapshot=")) -const snapshot = args.includes("--snapshot") || snapshotArg !== undefined -const unknown = args.find((arg) => arg.startsWith("-") && arg !== "--snapshot" && !arg.startsWith("--snapshot=")) -if (unknown) { - console.error(`Unknown option: ${unknown}`) - console.error(usage) - process.exit(1) -} +export const setVersion = (cur: string, kind: "dep" | "peer", ver: string, snapshot: boolean) => { + if (cur === "catalog:" || cur.startsWith("workspace:")) return cur + if (snapshot) return ver -const positional = args.filter((arg) => arg !== "--snapshot" && !arg.startsWith("--snapshot=")) -const raw = snapshotArg?.slice("--snapshot=".length) || positional[0] -if (!raw || positional.length > (snapshotArg ? 0 : 1)) { - console.error(usage) - process.exit(1) -} + const rules: [boolean, string][] = [ + [kind === "peer", ">="], + [cur.startsWith(">="), ">="], + [cur.startsWith("^"), "^"], + [cur.startsWith("~"), "~"], + ] -if (snapshotArg === "--snapshot=") { - console.error("Missing snapshot version") - console.error(usage) - process.exit(1) + const prefix = rules.find(([matches]) => matches)?.[1] ?? "" + return `${prefix}${ver}` } -const ver = raw.replace(/^v/, "") -const root = path.resolve(import.meta.dir, "..") -const lockfile = path.join(root, "bun.lock") -const skip = new Set([".git", ".opencode", ".turbo", "dist", "node_modules"]) -const keys = ["@opentui/core", "@opentui/keymap", "@opentui/solid"] as const +if (import.meta.main) { + const args = process.argv.slice(2) + const usage = "Usage: bun run script/upgrade-opentui.ts [--snapshot] " -const files = (await Array.fromAsync(new Bun.Glob("**/package.json").scan({ cwd: root }))).filter( - (file) => !file.split("/").some((part) => skip.has(part)), -) + if (args.includes("--help") || args.includes("-h")) { + console.log(usage) + process.exit(0) + } -const setVersion = (cur: string, kind: "dep" | "peer") => { - if (cur === "catalog:" || cur.startsWith("workspace:")) return cur - if (snapshot) return ver - if (kind === "peer") return `>=${ver}` - if (cur.startsWith(">=")) return `>=${ver}` - if (cur.startsWith("^")) return `^${ver}` - if (cur.startsWith("~")) return `~${ver}` - return ver -} + const snapshotArg = args.find((arg) => arg.startsWith("--snapshot=")) + const snapshot = args.includes("--snapshot") || snapshotArg !== undefined + const unknown = args.find((arg) => arg.startsWith("-") && arg !== "--snapshot" && !arg.startsWith("--snapshot=")) + if (unknown) { + console.error(`Unknown option: ${unknown}`) + console.error(usage) + process.exit(1) + } -const editDeps = (obj: unknown, kind: "dep" | "peer") => { - if (!obj || typeof obj !== "object") return false - const map = obj as Record - return keys - .map((key) => { - const cur = map[key] - if (typeof cur !== "string") return false - const next = setVersion(cur, kind) - if (next === cur) return false - map[key] = next - return true - }) - .some(Boolean) -} + const positional = args.filter((arg) => arg !== "--snapshot" && !arg.startsWith("--snapshot=")) + const raw = snapshotArg?.slice("--snapshot=".length) || positional[0] + if (!raw || positional.length > (snapshotArg ? 0 : 1)) { + console.error(usage) + process.exit(1) + } -const editCatalog = (obj: unknown) => { - if (!obj || typeof obj !== "object") return false - const map = obj as Record - return keys - .map((key) => { - const cur = map[key] - if (typeof cur !== "string" || cur === ver) return false - map[key] = ver - return true - }) - .some(Boolean) -} + if (snapshotArg === "--snapshot=") { + console.error("Missing snapshot version") + console.error(usage) + process.exit(1) + } -const editOverrides = (obj: unknown) => { - if (!obj || typeof obj !== "object") return false - const map = obj as Record - return keys - .map((key) => { - const cur = map[key] - if (typeof cur !== "string") return false - const next = snapshot ? ver : "catalog:" - if (next === cur) return false - map[key] = next - return true - }) - .some(Boolean) -} + const ver = raw.replace(/^v/, "") + const root = path.resolve(import.meta.dir, "..") + const lockfile = path.join(root, "bun.lock") + const skip = new Set([".git", ".opencode", ".turbo", "dist", "node_modules"]) + const keys = ["@opentui/core", "@opentui/keymap", "@opentui/solid"] as const -const out = ( - await Promise.all( - files.map(async (rel) => { - const file = path.join(root, rel) - const txt = await Bun.file(file).text() - const json = JSON.parse(txt) - const hit = [ - editCatalog(json.workspaces?.catalog), - editOverrides(json.overrides), - editDeps(json.dependencies, "dep"), - editDeps(json.devDependencies, "dep"), - editDeps(json.peerDependencies, "peer"), - ].some(Boolean) - if (!hit) return null - await Bun.write(file, `${JSON.stringify(json, null, 2)}\n`) - return rel - }), + const files = (await Array.fromAsync(new Bun.Glob("**/package.json").scan({ cwd: root }))).filter( + (file) => !file.split("/").some((part) => skip.has(part)), ) -).filter((item): item is string => item !== null) -if (out.length === 0) { - console.log(`No opentui manifest updates needed for ${ver}`) -} + const editDeps = (obj: unknown, kind: "dep" | "peer") => { + if (!obj || typeof obj !== "object") return false + const map = obj as Record + return keys + .map((key) => { + const cur = map[key] + if (typeof cur !== "string") return false + const next = setVersion(cur, kind, ver, snapshot) + if (next === cur) return false + map[key] = next + return true + }) + .some(Boolean) + } -if (out.length > 0) { - console.log(`Updated opentui${snapshot ? " snapshot" : ""} to ${ver} in:`) - for (const file of out) { - console.log(`- ${file}`) + const editCatalog = (obj: unknown) => { + if (!obj || typeof obj !== "object") return false + const map = obj as Record + return keys + .map((key) => { + const cur = map[key] + if (typeof cur !== "string" || cur === ver) return false + map[key] = ver + return true + }) + .some(Boolean) } -} -console.log("Running bun install to update bun.lock...") -const install = Bun.spawn([process.execPath, "install"], { - cwd: root, - stdout: "inherit", - stderr: "inherit", -}) -const installCode = await install.exited -if (installCode !== 0) process.exit(installCode) - -const fixed = await fixKnownLockfileIssues() -if (fixed.length > 0) { - console.log("Removed stale opentui-spinner peer lockfile entries:") - for (const item of fixed) { - console.log(`- ${item}`) + const editOverrides = (obj: unknown) => { + if (!obj || typeof obj !== "object") return false + const map = obj as Record + return keys + .map((key) => { + const cur = map[key] + if (typeof cur !== "string") return false + const next = snapshot ? ver : "catalog:" + if (next === cur) return false + map[key] = next + return true + }) + .some(Boolean) } -} -const stale = await findStaleLockfileEntries() -if (stale.length > 0) { - console.error(`bun.lock still contains stale opentui versions after upgrading to ${ver}:`) - for (const item of stale) { - console.error(`- ${item.entry}: ${item.pkg}@${item.version}`) + const out = ( + await Promise.all( + files.map(async (rel) => { + const file = path.join(root, rel) + const txt = await Bun.file(file).text() + const json = JSON.parse(txt) + const hit = [ + editCatalog(json.workspaces?.catalog), + editOverrides(json.overrides), + editDeps(json.dependencies, "dep"), + editDeps(json.devDependencies, "dep"), + editDeps(json.peerDependencies, "peer"), + ].some(Boolean) + if (!hit) return null + await Bun.write(file, `${JSON.stringify(json, null, 2)}\n`) + return rel + }), + ) + ).filter((item): item is string => item !== null) + + if (out.length === 0) { + console.log(`No opentui manifest updates needed for ${ver}`) } - process.exit(1) -} -console.log("bun.lock opentui versions are consistent") + if (out.length > 0) { + console.log(`Updated opentui${snapshot ? " snapshot" : ""} to ${ver} in:`) + for (const file of out) { + console.log(`- ${file}`) + } + } -async function fixKnownLockfileIssues() { - const txt = await Bun.file(lockfile).text() - const stale = findStaleLockfileEntriesInText(txt) - if (stale.length === 0) return [] - if (stale.some((item) => !item.entry.startsWith("opentui-spinner/@opentui/"))) return [] + console.log("Running bun install to update bun.lock...") + const install = Bun.spawn([process.execPath, "install"], { + cwd: root, + stdout: "inherit", + stderr: "inherit", + }) + const installCode = await install.exited + if (installCode !== 0) process.exit(installCode) + + async function fixKnownLockfileIssues() { + const txt = await Bun.file(lockfile).text() + const stale = findStaleLockfileEntriesInText(txt) + if (stale.length === 0) return [] + if (stale.some((item) => !item.entry.startsWith("opentui-spinner/@opentui/"))) return [] + + const removed = txt + .split("\n") + .map((line) => line.match(/^ "(opentui-spinner\/@opentui\/[^"]+)": /)?.[1]) + .filter((item): item is string => item !== undefined) + + if (removed.length === 0) return [] + + await Bun.write( + lockfile, + txt + .split("\n") + .filter((line) => !line.match(/^ "opentui-spinner\/@opentui\//)) + .join("\n"), + ) + return removed + } - const removed = txt - .split("\n") - .map((line) => line.match(/^ "(opentui-spinner\/@opentui\/[^\"]+)": /)?.[1]) - .filter((item): item is string => item !== undefined) + async function findStaleLockfileEntries() { + return findStaleLockfileEntriesInText(await Bun.file(lockfile).text()) + } - if (removed.length === 0) return [] + function findStaleLockfileEntriesInText(txt: string) { + return Array.from(txt.matchAll(/^ "([^"]+)": \["(@opentui\/(?:core(?:-[^@"]+)?|keymap|solid))@([^"]+)"/gm)) + .map((match) => ({ + entry: match[1]!, + pkg: match[2]!, + version: match[3]!, + })) + .filter((item) => item.version !== ver) + } - await Bun.write( - lockfile, - txt - .split("\n") - .filter((line) => !line.match(/^ "opentui-spinner\/@opentui\//)) - .join("\n"), - ) - return removed -} + const fixed = await fixKnownLockfileIssues() + if (fixed.length > 0) { + console.log("Removed stale opentui-spinner peer lockfile entries:") + for (const item of fixed) { + console.log(`- ${item}`) + } + } -async function findStaleLockfileEntries() { - return findStaleLockfileEntriesInText(await Bun.file(lockfile).text()) -} + const stale = await findStaleLockfileEntries() + if (stale.length > 0) { + console.error(`bun.lock still contains stale opentui versions after upgrading to ${ver}:`) + for (const item of stale) { + console.error(`- ${item.entry}: ${item.pkg}@${item.version}`) + } + process.exit(1) + } -function findStaleLockfileEntriesInText(txt: string) { - return Array.from(txt.matchAll(/^ "([^"]+)": \["(@opentui\/(?:core(?:-[^@"]+)?|keymap|solid))@([^"]+)"/gm)) - .map((match) => ({ - entry: match[1]!, - pkg: match[2]!, - version: match[3]!, - })) - .filter((item) => item.version !== ver) -} + console.log("bun.lock opentui versions are consistent") +} \ No newline at end of file