diff --git a/.gitignore b/.gitignore index 478c22e..c981ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ coverage *.tsbuildinfo .env .env.* +.claude/ diff --git a/biome.json b/biome.json index 29552c9..3a5efeb 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json", "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { "enabled": true, @@ -37,6 +37,6 @@ } }, "files": { - "includes": ["**", "!**/dist", "!**/node_modules", "!**/coverage"] + "includes": ["**", "!**/dist", "!**/node_modules", "!**/coverage", "!**/.claude"] } } diff --git a/package-lock.json b/package-lock.json index 67abbee..a3bc279 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,21 +9,21 @@ "version": "0.0.2", "license": "MIT", "dependencies": { - "@tigrisdata/storage": "^3.2.1", - "just-bash": "^2.14.2" + "@tigrisdata/storage": "^3.7.0", + "just-bash": "^3.0.1" }, "bin": { "agent-shell": "dist/cli.js", "tigris-agent-shell": "dist/cli.js" }, "devDependencies": { - "@biomejs/biome": "^2.4.13", - "@commitlint/cli": "^20.5.2", - "@commitlint/config-conventional": "^20.5.0", + "@biomejs/biome": "^2.4.15", + "@commitlint/cli": "^21.0.1", + "@commitlint/config-conventional": "^21.0.1", "husky": "^9.1.7", "semantic-release": "^25.0.3", "typescript": "~5.8.0", - "vitest": "^4.1.5" + "vitest": "^4.1.7" }, "engines": { "node": ">=20.0.0" @@ -965,9 +965,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.13.tgz", - "integrity": "sha512-gLXOwkOBBg0tr7bDsqlkIh4uFeKuMjxvqsrb1Tukww1iDmHcfr4Uu8MoQxp0Rcte+69+osRNWXwHsu/zxT6XqA==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.15.tgz", + "integrity": "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -981,20 +981,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.13", - "@biomejs/cli-darwin-x64": "2.4.13", - "@biomejs/cli-linux-arm64": "2.4.13", - "@biomejs/cli-linux-arm64-musl": "2.4.13", - "@biomejs/cli-linux-x64": "2.4.13", - "@biomejs/cli-linux-x64-musl": "2.4.13", - "@biomejs/cli-win32-arm64": "2.4.13", - "@biomejs/cli-win32-x64": "2.4.13" + "@biomejs/cli-darwin-arm64": "2.4.15", + "@biomejs/cli-darwin-x64": "2.4.15", + "@biomejs/cli-linux-arm64": "2.4.15", + "@biomejs/cli-linux-arm64-musl": "2.4.15", + "@biomejs/cli-linux-x64": "2.4.15", + "@biomejs/cli-linux-x64-musl": "2.4.15", + "@biomejs/cli-win32-arm64": "2.4.15", + "@biomejs/cli-win32-x64": "2.4.15" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.13.tgz", - "integrity": "sha512-2KImO1jhNFBa2oWConyr0x6flxbQpGKv6902uGXpYM62Xyem8U80j441SyUJ8KyngsmKbQjeIv1q2CQfDkNnYg==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.15.tgz", + "integrity": "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==", "cpu": [ "arm64" ], @@ -1009,9 +1009,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.13.tgz", - "integrity": "sha512-BKrJklbaFN4p1Ts4kPBczo+PkbsHQg57kmJ+vON9u2t6uN5okYHaSr7h/MutPCWQgg2lglaWoSmm+zhYW+oOkg==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.15.tgz", + "integrity": "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==", "cpu": [ "x64" ], @@ -1026,9 +1026,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.13.tgz", - "integrity": "sha512-NzkUDSqfvMBrPplKgVr3aXLHZ2NEELvvF4vZxXulEylKWIGqlvNEcwUcj9OLrn75TD3lJ/GIqCVlBwd1MZCuYQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.15.tgz", + "integrity": "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==", "cpu": [ "arm64" ], @@ -1043,9 +1043,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.13.tgz", - "integrity": "sha512-U5MsuBQW25dXaYtqWWSPM3P96H6Y+fHuja3TQpMNnylocHW0tEbtFTDlUj6oM+YJLntvEkQy4grBvQNUD4+RCg==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.15.tgz", + "integrity": "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==", "cpu": [ "arm64" ], @@ -1060,9 +1060,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.13.tgz", - "integrity": "sha512-Az3ZZedYRBo9EQzNnD9SxFcR1G5QsGo6VEc2hIyVPZ1rdKwee/7E9oeBBZFpE8Z44ekxsDQBqbiWGW5ShOhUSQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.15.tgz", + "integrity": "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==", "cpu": [ "x64" ], @@ -1077,9 +1077,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.13.tgz", - "integrity": "sha512-Z601MienRgTBDza/+u2CH3RSrWoXo9rtr8NK6A4KJzqGgfxx+H3VlyLgTJ4sRo40T3pIsqpTmiOQEvYzQvBRvQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.15.tgz", + "integrity": "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==", "cpu": [ "x64" ], @@ -1094,9 +1094,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.13.tgz", - "integrity": "sha512-Px9PS2B5/Q183bUwy/5VHqp3J2lzdOCeVGzMpphYfl8oSa7VDCqenBdqWpy6DCy/en4Rbf/Y1RieZF6dJPcc9A==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.15.tgz", + "integrity": "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==", "cpu": [ "arm64" ], @@ -1111,9 +1111,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.13", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.13.tgz", - "integrity": "sha512-tTcMkXyBrmHi9BfrD2VNHs/5rYIUKETqsBlYOvSAABwBkJhSDVb5e7wPukftsQbO3WzQkXe6kaztC6WtUOXSoQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.15.tgz", + "integrity": "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==", "cpu": [ "x64" ], @@ -1149,251 +1149,245 @@ } }, "node_modules/@commitlint/cli": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.2.tgz", - "integrity": "sha512-IXr5xd3IX8SEG936P8gcpozRplkDeDSwJlt8UvoY1winwIy2udTbQ/cOCgbaaxcjdDqVoS29VUcz/wkwnSozbA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.0.1.tgz", + "integrity": "sha512-8vq10krmbJwBkvzXKhbs4o4JQEVscd3pqOlWuDUaDBwbeL694/P33UC29tZQFTAgPU9fVJ2+f2m3zw16yKWxHg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/format": "^20.5.0", - "@commitlint/lint": "^20.5.0", - "@commitlint/load": "^20.5.2", - "@commitlint/read": "^20.5.0", - "@commitlint/types": "^20.5.0", + "@commitlint/format": "^21.0.1", + "@commitlint/lint": "^21.0.1", + "@commitlint/load": "^21.0.1", + "@commitlint/read": "^21.0.1", + "@commitlint/types": "^21.0.1", "tinyexec": "^1.0.0", - "yargs": "^17.0.0" + "yargs": "^18.0.0" }, "bin": { "commitlint": "cli.js" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/config-conventional": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.0.tgz", - "integrity": "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.0.1.tgz", + "integrity": "sha512-gRorrkfWOh/+V5X8GYWWbQvrzPczopGMS4CCNrQdHkK4xWElv82BDvIsDhJZWTlI7TazOlYea6VATufCsFs+sw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.5.0", + "@commitlint/types": "^21.0.1", "conventional-changelog-conventionalcommits": "^9.2.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/config-validator": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz", - "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.0.1.tgz", + "integrity": "sha512-Zd2UFdndeMMaW2O96HK0tdfT4gOImUvidMpAd/pws2zZ4m1nrAZ/9b/v2JYuE8fs86GpXv9F7LNaIuCIWhY+pA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.5.0", + "@commitlint/types": "^21.0.1", "ajv": "^8.11.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/ensure": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.0.tgz", - "integrity": "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.0.1.tgz", + "integrity": "sha512-jJ1037967wU7YN/xkv+iRlOBlmaOXPhPO5KQSqya6GyXzBlwuLzELBFao16DVg9dZyqmNrhewzwZ3SAibetHBQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.5.0", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" + "@commitlint/types": "^21.0.1", + "es-toolkit": "^1.46.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/execute-rule": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", - "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", + "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", "dev": true, "license": "MIT", "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/format": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz", - "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.0.1.tgz", + "integrity": "sha512-ksmG2+cHGtuDPQQbhBbC4unwm444+6TiPw0d1bKf67hntgZqZ8E0g1MuYKUuyT5IH4IMmXZhKq22/Z3jBvtQIw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.5.0", + "@commitlint/types": "^21.0.1", "picocolors": "^1.1.1" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/is-ignored": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.5.0.tgz", - "integrity": "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.0.1.tgz", + "integrity": "sha512-iNDP8SFdw8JEkM0CHZ2XFnhTN4Zg5jKUY2d8kBOSFrI2aA+3YJI7fcqVpfgbpJ9xtxFVYpi+DBATU5AvhoTq8g==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.5.0", + "@commitlint/types": "^21.0.1", "semver": "^7.6.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/lint": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.0.tgz", - "integrity": "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.0.1.tgz", + "integrity": "sha512-gF+iYtUw1gBG3HUH9z3VxwUjGg2R2G5j+nmvPs8aIeYkiB7TtneBu3wO85I0bUl93bYNsvsCNI9Nte2fmDUMww==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/is-ignored": "^20.5.0", - "@commitlint/parse": "^20.5.0", - "@commitlint/rules": "^20.5.0", - "@commitlint/types": "^20.5.0" + "@commitlint/is-ignored": "^21.0.1", + "@commitlint/parse": "^21.0.1", + "@commitlint/rules": "^21.0.1", + "@commitlint/types": "^21.0.1" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/load": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.2.tgz", - "integrity": "sha512-zmr0RGDz7vThxW1I8ohb9yBjnGuH9mqwJpn21hInjGla+IlLOkS9ey0+dD5HlkzFlY0lX2NYdA2lDW6/0rO7Gw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.0.1.tgz", + "integrity": "sha512-Btg1q1mKmiihN4W3x0EsPDrJMOQfMa9NIqlzlJyXAfxvsOGdGXOW5p3R3RcSxDCaY7JabY9flIl+Om1af3PSrw==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^20.5.0", - "@commitlint/execute-rule": "^20.0.0", - "@commitlint/resolve-extends": "^20.5.2", - "@commitlint/types": "^20.5.0", + "@commitlint/config-validator": "^21.0.1", + "@commitlint/execute-rule": "^21.0.1", + "@commitlint/resolve-extends": "^21.0.1", + "@commitlint/types": "^21.0.1", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", + "es-toolkit": "^1.46.0", "is-plain-obj": "^4.1.0", - "lodash.mergewith": "^4.6.2", "picocolors": "^1.1.1" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/message": { - "version": "20.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", - "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.0.1.tgz", + "integrity": "sha512-R3dVQeJQ0B6yqrZEjkUHD4r7UJYLV9Lvk2xs3PTOmtWk2G3mI6Xgc+YdRxL1PwcDfBiUjv2SkIkW4AUc976w1w==", "dev": true, "license": "MIT", "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/parse": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz", - "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.0.1.tgz", + "integrity": "sha512-oh/nCSOqdoeQNA1tO8aAmxkq5EBo8/NzcFQRvv66AWc9HpED28sL2iSicCKU6hPintWuscL6BJEWi77Wq1LPMQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/types": "^20.5.0", + "@commitlint/types": "^21.0.1", "conventional-changelog-angular": "^8.2.0", "conventional-commits-parser": "^6.3.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/read": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz", - "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.0.1.tgz", + "integrity": "sha512-pMEu4lbpC8W0ZgKJj2U6WaobXIZWdFlULpIEewYhkPXx+WZcnoO53YrVPc7QErQuNolq2Me8dP58Wu7YAVXVOA==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/top-level": "^20.4.3", - "@commitlint/types": "^20.5.0", + "@commitlint/top-level": "^21.0.1", + "@commitlint/types": "^21.0.1", "git-raw-commits": "^5.0.0", - "minimist": "^1.2.8", "tinyexec": "^1.0.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/resolve-extends": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.2.tgz", - "integrity": "sha512-8EhSCU9eNos/5cI1yg64GW79UH1c64O69AfStCsj4zqy6An/qIphVEXj4/+2M6056T8coz00f+UXFn4WUUP1HQ==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.0.1.tgz", + "integrity": "sha512-0DhjYWL6uYrY16Efa032fYk3woGJDU4AGWiG1XXltT9AMUNYKyb5cIZU2ivbaMZ3+kKFqUjikD2cjh66Sbh/Sg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/config-validator": "^20.5.0", - "@commitlint/types": "^20.5.0", + "@commitlint/config-validator": "^21.0.1", + "@commitlint/types": "^21.0.1", + "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", - "import-meta-resolve": "^4.0.0", - "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/rules": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.0.tgz", - "integrity": "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.0.1.tgz", + "integrity": "sha512-VMooYpz4nJg7xlaUso6CCOWEz8D/ChkvsvZUMARcoJ1ZpfKPyFCGrHNha2tbsETNAb6ErgiRuCr2DvghrvPDYQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/ensure": "^20.5.0", - "@commitlint/message": "^20.4.3", - "@commitlint/to-lines": "^20.0.0", - "@commitlint/types": "^20.5.0" + "@commitlint/ensure": "^21.0.1", + "@commitlint/message": "^21.0.1", + "@commitlint/to-lines": "^21.0.1", + "@commitlint/types": "^21.0.1" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/to-lines": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", - "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", + "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", "dev": true, "license": "MIT", "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/top-level": { - "version": "20.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", - "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.0.1.tgz", + "integrity": "sha512-4esUYqzY7K0FCgcJ/1xWEZekV7Ch4yZT1+xjEb7KzqbJ05XEkxHVsTfC8ADKNNtlCE2pj98KEbPGZWw9WwEnVw==", "dev": true, "license": "MIT", "dependencies": { "escalade": "^3.2.0" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@commitlint/types": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz", - "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.0.1.tgz", + "integrity": "sha512-4u7w8jcoCUFWhjWnASYzZHAP34OqOtuFBN87nQmFvqda03YU0T6z+yB4w0gSAMpekiRqqGk5rt+qSlW+a2vSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -1401,7 +1395,7 @@ "picocolors": "^1.1.1" }, "engines": { - "node": ">=v18" + "node": ">=22.12.0" } }, "node_modules/@conventional-changelog/git-client": { @@ -1432,9 +1426,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, @@ -1444,9 +1438,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, @@ -1724,9 +1718,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.126.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", - "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", + "version": "0.132.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", + "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", "dev": true, "license": "MIT", "funding": { @@ -1779,9 +1773,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.16.tgz", - "integrity": "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", + "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", "cpu": [ "arm64" ], @@ -1796,9 +1790,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.16.tgz", - "integrity": "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", "cpu": [ "arm64" ], @@ -1813,9 +1807,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.16.tgz", - "integrity": "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", + "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", "cpu": [ "x64" ], @@ -1830,9 +1824,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.16.tgz", - "integrity": "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", + "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", "cpu": [ "x64" ], @@ -1847,9 +1841,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.16.tgz", - "integrity": "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", + "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", "cpu": [ "arm" ], @@ -1864,9 +1858,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.16.tgz", - "integrity": "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", + "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", "cpu": [ "arm64" ], @@ -1881,9 +1875,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.16.tgz", - "integrity": "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", + "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", "cpu": [ "arm64" ], @@ -1898,9 +1892,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.16.tgz", - "integrity": "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", + "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", "cpu": [ "ppc64" ], @@ -1915,9 +1909,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.16.tgz", - "integrity": "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", + "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", "cpu": [ "s390x" ], @@ -1932,9 +1926,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.16.tgz", - "integrity": "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", + "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", "cpu": [ "x64" ], @@ -1949,9 +1943,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.16.tgz", - "integrity": "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", + "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", "cpu": [ "x64" ], @@ -1966,9 +1960,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.16.tgz", - "integrity": "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", + "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", "cpu": [ "arm64" ], @@ -1983,9 +1977,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.16.tgz", - "integrity": "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", + "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", "cpu": [ "wasm32" ], @@ -1993,8 +1987,8 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.9.2", - "@emnapi/runtime": "1.9.2", + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { @@ -2002,9 +1996,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.16.tgz", - "integrity": "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", "cpu": [ "arm64" ], @@ -2019,9 +2013,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.16.tgz", - "integrity": "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", "cpu": [ "x64" ], @@ -2036,9 +2030,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.16.tgz", - "integrity": "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, @@ -3162,15 +3156,16 @@ "license": "MIT" }, "node_modules/@tigrisdata/storage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@tigrisdata/storage/-/storage-3.2.1.tgz", - "integrity": "sha512-mbOVzSAJ0KPTJjhGbsNo6ok1gAmnmALWB3AuBHPky1+lXfvFty96gDYWteu7Vcz5hLJnKuo3Cni2Nk6o8d6cHg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@tigrisdata/storage/-/storage-3.7.0.tgz", + "integrity": "sha512-MtJE2sspBPq3t/tJ1BVICCtoNXw+GyP0in0/ykmdraryi4/5s0jKZL87RvjEYoS7/Ryih1ueaD9yP6E5nmcy3w==", "license": "MIT", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-sdk/client-s3": "^3.1038.0", "@aws-sdk/lib-storage": "^3.1038.0", "@aws-sdk/s3-request-presigner": "^3.1038.0", + "@aws-sdk/types": "^3.973.8", "@smithy/signature-v4": "^5.3.14", "dotenv": "^17.4.2" } @@ -3199,9 +3194,9 @@ "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, "license": "MIT", "optional": true, @@ -3228,21 +3223,21 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/normalize-package-data": { @@ -3253,16 +3248,16 @@ "license": "MIT" }, "node_modules/@vitest/expect": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", - "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz", + "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.5", - "@vitest/utils": "4.1.5", + "@vitest/spy": "4.1.7", + "@vitest/utils": "4.1.7", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -3271,13 +3266,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", - "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz", + "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.5", + "@vitest/spy": "4.1.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -3298,9 +3293,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", - "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz", + "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==", "dev": true, "license": "MIT", "dependencies": { @@ -3311,13 +3306,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", - "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz", + "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.5", + "@vitest/utils": "4.1.7", "pathe": "^2.0.3" }, "funding": { @@ -3325,14 +3320,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", - "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz", + "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.5", - "@vitest/utils": "4.1.5", + "@vitest/pretty-format": "4.1.7", + "@vitest/utils": "4.1.7", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -3341,9 +3336,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", - "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz", + "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==", "dev": true, "license": "MIT", "funding": { @@ -3351,13 +3346,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", - "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz", + "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.5", + "@vitest/pretty-format": "4.1.7", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -3569,9 +3564,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -3766,18 +3761,90 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/color-convert": { @@ -4317,6 +4384,17 @@ "dev": true, "license": "MIT" }, + "node_modules/es-toolkit": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4448,9 +4526,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -4465,9 +4543,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz", - "integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "funding": [ { "type": "github", @@ -4476,7 +4554,8 @@ ], "license": "MIT", "dependencies": { - "path-expression-matcher": "^1.1.3" + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" } }, "node_modules/fast-xml-parser": { @@ -4649,9 +4728,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "dev": true, "license": "MIT", "engines": { @@ -5197,13 +5276,13 @@ } }, "node_modules/just-bash": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/just-bash/-/just-bash-2.14.2.tgz", - "integrity": "sha512-9Na1rH03Ta5ydHTNotJ7dms1iZwb2kToOnKbnS29AlrCvi1CQ21Fm2lfu4S4rfwDGHYi4E4evgTDC/DcDx8tuQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/just-bash/-/just-bash-3.0.1.tgz", + "integrity": "sha512-YVyzCN08fKarUnwqy7rKOAcX+2MLYLnYInuowmUXn3mqhrtd4ieZNBuzdQG+qYV9DqnIWuv9Whiph0WRIWsBtw==", "license": "Apache-2.0", "dependencies": { "diff": "^8.0.2", - "fast-xml-parser": "^5.3.3", + "fast-xml-parser": "^5.7.3", "file-type": "^21.2.0", "ini": "^6.0.0", "minimatch": "^10.1.1", @@ -5227,6 +5306,28 @@ "node-liblzma": "^2.0.3" } }, + "node_modules/just-bash/node_modules/fast-xml-parser": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz", + "integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.2.0", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.3.0", + "xml-naming": "^0.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -5532,13 +5633,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", @@ -5567,34 +5661,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.uniqby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", @@ -5602,13 +5668,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -5815,9 +5874,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -8303,9 +8362,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -8323,7 +8382,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8570,14 +8629,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.16", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.16.tgz", - "integrity": "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", + "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.126.0", - "@rolldown/pluginutils": "1.0.0-rc.16" + "@oxc-project/types": "=0.132.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -8586,21 +8645,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.16", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", - "@rolldown/binding-darwin-x64": "1.0.0-rc.16", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" + "@rolldown/binding-android-arm64": "1.0.2", + "@rolldown/binding-darwin-arm64": "1.0.2", + "@rolldown/binding-darwin-x64": "1.0.2", + "@rolldown/binding-freebsd-x64": "1.0.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", + "@rolldown/binding-linux-arm64-gnu": "1.0.2", + "@rolldown/binding-linux-arm64-musl": "1.0.2", + "@rolldown/binding-linux-ppc64-gnu": "1.0.2", + "@rolldown/binding-linux-s390x-gnu": "1.0.2", + "@rolldown/binding-linux-x64-gnu": "1.0.2", + "@rolldown/binding-linux-x64-musl": "1.0.2", + "@rolldown/binding-openharmony-arm64": "1.0.2", + "@rolldown/binding-wasm32-wasi": "1.0.2", + "@rolldown/binding-win32-arm64-msvc": "1.0.2", + "@rolldown/binding-win32-x64-msvc": "1.0.2" } }, "node_modules/safe-buffer": { @@ -8665,41 +8724,6 @@ "node": "^22.14.0 || >= 24.10.0" } }, - "node_modules/semantic-release/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/semantic-release/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, "node_modules/semantic-release/node_modules/normalize-package-data": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", @@ -8784,40 +8808,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/semantic-release/node_modules/type-fest": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", @@ -8847,52 +8837,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/semantic-release/node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/semantic-release/node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -9345,9 +9289,9 @@ } }, "node_modules/strnum": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", - "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", "funding": [ { "type": "github", @@ -9808,9 +9752,9 @@ } }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT", "peer": true @@ -9899,16 +9843,16 @@ } }, "node_modules/vite": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.9.tgz", - "integrity": "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==", + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", + "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.10", - "rolldown": "1.0.0-rc.16", + "postcss": "^8.5.15", + "rolldown": "1.0.2", "tinyglobby": "^0.2.16" }, "bin": { @@ -9925,7 +9869,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", + "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -9990,19 +9934,19 @@ } }, "node_modules/vitest": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", - "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz", + "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.5", - "@vitest/mocker": "4.1.5", - "@vitest/pretty-format": "4.1.5", - "@vitest/runner": "4.1.5", - "@vitest/snapshot": "4.1.5", - "@vitest/spy": "4.1.5", - "@vitest/utils": "4.1.5", + "@vitest/expect": "4.1.7", + "@vitest/mocker": "4.1.7", + "@vitest/pretty-format": "4.1.7", + "@vitest/runner": "4.1.7", + "@vitest/snapshot": "4.1.7", + "@vitest/spy": "4.1.7", + "@vitest/utils": "4.1.7", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -10030,12 +9974,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.5", - "@vitest/browser-preview": "4.1.5", - "@vitest/browser-webdriverio": "4.1.5", - "@vitest/coverage-istanbul": "4.1.5", - "@vitest/coverage-v8": "4.1.5", - "@vitest/ui": "4.1.5", + "@vitest/browser-playwright": "4.1.7", + "@vitest/browser-preview": "4.1.7", + "@vitest/browser-webdriverio": "4.1.7", + "@vitest/coverage-istanbul": "4.1.7", + "@vitest/coverage-v8": "4.1.7", + "@vitest/ui": "4.1.7", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -10164,6 +10108,21 @@ "license": "ISC", "optional": true }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -10200,32 +10159,72 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", + "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^7.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yargs-parser": "^22.0.0" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "dev": true, "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/yoctocolors": { diff --git a/package.json b/package.json index 6365bbc..c3f6d71 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "node": ">=20.0.0" }, "dependencies": { - "@tigrisdata/storage": "^3.2.1", - "just-bash": "^2.14.2" + "@tigrisdata/storage": "^3.7.0", + "just-bash": "^3.0.1" }, "release": { "branches": [ @@ -81,12 +81,12 @@ ] }, "devDependencies": { - "@biomejs/biome": "^2.4.13", - "@commitlint/cli": "^20.5.2", - "@commitlint/config-conventional": "^20.5.0", + "@biomejs/biome": "^2.4.15", + "@commitlint/cli": "^21.0.1", + "@commitlint/config-conventional": "^21.0.1", "husky": "^9.1.7", "semantic-release": "^25.0.3", "typescript": "~5.8.0", - "vitest": "^4.1.5" + "vitest": "^4.1.7" } } diff --git a/src/cli.ts b/src/cli.ts index deff4dd..34ad8ee 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -70,27 +70,18 @@ async function main() { const args = parseArgs(rawArgs); const session = new ReplSession({ loginFn: deviceLogin }); - const REPL_COMMANDS = [ - "login", - "configure", - "mount", - "umount", - "df", - "flush", - "whoami", - "logout", - "clear", - "help", - "exit", - ]; - const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: "$ ", - completer: (line: string) => { - const hits = REPL_COMMANDS.filter((cmd) => cmd.startsWith(line)); - return [hits.length ? hits : REPL_COMMANDS, line]; + completer: ( + line: string, + callback: (err: Error | null, result?: [string[], string]) => void, + ) => { + session.complete(line).then( + (result) => callback(null, result), + (err) => callback(err instanceof Error ? err : new Error(String(err))), + ); }, }); diff --git a/src/commands/args.ts b/src/commands/args.ts new file mode 100644 index 0000000..e975e3a --- /dev/null +++ b/src/commands/args.ts @@ -0,0 +1,61 @@ +import type { ExecResult } from "just-bash"; + +/** Format a usage error. The `usage` line is appended on the next line if given. */ +export function argError(cmd: string, message: string, usage?: string): ExecResult { + const usageLine = usage ? `Usage: ${usage}\n` : ""; + return { + stdout: "", + stderr: `${cmd}: ${message}\n${usageLine}`, + exitCode: 1, + }; +} + +/** Format an SDK error consistently across commands. */ +export function sdkError(cmd: string, err: { message: string }): ExecResult { + return { stdout: "", stderr: `${cmd}: ${err.message}\n`, exitCode: 1 }; +} + +export type FlagSchema = Record; + +export interface FlagParseResult { + flags: Record; + positional: string[]; +} + +/** + * Parse a flag-and-positional argv. Returns either the parsed result or an + * error describing an unknown option or a missing value. Does not allow + * `--flag=value` syntax — values are taken from the following arg. + */ +export function parseFlags( + args: string[], + schema: FlagSchema, +): FlagParseResult | { error: string } { + const flags: Record = {}; + const positional: string[] = []; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (arg === undefined) continue; + if (!arg.startsWith("--")) { + positional.push(arg); + continue; + } + const kind = schema[arg]; + if (kind === undefined) { + return { error: `unknown option: ${arg}` }; + } + if (kind === "boolean") { + flags[arg] = true; + continue; + } + const value = args[i + 1]; + if (value === undefined || value.startsWith("--")) { + return { error: `option ${arg} requires a value` }; + } + flags[arg] = value; + i++; + } + + return { flags, positional }; +} diff --git a/src/commands/fork.ts b/src/commands/fork.ts index 073cf2b..bbee945 100644 --- a/src/commands/fork.ts +++ b/src/commands/fork.ts @@ -1,84 +1,94 @@ -import { createBucket, listBuckets } from "@tigrisdata/storage"; -import { defineCommand } from "just-bash"; +import { createBucket, listForks } from "@tigrisdata/storage"; +import { defineCommand, type ExecResult } from "just-bash"; import type { TigrisConfig } from "../types.js"; +import { argError, type FlagSchema, parseFlags, sdkError } from "./args.js"; -/** - * fork [--snapshot version] - * - * Create a fork of a bucket, optionally from a specific snapshot. - */ -export function createForkCommand(config: TigrisConfig) { - return defineCommand("fork", async (args) => { - const sourceBucket = args[0]; - const forkName = args[1]; - - if (!sourceBucket || !forkName) { - return { - stdout: "", - stderr: - "fork: missing arguments\nUsage: fork [--snapshot version]\n", - exitCode: 1, - }; - } - - let snapshotVersion: string | undefined; - for (let i = 2; i < args.length; i++) { - if (args[i] === "--snapshot" && args[i + 1]) { - snapshotVersion = args[i + 1]; - i++; - } - } - - const result = await createBucket(forkName, { - sourceBucketName: sourceBucket, - ...(snapshotVersion !== undefined && { sourceBucketSnapshot: snapshotVersion }), +const USAGE = + "fork [] --name [--snapshot version] | fork [] --list"; +const SCHEMA: FlagSchema = { + "--name": "value", + "--snapshot": "value", + "--list": "boolean", +}; + +export interface ForkOptions { + /** Resolve cwd to a mounted bucket so can be omitted. */ + resolveBucket?: (path: string) => { bucket: string; key: string } | null; +} + +type ForkInput = + | { mode: "create"; sourceBucket: string; forkName: string; snapshotVersion: string | undefined } + | { mode: "list"; sourceBucket: string }; + +export function createForkCommand(config: TigrisConfig, options?: ForkOptions) { + return defineCommand("fork", async (args, ctx) => { + const input = parseInput(args, ctx.cwd, options); + if ("stderr" in input) return input; + + if (input.mode === "list") return listForksOf(input.sourceBucket, config); + + const result = await createBucket(input.forkName, { + sourceBucketName: input.sourceBucket, + ...(input.snapshotVersion !== undefined && { + sourceBucketSnapshot: input.snapshotVersion, + }), config, }); + if ("error" in result) return sdkError("fork", result.error); - if ("error" in result) { - return { - stdout: "", - stderr: `fork: ${result.error.message}\n`, - exitCode: 1, - }; - } - - return { - stdout: `${forkName}\n`, - stderr: "", - exitCode: 0, - }; + return { stdout: `${input.forkName}\n`, stderr: "", exitCode: 0 }; }); } -/** - * forks — list all forks of a bucket. - */ -export function createForksListCommand(config: TigrisConfig) { - return defineCommand("forks", async (args) => { - const bucket = args[0]; - if (!bucket) { - return { - stdout: "", - stderr: "forks: missing bucket argument\nUsage: forks \n", - exitCode: 1, - }; - } - - const result = await listBuckets({ config }); - if ("error" in result) { - return { - stdout: "", - stderr: `forks: ${result.error.message}\n`, - exitCode: 1, - }; - } - - const lines = result.data.buckets.map((b) => b.name).join("\n"); - return { - stdout: lines ? `${lines}\n` : "", - stderr: "", - exitCode: 0, - }; - }); +function parseInput( + args: string[], + cwd: string, + options: ForkOptions | undefined, +): ForkInput | ExecResult { + const parsed = parseFlags(args, SCHEMA); + if ("error" in parsed) return argError("fork", parsed.error, USAGE); + const { flags, positional } = parsed; + + if (positional.length > 1) { + return argError("fork", `unexpected argument: ${positional[1]}`, USAGE); + } + + const isList = flags["--list"] === true; + const forkName = typeof flags["--name"] === "string" ? flags["--name"] : undefined; + const snapshotVersion = typeof flags["--snapshot"] === "string" ? flags["--snapshot"] : undefined; + + if (isList && forkName !== undefined) { + return argError("fork", "--name and --list cannot be combined", USAGE); + } + if (isList && snapshotVersion !== undefined) { + return argError("fork", "--snapshot and --list cannot be combined", USAGE); + } + if (!isList && forkName === undefined) { + return argError("fork", "either --name or --list is required", USAGE); + } + + const sourceBucket = positional[0] ?? options?.resolveBucket?.(cwd)?.bucket; + if (!sourceBucket) { + return argError("fork", "missing (cwd not in a mounted bucket)", USAGE); + } + + if (isList) return { mode: "list", sourceBucket }; + + if (sourceBucket === forkName) { + return argError("fork", " and --name must differ"); + } + + return { mode: "create", sourceBucket, forkName: forkName as string, snapshotVersion }; +} + +async function listForksOf(bucket: string, config: TigrisConfig) { + const result = await listForks(bucket, { config }); + if ("error" in result) return sdkError("fork", result.error); + + const forks = result.data.forks; + if (forks.length === 0) { + return { stdout: "No forks.\n", stderr: "", exitCode: 0 }; + } + const lines = forks.map((b) => b.name).join("\n"); + return { stdout: `${lines}\n`, stderr: "", exitCode: 0 }; } diff --git a/src/commands/index.ts b/src/commands/index.ts index 8963386..7b5a057 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,6 +1,6 @@ import type { Command } from "just-bash"; import type { TigrisConfig } from "../types.js"; -import { createForkCommand, createForksListCommand } from "./fork.js"; +import { createForkCommand } from "./fork.js"; import { createPresignCommand } from "./presign.js"; import { createSnapshotCommand } from "./snapshot.js"; @@ -8,15 +8,12 @@ import { createSnapshotCommand } from "./snapshot.js"; * Create all Tigris custom commands for use with just-bash. */ export function createTigrisCommands(config: TigrisConfig): Command[] { - return [ - createPresignCommand(config), - createSnapshotCommand(config), - createForkCommand(config), - createForksListCommand(config), - ]; + return [createPresignCommand(config), createSnapshotCommand(config), createForkCommand(config)]; } -export { createForkCommand, createForksListCommand } from "./fork.js"; +export type { ForkOptions } from "./fork.js"; +export { createForkCommand } from "./fork.js"; export type { PresignOptions } from "./presign.js"; export { createPresignCommand } from "./presign.js"; +export type { SnapshotOptions } from "./snapshot.js"; export { createSnapshotCommand } from "./snapshot.js"; diff --git a/src/commands/presign.ts b/src/commands/presign.ts index e4e5672..138c7e3 100644 --- a/src/commands/presign.ts +++ b/src/commands/presign.ts @@ -1,95 +1,95 @@ import { getPresignedUrl } from "@tigrisdata/storage"; -import { defineCommand } from "just-bash"; +import { defineCommand, type ExecResult } from "just-bash"; import type { TigrisConfig } from "../types.js"; +import { argError, type FlagSchema, parseFlags, sdkError } from "./args.js"; -/** - * presign [--expires N] [--put] - * - * Generate a presigned URL for a Tigris object. - * Defaults to GET with 1 hour expiry. - */ -function parsePresignArgs(args: string[]): { - expiresIn: number; - operation: "get" | "put"; -} { - let expiresIn = 3600; - let operation: "get" | "put" = "get"; - - for (let i = 0; i < args.length; i++) { - if (args[i] === "--expires" && args[i + 1]) { - expiresIn = Number.parseInt(args[i + 1] ?? "3600", 10); - i++; - } else if (args[i] === "--put") { - operation = "put"; - } - } - - return { expiresIn, operation }; -} +const USAGE = "presign [--expires N] [--put] [--key accessKeyId]"; +const SCHEMA: FlagSchema = { + "--expires": "value", + "--put": "boolean", + "--key": "value", +}; +const DEFAULT_EXPIRES = 3600; export interface PresignOptions { /** Resolve an absolute path to bucket + key from the mount table. */ resolveBucket?: (path: string) => { bucket: string; key: string } | null; } +interface PresignInput { + path: string; + expiresIn: number; + operation: "get" | "put"; + accessKeyOverride: string | undefined; +} + export function createPresignCommand(config: TigrisConfig, options?: PresignOptions) { return defineCommand("presign", async (args, ctx) => { - const rawPath = args[0]; - if (!rawPath) { - return { - stdout: "", - stderr: "presign: missing path argument\nUsage: presign [--expires N] [--put]\n", - exitCode: 1, - }; - } + const input = parseInput(args); + if ("stderr" in input) return input; - if (!config.accessKeyId) { - return { - stdout: "", - stderr: "presign: requires access key auth. Use 'configure' instead of 'login'.\n", - exitCode: 1, - }; + const accessKeyId = input.accessKeyOverride ?? config.accessKeyId; + if (!accessKeyId) { + return argError("presign", "--key is required when logged in via 'login'", USAGE); } - let resolved: { bucket: string; key: string } | null; - if (config.bucket) { - // Single-bucket mode — rawPath is relative to the bucket root - const key = rawPath.startsWith("/") ? rawPath.slice(1) : rawPath; - resolved = { bucket: config.bucket, key }; - } else { - // Multi-bucket — resolve against cwd to find the mount - const absolutePath = rawPath.startsWith("/") - ? rawPath - : `${ctx.cwd.replace(/\/$/, "")}/${rawPath}`; - resolved = options?.resolveBucket?.(absolutePath) ?? null; - } + const resolved = resolveTarget(input.path, ctx.cwd, config, options); if (!resolved) { - return { - stdout: "", - stderr: "presign: cannot determine bucket. cd into a mounted bucket first.\n", - exitCode: 1, - }; + return argError("presign", "cannot determine bucket. cd into a mounted bucket first."); } - const { expiresIn, operation } = parsePresignArgs(args.slice(1)); const result = await getPresignedUrl(resolved.key, { - operation, - expiresIn, + operation: input.operation, + expiresIn: input.expiresIn, + accessKeyId, config: { ...config, bucket: resolved.bucket }, }); + if ("error" in result) return sdkError("presign", result.error); - if ("error" in result) { - return { - stdout: "", - stderr: `presign: ${result.error.message}\n`, - exitCode: 1, - }; - } - - return { - stdout: `${result.data.url}\n`, - stderr: "", - exitCode: 0, - }; + return { stdout: `${result.data.url}\n`, stderr: "", exitCode: 0 }; }); } + +function parseInput(args: string[]): PresignInput | ExecResult { + const parsed = parseFlags(args, SCHEMA); + if ("error" in parsed) return argError("presign", parsed.error, USAGE); + const { flags, positional } = parsed; + + if (positional.length === 0) return argError("presign", "missing ", USAGE); + if (positional.length > 1) { + return argError("presign", `unexpected argument: ${positional[1]}`, USAGE); + } + + const expires = parseExpires(flags["--expires"]); + if (typeof expires !== "number") return expires; + + return { + path: positional[0] ?? "", + expiresIn: expires, + operation: flags["--put"] === true ? "put" : "get", + accessKeyOverride: typeof flags["--key"] === "string" ? flags["--key"] : undefined, + }; +} + +function parseExpires(raw: string | true | undefined): number | ExecResult { + if (typeof raw !== "string") return DEFAULT_EXPIRES; + const value = Number(raw); + if (!Number.isInteger(value) || value <= 0) { + return argError("presign", `--expires must be a positive integer (got '${raw}')`, USAGE); + } + return value; +} + +function resolveTarget( + rawPath: string, + cwd: string, + config: TigrisConfig, + options: PresignOptions | undefined, +): { bucket: string; key: string } | null { + if (config.bucket) { + const key = rawPath.startsWith("/") ? rawPath.slice(1) : rawPath; + return { bucket: config.bucket, key }; + } + const absolutePath = rawPath.startsWith("/") ? rawPath : `${cwd.replace(/\/$/, "")}/${rawPath}`; + return options?.resolveBucket?.(absolutePath) ?? null; +} diff --git a/src/commands/snapshot.ts b/src/commands/snapshot.ts index d791402..ce7705e 100644 --- a/src/commands/snapshot.ts +++ b/src/commands/snapshot.ts @@ -1,72 +1,81 @@ import { createBucketSnapshot, listBucketSnapshots } from "@tigrisdata/storage"; -import { defineCommand } from "just-bash"; +import { defineCommand, type ExecResult } from "just-bash"; import type { TigrisConfig } from "../types.js"; +import { argError, type FlagSchema, parseFlags, sdkError } from "./args.js"; -/** - * snapshot [--name label] [--list] - * - * Create or list point-in-time bucket snapshots. - */ -function parseSnapshotArgs(args: string[]): { - isList: boolean; - name: string | undefined; -} { - let isList = false; - let name: string | undefined; - - for (let i = 0; i < args.length; i++) { - if (args[i] === "--list") { - isList = true; - } else if (args[i] === "--name" && args[i + 1]) { - name = args[i + 1]; - i++; - } - } +const USAGE = "snapshot [] [--name label] [--list]"; +const SCHEMA: FlagSchema = { + "--name": "value", + "--list": "boolean", +}; - return { isList, name }; +export interface SnapshotOptions { + /** Resolve cwd to a mounted bucket so can be omitted. */ + resolveBucket?: (path: string) => { bucket: string; key: string } | null; } -async function listSnapshots(bucket: string, config: TigrisConfig) { - const result = await listBucketSnapshots(bucket, { config }); - if ("error" in result) { - return { stdout: "", stderr: `snapshot: ${result.error.message}\n`, exitCode: 1 }; - } - const lines = result.data.snapshots - .map((s) => { - const label = s.name ? ` (${s.name})` : ""; - const date = s.creationDate?.toISOString() ?? "unknown"; - return `${s.version}${label} ${date}`; - }) - .join("\n"); - return { stdout: lines ? `${lines}\n` : "", stderr: "", exitCode: 0 }; +interface SnapshotInput { + bucket: string; + mode: "list" | "create"; + name: string | undefined; } -export function createSnapshotCommand(config: TigrisConfig) { - return defineCommand("snapshot", async (args) => { - const bucket = args[0]; - if (!bucket) { - return { - stdout: "", - stderr: - "snapshot: missing bucket argument\nUsage: snapshot [--name label] [--list]\n", - exitCode: 1, - }; - } - - const { isList, name } = parseSnapshotArgs(args.slice(1)); +export function createSnapshotCommand(config: TigrisConfig, options?: SnapshotOptions) { + return defineCommand("snapshot", async (args, ctx) => { + const input = parseInput(args, ctx.cwd, options); + if ("stderr" in input) return input; - if (isList) { - return listSnapshots(bucket, config); - } + if (input.mode === "list") return listSnapshots(input.bucket, config); - const result = await createBucketSnapshot(bucket, { - ...(name !== undefined && { name }), + const result = await createBucketSnapshot(input.bucket, { + ...(input.name !== undefined && { name: input.name }), config, }); - if ("error" in result) { - return { stdout: "", stderr: `snapshot: ${result.error.message}\n`, exitCode: 1 }; - } + if ("error" in result) return sdkError("snapshot", result.error); return { stdout: `${result.data.snapshotVersion}\n`, stderr: "", exitCode: 0 }; }); } + +function parseInput( + args: string[], + cwd: string, + options: SnapshotOptions | undefined, +): SnapshotInput | ExecResult { + const parsed = parseFlags(args, SCHEMA); + if ("error" in parsed) return argError("snapshot", parsed.error, USAGE); + const { flags, positional } = parsed; + + if (positional.length > 1) { + return argError("snapshot", `unexpected argument: ${positional[1]}`, USAGE); + } + + const isList = flags["--list"] === true; + const name = typeof flags["--name"] === "string" ? flags["--name"] : undefined; + if (isList && name !== undefined) { + return argError("snapshot", "--name and --list cannot be combined", USAGE); + } + + const bucket = positional[0] ?? options?.resolveBucket?.(cwd)?.bucket; + if (!bucket) { + return argError("snapshot", "missing (cwd not in a mounted bucket)", USAGE); + } + + return { bucket, mode: isList ? "list" : "create", name }; +} + +async function listSnapshots(bucket: string, config: TigrisConfig) { + const result = await listBucketSnapshots(bucket, { config }); + if ("error" in result) return sdkError("snapshot", result.error); + + const snapshots = result.data.snapshots; + if (snapshots.length === 0) { + return { stdout: "No snapshots.\n", stderr: "", exitCode: 0 }; + } + const lines = snapshots.map((s) => { + const label = s.name ? ` (${s.name})` : ""; + const date = s.creationDate?.toISOString() ?? "unknown"; + return `${s.version}${label} ${date}`; + }); + return { stdout: `${lines.join("\n")}\n`, stderr: "", exitCode: 0 }; +} diff --git a/src/repl/complete.ts b/src/repl/complete.ts new file mode 100644 index 0000000..ae4ccb7 --- /dev/null +++ b/src/repl/complete.ts @@ -0,0 +1,146 @@ +import { getCommandNames } from "just-bash"; +import type { TigrisShell } from "../shell.js"; + +type ShellFs = TigrisShell["engine"]["fs"]; + +const REPL_COMMANDS = [ + "login", + "configure", + "mount", + "umount", + "df", + "flush", + "whoami", + "logout", + "clear", + "help", + "exit", + "quit", +]; + +const CUSTOM_COMMANDS = ["presign", "snapshot", "fork"]; + +const BUCKET_ARG_COMMANDS = new Set(["mount", "snapshot", "fork"]); +const MOUNT_POINT_ARG_COMMANDS = new Set(["umount", "flush"]); + +export interface CompleteContext { + shell: TigrisShell | null; + cwd: string | undefined; +} + +let cachedAllCommands: string[] | undefined; + +function allCommandNames(): string[] { + if (!cachedAllCommands) { + const seen = new Set(); + const ordered: string[] = []; + for (const name of [...REPL_COMMANDS, ...CUSTOM_COMMANDS, ...getCommandNames()]) { + if (seen.has(name)) continue; + seen.add(name); + ordered.push(name); + } + cachedAllCommands = ordered; + } + return cachedAllCommands; +} + +/** + * Compute completion candidates for a partial command line. + * + * Returns `[hits, completedToken]` in the shape that node:readline expects: + * readline appends `hit.slice(completedToken.length)` when there is a single hit. + */ +export async function computeCompletions( + line: string, + ctx: CompleteContext, +): Promise<[string[], string]> { + const match = /\s(\S*)$/.exec(line); + const tokenStart = match ? match.index + 1 : 0; + const currentToken = line.slice(tokenStart); + + const before = line.slice(0, tokenStart).trim(); + const argIndex = before === "" ? 0 : before.split(/\s+/).length; + + if (argIndex === 0) { + const all = allCommandNames(); + const hits = all.filter((c) => c.startsWith(currentToken)); + return [hits.length > 0 ? hits : all, currentToken]; + } + + const commandName = before.split(/\s+/)[0] ?? ""; + + if (BUCKET_ARG_COMMANDS.has(commandName) && argIndex === 1) { + const buckets = ctx.shell?.listMounts().map((m) => m.bucket) ?? []; + const unique = Array.from(new Set(buckets)); + return [unique.filter((b) => b.startsWith(currentToken)), currentToken]; + } + + if (MOUNT_POINT_ARG_COMMANDS.has(commandName) && argIndex === 1) { + const points = ctx.shell?.listMounts().map((m) => m.mountPoint) ?? []; + return [points.filter((p) => p.startsWith(currentToken)), currentToken]; + } + + return [await completePath(currentToken, ctx), currentToken]; +} + +interface ResolvedPath { + dir: string; + prefix: string; + displayDir: string; +} + +function resolvePathToken(token: string, cwd: string): ResolvedPath { + const lastSlash = token.lastIndexOf("/"); + if (lastSlash === -1) { + return { dir: cwd, prefix: token, displayDir: "" }; + } + const beforeSlash = token.slice(0, lastSlash); + const prefix = token.slice(lastSlash + 1); + const dir = token.startsWith("/") + ? beforeSlash === "" + ? "/" + : beforeSlash + : `${cwd}/${beforeSlash}`.replace(/\/\/+/g, "/"); + return { dir, prefix, displayDir: `${beforeSlash}/` }; +} + +interface DirEntry { + name: string; + isDirectory: boolean; +} + +async function readDirEntries(fs: ShellFs, dir: string): Promise { + if (typeof fs.readdirWithFileTypes === "function") { + const result = await fs.readdirWithFileTypes(dir); + return result.map((d) => ({ name: d.name, isDirectory: d.isDirectory })); + } + const names = await fs.readdir(dir); + return Promise.all( + names.map(async (name) => { + try { + const stat = await fs.stat(`${dir}/${name}`.replace(/\/\/+/g, "/")); + return { name, isDirectory: stat.isDirectory }; + } catch { + return { name, isDirectory: false }; + } + }), + ); +} + +async function completePath(token: string, ctx: CompleteContext): Promise { + if (!ctx.shell) return []; + const fs = ctx.shell.engine.fs; + const cwd = ctx.cwd ?? ctx.shell.engine.getCwd(); + const { dir, prefix, displayDir } = resolvePathToken(token, cwd); + + let entries: DirEntry[]; + try { + entries = await readDirEntries(fs, dir); + } catch { + return []; + } + + return entries + .filter((e) => e.name.startsWith(prefix)) + .map((e) => `${displayDir}${e.name}${e.isDirectory ? "/" : ""}`); +} diff --git a/src/repl/session.ts b/src/repl/session.ts index 4a62c99..dfb70ee 100644 --- a/src/repl/session.ts +++ b/src/repl/session.ts @@ -3,6 +3,7 @@ import type { BashExecResult } from "just-bash"; import { TigrisShell } from "../shell.js"; import { type TigrisConfig, withConfigDefaults } from "../types.js"; import type { LoginFn } from "./auth.js"; +import { computeCompletions } from "./complete.js"; import type { ReplIO } from "./io.js"; export interface ReplSessionOptions { @@ -412,10 +413,10 @@ export class ReplSession { io.write(" umount Unmount a path\n"); io.write(" df List mounts\n"); io.write(" flush [path] Flush changes to Tigris\n"); - io.write(" presign [--expires N] [--put] Generate a presigned URL\n"); - io.write(" snapshot [--name N] [--list] Create or list snapshots\n"); - io.write(" fork [--snapshot V] Fork a bucket\n"); - io.write(" forks List forks\n"); + io.write(" presign [--expires N] [--put] [--key] Generate a presigned URL\n"); + io.write(" snapshot [] [--name N] [--list] Create or list snapshots\n"); + io.write(" fork [] --name [--snapshot V] Fork a bucket\n"); + io.write(" fork [] --list List forks\n"); io.write("\nShell:\n"); io.write(" clear Clear screen\n"); io.write(" help Show this help\n"); @@ -428,6 +429,14 @@ export class ReplSession { return this.shell !== null; } + /** + * Tab-completion entry point. Returns `[hits, completedToken]` in the + * shape that node:readline expects. + */ + async complete(line: string): Promise<[string[], string]> { + return computeCompletions(line, { shell: this.shell, cwd: this.cwd }); + } + /** Get the current prompt string (e.g. "/my-bucket $ "). */ get promptText(): string { if (this.cwd) { diff --git a/src/shell.ts b/src/shell.ts index 5e29b21..9e49d6b 100644 --- a/src/shell.ts +++ b/src/shell.ts @@ -1,6 +1,6 @@ import type { BashExecResult } from "just-bash"; import { Bash, InMemoryFs, MountableFs } from "just-bash"; -import { createForkCommand, createForksListCommand } from "./commands/fork.js"; +import { createForkCommand } from "./commands/fork.js"; import { createPresignCommand } from "./commands/presign.js"; import { createSnapshotCommand } from "./commands/snapshot.js"; import { TigrisAdapter } from "./fs/tigris-adapter.js"; @@ -45,14 +45,23 @@ export class TigrisShell { this.bash = new Bash({ fs: this.mountableFs, cwd, + // just-bash v3 enables defense-in-depth by default, which blocks + // process.env reads. The Tigris/AWS SDK reads env eagerly (e.g. + // AWS_PROFILE) from inside the TigrisAdapter, so we disable the + // sandbox here. We don't enable js-exec / python / node, so the + // sandbox provides no real value in our setup. + defenseInDepth: false, ...(shellOptions?.env !== undefined && { env: shellOptions.env }), customCommands: [ createPresignCommand(resolvedConfig, { resolveBucket: (path) => this.resolveBucketForPath(path), }), - createSnapshotCommand(resolvedConfig), - createForkCommand(resolvedConfig), - createForksListCommand(resolvedConfig), + createSnapshotCommand(resolvedConfig, { + resolveBucket: (path) => this.resolveBucketForPath(path), + }), + createForkCommand(resolvedConfig, { + resolveBucket: (path) => this.resolveBucketForPath(path), + }), ], }); } diff --git a/tests/commands.test.ts b/tests/commands.test.ts index edbcf28..3457ecf 100644 --- a/tests/commands.test.ts +++ b/tests/commands.test.ts @@ -5,7 +5,7 @@ vi.mock("@tigrisdata/storage", () => ({ createBucketSnapshot: vi.fn(), listBucketSnapshots: vi.fn(), createBucket: vi.fn(), - listBuckets: vi.fn(), + listForks: vi.fn(), })); import { @@ -13,9 +13,10 @@ import { createBucketSnapshot, getPresignedUrl, listBucketSnapshots, - listBuckets, + listForks, } from "@tigrisdata/storage"; -import { createForkCommand, createForksListCommand } from "../src/commands/fork.js"; +import { EMPTY_BYTES } from "just-bash"; +import { createForkCommand } from "../src/commands/fork.js"; import { createPresignCommand } from "../src/commands/presign.js"; import { createSnapshotCommand } from "../src/commands/snapshot.js"; import { TEST_CONFIG_WITH_BUCKET } from "./helpers.js"; @@ -23,15 +24,23 @@ import { TEST_CONFIG_WITH_BUCKET } from "./helpers.js"; const config = TEST_CONFIG_WITH_BUCKET; // Minimal CommandContext for testing -function makeCtx() { +function makeCtx(cwd = "/") { return { fs: {} as never, - cwd: "/", + cwd, env: new Map(), - stdin: "", + stdin: EMPTY_BYTES, }; } +// Resolves any path under //* to that bucket. Used to test the +// cwd-bucket fallback in snapshot and fork. +function stubResolveBucket(path: string): { bucket: string; key: string } | null { + const match = /^\/([^/]+)(?:\/(.*))?$/.exec(path); + if (!match) return null; + return { bucket: match[1] ?? "", key: match[2] ?? "" }; +} + afterEach(() => { vi.clearAllMocks(); }); @@ -42,7 +51,38 @@ describe("presign", () => { it("returns error when path is missing", async () => { const result = await cmd.execute([], makeCtx()); expect(result.exitCode).toBe(1); - expect(result.stderr).toContain("missing path"); + expect(result.stderr).toContain("missing "); + expect(result.stderr).toContain("Usage: presign"); + }); + + it("rejects unknown options", async () => { + const result = await cmd.execute(["/f.txt", "--exires", "60"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unknown option: --exires"); + }); + + it("rejects extra positional args", async () => { + const result = await cmd.execute(["/a.txt", "/b.txt"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unexpected argument: /b.txt"); + }); + + it("rejects --expires without a value", async () => { + const result = await cmd.execute(["/f.txt", "--expires"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--expires requires a value"); + }); + + it("rejects non-numeric --expires", async () => { + const result = await cmd.execute(["/f.txt", "--expires", "abc"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--expires must be a positive integer"); + }); + + it("rejects non-positive --expires", async () => { + const result = await cmd.execute(["/f.txt", "--expires", "-1"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--expires must be a positive integer"); }); it("generates a GET presigned URL", async () => { @@ -56,6 +96,7 @@ describe("presign", () => { expect(vi.mocked(getPresignedUrl)).toHaveBeenCalledWith("file.txt", { operation: "get", expiresIn: 3600, + accessKeyId: "tid_test", config, }); }); @@ -70,10 +111,52 @@ describe("presign", () => { expect(vi.mocked(getPresignedUrl)).toHaveBeenCalledWith("file.txt", { operation: "put", expiresIn: 7200, + accessKeyId: "tid_test", config, }); }); + it("--key overrides the configured access key", async () => { + vi.mocked(getPresignedUrl).mockResolvedValue({ + data: { url: "https://example.com/signed", expiresIn: 3600, operation: "get" }, + }); + + const result = await cmd.execute(["/file.txt", "--key", "tid_other"], makeCtx()); + expect(result.exitCode).toBe(0); + expect(vi.mocked(getPresignedUrl)).toHaveBeenCalledWith( + "file.txt", + expect.objectContaining({ accessKeyId: "tid_other" }), + ); + }); + + describe("OAuth session (no access key in config)", () => { + const oauthConfig: typeof config = { + sessionToken: "session_test", + organizationId: "org_test", + bucket: "test", + }; + const oauthCmd = createPresignCommand(oauthConfig); + + it("errors when --key is not provided", async () => { + const result = await oauthCmd.execute(["/file.txt"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--key is required"); + }); + + it("succeeds when --key is provided", async () => { + vi.mocked(getPresignedUrl).mockResolvedValue({ + data: { url: "https://example.com/signed", expiresIn: 3600, operation: "get" }, + }); + + const result = await oauthCmd.execute(["/file.txt", "--key", "tid_user"], makeCtx()); + expect(result.exitCode).toBe(0); + expect(vi.mocked(getPresignedUrl)).toHaveBeenCalledWith( + "file.txt", + expect.objectContaining({ accessKeyId: "tid_user" }), + ); + }); + }); + it("returns error on SDK failure", async () => { vi.mocked(getPresignedUrl).mockResolvedValue({ error: new Error("denied") }); @@ -86,10 +169,66 @@ describe("presign", () => { describe("snapshot", () => { const cmd = createSnapshotCommand(config); - it("returns error when bucket is missing", async () => { + it("returns error when bucket is missing and cwd not in a mount", async () => { const result = await cmd.execute([], makeCtx()); expect(result.exitCode).toBe(1); - expect(result.stderr).toContain("missing bucket"); + expect(result.stderr).toContain("missing "); + expect(result.stderr).toContain("Usage: snapshot"); + }); + + it("falls back to cwd bucket when positional omitted", async () => { + const cmdWithResolve = createSnapshotCommand(config, { resolveBucket: stubResolveBucket }); + vi.mocked(createBucketSnapshot).mockResolvedValue({ + data: { snapshotVersion: "1713200000" }, + }); + + const result = await cmdWithResolve.execute([], makeCtx("/my-bucket/sub")); + expect(result.exitCode).toBe(0); + expect(vi.mocked(createBucketSnapshot)).toHaveBeenCalledWith( + "my-bucket", + expect.objectContaining({ config }), + ); + }); + + it("explicit bucket wins over cwd fallback", async () => { + const cmdWithResolve = createSnapshotCommand(config, { resolveBucket: stubResolveBucket }); + vi.mocked(createBucketSnapshot).mockResolvedValue({ + data: { snapshotVersion: "1713200000" }, + }); + + await cmdWithResolve.execute(["explicit"], makeCtx("/my-bucket")); + expect(vi.mocked(createBucketSnapshot)).toHaveBeenCalledWith( + "explicit", + expect.objectContaining({ config }), + ); + }); + + it("rejects unknown options", async () => { + const result = await cmd.execute(["b", "--label", "v1"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unknown option: --label"); + }); + + it("rejects --name and --list together", async () => { + const result = await cmd.execute(["b", "--list", "--name", "v1"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--name and --list cannot be combined"); + }); + + it("rejects extra positional args", async () => { + const result = await cmd.execute(["b", "extra"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unexpected argument: extra"); + }); + + it("prints 'No snapshots.' when listing is empty", async () => { + vi.mocked(listBucketSnapshots).mockResolvedValue({ + data: { snapshots: [] }, + }); + + const result = await cmd.execute(["my-bucket", "--list"], makeCtx()); + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain("No snapshots."); }); it("creates a snapshot", async () => { @@ -143,16 +282,40 @@ describe("snapshot", () => { describe("fork", () => { const cmd = createForkCommand(config); - it("returns error when arguments are missing", async () => { + it("returns error when neither --name nor --list is given", async () => { const result = await cmd.execute(["source-only"], makeCtx()); expect(result.exitCode).toBe(1); - expect(result.stderr).toContain("missing arguments"); + expect(result.stderr).toContain("either --name or --list is required"); + }); + + it("returns error when source bucket is missing and cwd not in a mount", async () => { + const result = await cmd.execute(["--name", "fork-name"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("missing "); + }); + + it("rejects source == name", async () => { + const result = await cmd.execute(["same", "--name", "same"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("must differ"); + }); + + it("rejects unknown options", async () => { + const result = await cmd.execute(["a", "--name", "b", "--snap", "v"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unknown option: --snap"); + }); + + it("rejects extra positional args", async () => { + const result = await cmd.execute(["a", "b", "--name", "fork"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unexpected argument: b"); }); it("creates a fork", async () => { vi.mocked(createBucket).mockResolvedValue({ data: {} as never }); - const result = await cmd.execute(["source-bucket", "my-fork"], makeCtx()); + const result = await cmd.execute(["source-bucket", "--name", "my-fork"], makeCtx()); expect(result.exitCode).toBe(0); expect(result.stdout.trim()).toBe("my-fork"); expect(vi.mocked(createBucket)).toHaveBeenCalledWith("my-fork", { @@ -164,7 +327,7 @@ describe("fork", () => { it("creates a fork from a snapshot", async () => { vi.mocked(createBucket).mockResolvedValue({ data: {} as never }); - await cmd.execute(["source", "fork-name", "--snapshot", "1713200000"], makeCtx()); + await cmd.execute(["source", "--name", "fork-name", "--snapshot", "1713200000"], makeCtx()); expect(vi.mocked(createBucket)).toHaveBeenCalledWith("fork-name", { sourceBucketName: "source", sourceBucketSnapshot: "1713200000", @@ -172,38 +335,94 @@ describe("fork", () => { }); }); + it("falls back to cwd bucket for source when omitted", async () => { + const cmdWithResolve = createForkCommand(config, { resolveBucket: stubResolveBucket }); + vi.mocked(createBucket).mockResolvedValue({ data: {} as never }); + + await cmdWithResolve.execute(["--name", "new-fork"], makeCtx("/my-bucket/sub")); + expect(vi.mocked(createBucket)).toHaveBeenCalledWith( + "new-fork", + expect.objectContaining({ sourceBucketName: "my-bucket" }), + ); + }); + it("returns error on SDK failure", async () => { vi.mocked(createBucket).mockResolvedValue({ error: new Error("already exists") }); - const result = await cmd.execute(["source", "fork"], makeCtx()); + const result = await cmd.execute(["source", "--name", "fork"], makeCtx()); expect(result.exitCode).toBe(1); expect(result.stderr).toContain("already exists"); }); -}); -describe("forks", () => { - const cmd = createForksListCommand(config); + describe("--list", () => { + it("returns error when source bucket is missing and cwd not in a mount", async () => { + const result = await cmd.execute(["--list"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("missing "); + }); - it("returns error when bucket is missing", async () => { - const result = await cmd.execute([], makeCtx()); - expect(result.exitCode).toBe(1); - expect(result.stderr).toContain("missing bucket"); - }); + it("falls back to cwd bucket when source omitted", async () => { + const cmdWithResolve = createForkCommand(config, { resolveBucket: stubResolveBucket }); + vi.mocked(listForks).mockResolvedValue({ data: { forks: [] } }); - it("lists buckets", async () => { - vi.mocked(listBuckets).mockResolvedValue({ - data: { - buckets: [ - { name: "bucket-a", creationDate: new Date() }, - { name: "bucket-b", creationDate: new Date() }, - ], - owner: { name: "test", id: "1" }, - }, + const result = await cmdWithResolve.execute(["--list"], makeCtx("/my-bucket")); + expect(result.exitCode).toBe(0); + expect(vi.mocked(listForks)).toHaveBeenCalledWith("my-bucket", expect.anything()); }); - const result = await cmd.execute(["my-bucket"], makeCtx()); - expect(result.exitCode).toBe(0); - expect(result.stdout).toContain("bucket-a"); - expect(result.stdout).toContain("bucket-b"); + it("rejects extra positional args", async () => { + const result = await cmd.execute(["a", "b", "--list"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("unexpected argument: b"); + }); + + it("rejects --snapshot + --list", async () => { + const result = await cmd.execute(["a", "--list", "--snapshot", "v1"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--snapshot and --list cannot be combined"); + }); + + it("rejects --name + --list", async () => { + const result = await cmd.execute(["a", "--list", "--name", "x"], makeCtx()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("--name and --list cannot be combined"); + }); + + it("prints 'No forks.' when listing is empty", async () => { + vi.mocked(listForks).mockResolvedValue({ data: { forks: [] } }); + + const result = await cmd.execute(["my-bucket", "--list"], makeCtx()); + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain("No forks."); + }); + + it("lists forks", async () => { + const now = new Date(); + vi.mocked(listForks).mockResolvedValue({ + data: { + forks: [ + { + name: "bucket-a", + creationDate: now, + forkCreatedAt: now, + snapshot: "snap-a", + snapshotCreatedAt: now, + }, + { + name: "bucket-b", + creationDate: now, + forkCreatedAt: now, + snapshot: "snap-b", + snapshotCreatedAt: now, + }, + ], + }, + }); + + const result = await cmd.execute(["my-bucket", "--list"], makeCtx()); + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain("bucket-a"); + expect(result.stdout).toContain("bucket-b"); + }); }); }); diff --git a/tests/complete.test.ts b/tests/complete.test.ts new file mode 100644 index 0000000..51efd44 --- /dev/null +++ b/tests/complete.test.ts @@ -0,0 +1,213 @@ +import { describe, expect, it, vi } from "vitest"; +import { computeCompletions } from "../src/repl/complete.js"; +import { TigrisShell } from "../src/shell.js"; +import { TEST_CONFIG, TEST_CONFIG_WITH_BUCKET } from "./helpers.js"; + +vi.mock("@tigrisdata/storage", () => ({ + get: vi.fn(), + put: vi.fn(), + head: vi.fn(), + list: vi.fn(), + remove: vi.fn(), + updateObject: vi.fn(), + getPresignedUrl: vi.fn(), + createBucketSnapshot: vi.fn(), + listBucketSnapshots: vi.fn(), + createBucket: vi.fn(), + listBuckets: vi.fn(), + bundle: vi.fn(), +})); + +describe("computeCompletions", () => { + describe("command completion", () => { + it("returns full command list for empty line", async () => { + const [hits, token] = await computeCompletions("", { shell: null, cwd: undefined }); + expect(token).toBe(""); + expect(hits).toContain("login"); + expect(hits).toContain("configure"); + expect(hits).toContain("presign"); + expect(hits).toContain("ls"); + expect(hits).toContain("grep"); + }); + + it("filters built-in commands by prefix", async () => { + const [hits, token] = await computeCompletions("gr", { shell: null, cwd: undefined }); + expect(token).toBe("gr"); + expect(hits).toContain("grep"); + expect(hits.every((h) => h.startsWith("gr"))).toBe(true); + }); + + it("filters REPL commands by prefix", async () => { + const [hits, token] = await computeCompletions("mou", { shell: null, cwd: undefined }); + expect(token).toBe("mou"); + expect(hits).toEqual(["mount"]); + }); + + it("filters custom commands by prefix", async () => { + const [hits, token] = await computeCompletions("pres", { shell: null, cwd: undefined }); + expect(token).toBe("pres"); + expect(hits).toEqual(["presign"]); + }); + + it("returns full list when no command matches prefix", async () => { + const [hits] = await computeCompletions("zzzzzz", { shell: null, cwd: undefined }); + expect(hits.length).toBeGreaterThan(10); + }); + }); + + describe("bucket-name argument completion", () => { + it("completes bucket names for 'mount'", async () => { + const shell = new TigrisShell(TEST_CONFIG); + shell.mount("alpha", "/a"); + shell.mount("beta", "/b"); + + const [hits, token] = await computeCompletions("mount ", { + shell, + cwd: undefined, + }); + expect(token).toBe(""); + expect(hits.sort()).toEqual(["alpha", "beta"]); + }); + + it("filters bucket names by prefix", async () => { + const shell = new TigrisShell(TEST_CONFIG); + shell.mount("alpha", "/a"); + shell.mount("beta", "/b"); + + const [hits, token] = await computeCompletions("snapshot al", { + shell, + cwd: undefined, + }); + expect(token).toBe("al"); + expect(hits).toEqual(["alpha"]); + }); + + it("completes bucket names for 'fork'", async () => { + const shell = new TigrisShell(TEST_CONFIG); + shell.mount("alpha", "/a"); + + const [forkHits] = await computeCompletions("fork ", { shell, cwd: undefined }); + expect(forkHits).toEqual(["alpha"]); + }); + + it("falls back to path completion for the second arg of mount", async () => { + const shell = new TigrisShell(TEST_CONFIG); + shell.mount("alpha", "/a"); + + const [, token] = await computeCompletions("mount alpha /", { + shell, + cwd: undefined, + }); + expect(token).toBe("/"); + }); + }); + + describe("mount-point argument completion", () => { + it("completes mount points for 'umount'", async () => { + const shell = new TigrisShell(TEST_CONFIG); + shell.mount("alpha", "/data"); + shell.mount("beta", "/models"); + + const [hits, token] = await computeCompletions("umount /m", { + shell, + cwd: undefined, + }); + expect(token).toBe("/m"); + expect(hits).toEqual(["/models"]); + }); + + it("completes mount points for 'flush'", async () => { + const shell = new TigrisShell(TEST_CONFIG); + shell.mount("alpha", "/data"); + + const [hits] = await computeCompletions("flush ", { shell, cwd: undefined }); + expect(hits).toEqual(["/data"]); + }); + }); + + describe("path completion", () => { + it("lists cwd entries for bare token", async () => { + const shell = new TigrisShell(TEST_CONFIG, { cwd: "/workspace" }); + await shell.exec("mkdir -p /workspace && touch /workspace/notes.md /workspace/data.json"); + await shell.exec("mkdir /workspace/sub"); + + const [hits, token] = await computeCompletions("ls ", { + shell, + cwd: "/workspace", + }); + expect(token).toBe(""); + expect(hits.sort()).toEqual(["data.json", "notes.md", "sub/"]); + }); + + it("filters by file prefix in cwd", async () => { + const shell = new TigrisShell(TEST_CONFIG, { cwd: "/workspace" }); + await shell.exec("mkdir -p /workspace && touch /workspace/notes.md /workspace/data.json"); + + const [hits] = await computeCompletions("cat no", { + shell, + cwd: "/workspace", + }); + expect(hits).toEqual(["notes.md"]); + }); + + it("appends a trailing slash to directories", async () => { + const shell = new TigrisShell(TEST_CONFIG, { cwd: "/workspace" }); + await shell.exec("mkdir -p /workspace/projects"); + + const [hits] = await computeCompletions("cd pro", { + shell, + cwd: "/workspace", + }); + expect(hits).toEqual(["projects/"]); + }); + + it("completes absolute paths", async () => { + const shell = new TigrisShell(TEST_CONFIG, { cwd: "/" }); + await shell.exec("mkdir -p /etc /home"); + + const [hits, token] = await computeCompletions("ls /e", { + shell, + cwd: "/", + }); + expect(token).toBe("/e"); + expect(hits).toEqual(["/etc/"]); + }); + + it("lists the parent directory when token ends with a slash", async () => { + const shell = new TigrisShell(TEST_CONFIG, { cwd: "/" }); + await shell.exec("mkdir -p /tmp/a /tmp/b"); + + const [hits, token] = await computeCompletions("ls /tmp/", { + shell, + cwd: "/", + }); + expect(token).toBe("/tmp/"); + expect(hits.sort()).toEqual(["/tmp/a/", "/tmp/b/"]); + }); + + it("returns empty when the directory does not exist", async () => { + const shell = new TigrisShell(TEST_CONFIG, { cwd: "/" }); + + const [hits] = await computeCompletions("ls /does-not-exist/", { + shell, + cwd: "/", + }); + expect(hits).toEqual([]); + }); + + it("returns empty for path completion when shell is not configured", async () => { + const [hits] = await computeCompletions("ls foo", { shell: null, cwd: undefined }); + expect(hits).toEqual([]); + }); + }); + + describe("via ReplSession default cwd", () => { + it("falls back to engine cwd when ctx.cwd is undefined", async () => { + const shell = new TigrisShell(TEST_CONFIG_WITH_BUCKET); + // Auto-mounted at /workspace via TigrisAdapter — we only test that the engine cwd is used, + // not the contents (which would require mocking the adapter). + const [, token] = await computeCompletions("ls fo", { shell, cwd: undefined }); + expect(token).toBe("fo"); + }); + }); +});